diff --git a/project/vs2010_mfc/VBA2010.vcxproj b/project/vs2010_mfc/VBA2010.vcxproj index 8abe4400..9ca2dec7 100644 --- a/project/vs2010_mfc/VBA2010.vcxproj +++ b/project/vs2010_mfc/VBA2010.vcxproj @@ -201,7 +201,6 @@ - @@ -349,7 +348,6 @@ _SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - Create diff --git a/project/vs2010_mfc/VBA2010.vcxproj.filters b/project/vs2010_mfc/VBA2010.vcxproj.filters index ec1637f6..70ddff39 100644 --- a/project/vs2010_mfc/VBA2010.vcxproj.filters +++ b/project/vs2010_mfc/VBA2010.vcxproj.filters @@ -208,9 +208,6 @@ Linking - - Linking - Linking @@ -625,9 +622,6 @@ Linking - - Linking - Linking diff --git a/src/gba/GBA.cpp b/src/gba/GBA.cpp index 4a6183e7..aebffcdb 100644 --- a/src/gba/GBA.cpp +++ b/src/gba/GBA.cpp @@ -3591,7 +3591,7 @@ void CPULoop(int ticks) #ifndef NO_LINK // shuffle2: what's the purpose? - //if(gba_link_enabled) + //if(GetLinkMode() != LINK_DISCONNECTED) //cpuNextEvent = 1; #endif @@ -4042,11 +4042,8 @@ void CPULoop(int ticks) ticks -= clockTicks; #ifndef NO_LINK - if (gba_link_enabled) + if (GetLinkMode() != LINK_DISCONNECTED) LinkUpdate(clockTicks); - - if (gba_joybus_enabled) - JoyBusUpdate(clockTicks); #endif cpuNextEvent = CPUUpdateTicks(); @@ -4064,7 +4061,7 @@ void CPULoop(int ticks) #ifndef NO_LINK // shuffle2: what's the purpose? - if(gba_link_enabled || gba_joybus_active) + if (GetLinkMode() != LINK_DISCONNECTED || gba_joybus_active) cpuNextEvent = 1; #endif diff --git a/src/gba/GBALink.cpp b/src/gba/GBALink.cpp index 62c6df32..5c83b88f 100644 --- a/src/gba/GBALink.cpp +++ b/src/gba/GBALink.cpp @@ -10,7 +10,11 @@ #include #endif -int vbaid = 0; +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +static int vbaid = 0; const char *MakeInstanceFilename(const char *Input) { if (vbaid == 0) @@ -42,6 +46,9 @@ bool gba_link_enabled = false; #include "GBA.h" #include "GBALink.h" #include "GBASockClient.h" + +#include + #ifdef ENABLE_NLS #include #define _(x) gettext(x) @@ -49,6 +56,9 @@ bool gba_link_enabled = false; #define _(x) x #endif #define N_(x) x + + + #if (defined __WIN32__ || defined _WIN32) #include #else @@ -57,6 +67,8 @@ bool gba_link_enabled = false; #include #include #include + + #define ReleaseSemaphore(sem, nrel, orel) do { \ for(int i = 0; i < nrel; i++) \ sem_post(sem); \ @@ -158,57 +170,191 @@ int WaitForSingleObject(sem_t *s, int t) #endif #endif +#define UNSUPPORTED -1 +#define MULTIPLAYER 0 +#define NORMAL8 1 +#define NORMAL32 2 +#define UART 3 +#define JOYBUS 4 +#define GP 5 + +#define RFU_INIT 0 +#define RFU_COMM 1 +#define RFU_SEND 2 +#define RFU_RECV 3 + +static ConnectionState InitIPC(); +static ConnectionState InitSocket(); +static ConnectionState JoyBusConnect(); + +static void JoyBusShutdown(); +static void CloseIPC(); +static void CloseSocket(); + +static void StartCableSocket(u16 siocnt); +static void StartRFU(u16 siocnt); +static void StartCableIPC(u16 siocnt); + +static void JoyBusUpdate(int ticks); +static void UpdateCableIPC(int ticks); +static void UpdateRFUIPC(int ticks); +static void UpdateSocket(int ticks); + +static ConnectionState ConnectUpdateSocket(char * const message, size_t size); + +struct LinkDriver { + typedef ConnectionState (ConnectFunc)(); + typedef ConnectionState (ConnectUpdateFunc)(char * const message, size_t size); + typedef void (StartFunc)(u16 siocnt); + typedef void (UpdateFunc)(int ticks); + typedef void (CloseFunc)(); + + LinkMode mode; + ConnectFunc *connect; + ConnectUpdateFunc *connectUpdate; + StartFunc *start; + UpdateFunc *update; + CloseFunc *close; +}; +static const LinkDriver linkDrivers[] = +{ + { LINK_CABLE_IPC, InitIPC, NULL, StartCableIPC, UpdateCableIPC, CloseIPC }, + { LINK_CABLE_SOCKET, InitSocket, ConnectUpdateSocket, StartCableSocket, UpdateSocket, CloseSocket }, + { LINK_RFU_IPC, InitIPC, NULL, StartRFU, UpdateRFUIPC, CloseIPC }, + { LINK_GAMECUBE_DOLPHIN, JoyBusConnect, NULL, NULL, JoyBusUpdate, JoyBusShutdown } +}; + + +enum +{ + JOY_CMD_RESET = 0xff, + JOY_CMD_STATUS = 0x00, + JOY_CMD_READ = 0x14, + JOY_CMD_WRITE = 0x15 +}; + #define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value) -int linktime = 0; +typedef struct { + u16 linkdata[5]; + u16 linkcmd; + u16 numtransfers; + int lastlinktime; + u8 numgbas; + u8 trgbas; + u8 linkflags; + int rfu_q[4]; + u8 rfu_request[4]; + int rfu_linktime[4]; + u32 rfu_bdata[4][7]; + u32 rfu_data[4][32]; +} LINKDATA; -GBASockClient* dol = NULL; -sf::IPAddress joybusHostAddr = sf::IPAddress::LocalHost; +typedef struct { + sf::SocketTCP tcpsocket; + int numslaves; + int connectedSlaves; + int type; + bool server; + bool speed; +} LANLINKDATA; + +class lserver{ + int numbytes; + sf::Selector fdset; + //timeval udptimeout; + char inbuffer[256], outbuffer[256]; + s32 *intinbuffer; + u16 *u16inbuffer; + s32 *intoutbuffer; + u16 *u16outbuffer; + int counter; + int done; +public: + int howmanytimes; + sf::SocketTCP tcpsocket[4]; + sf::IPAddress udpaddr[4]; + lserver(void); + void Send(void); + void Recv(void); +}; + +class lclient{ + sf::Selector fdset; + char inbuffer[256], outbuffer[256]; + s32 *intinbuffer; + u16 *u16inbuffer; + s32 *intoutbuffer; + u16 *u16outbuffer; + int numbytes; +public: + sf::IPAddress serveraddr; + unsigned short serverport; + int numtransfers; + lclient(void); + void Send(void); + void Recv(void); + void CheckConn(void); +}; + +static const LinkDriver *linkDriver = NULL; +static ConnectionState gba_connection_state = LINK_OK; + +LinkMode GetLinkMode() { + if (linkDriver && gba_connection_state == LINK_OK) + return linkDriver->mode; + else + return LINK_DISCONNECTED; +} + +static int linktime = 0; + +static GBASockClient* dol = NULL; +static sf::IPAddress joybusHostAddr = sf::IPAddress::LocalHost; // Hodgepodge -u8 tspeed = 3; -u8 transfer = 0; -LINKDATA *linkmem = NULL; -int linkid = 0; +static u8 tspeed = 3; +static u8 transfer = 0; +static LINKDATA *linkmem = NULL; +static int linkid = 0; #if (defined __WIN32__ || defined _WIN32) -HANDLE linksync[4]; +static HANDLE linksync[4]; #else -sem_t *linksync[4]; +static sem_t *linksync[4]; #endif -int savedlinktime = 0; +static int savedlinktime = 0; #if (defined __WIN32__ || defined _WIN32) -HANDLE mmf = NULL; +static HANDLE mmf = NULL; #else -int mmf = -1; +static int mmf = -1; #endif -char linkevent[] = +static char linkevent[] = #if !(defined __WIN32__ || defined _WIN32) "/" #endif "VBA link event "; static int i, j; -int linktimeout = 1000; -LANLINKDATA lanlink; -u16 linkdata[4]; -lserver ls; -lclient lc; -bool oncewait = false, after = false; +static int linktimeout = 1000; +static LANLINKDATA lanlink; +static u16 linkdata[4]; +static lserver ls; +static lclient lc; +static bool oncewait = false, after = false; // RFU crap (except for numtransfers note...should probably check that out) -bool rfu_enabled = false; -u8 rfu_cmd, rfu_qsend, rfu_qrecv; -int rfu_state, rfu_polarity, rfu_counter, rfu_masterq; +static u8 rfu_cmd, rfu_qsend, rfu_qrecv; +static int rfu_state, rfu_polarity, rfu_counter, rfu_masterq; // numtransfers seems to be used interchangeably with linkmem->numtransfers // in rfu code; probably a bug? -int rfu_transfer_end; +static int rfu_transfer_end; // in local comm, setting this keeps slaves from trying to communicate even // when master isn't -u16 numtransfers = 0; -u32 rfu_masterdata[32]; +static u16 numtransfers = 0; +static u32 rfu_masterdata[32]; // time to end of single GBA's transfer, in 16.78 MHz clock ticks // first index is GBA # -int trtimedata[4][4] = { +static const int trtimedata[4][4] = { // 9600 38400 57600 115200 {34080, 8520, 5680, 2840}, {65536, 16384, 10923, 5461}, @@ -221,38 +367,281 @@ int trtimedata[4][4] = { // for < 3 slaves, this is time to transfer last machine + time to detect lack // of start bit from next slave // first index is (# of slaves) - 1 -int trtimeend[3][4] = { +static const int trtimeend[3][4] = { // 9600 38400 57600 115200 {72527, 18132, 12088, 6044}, {106608, 26652, 17768, 8884}, {133692, 33423, 22282, 11141} }; -int gbtime = 1024; +static int GetSIOMode(u16, u16); -int GetSIOMode(u16, u16); - -void LinkClientThread(void *); -void LinkServerThread(void *); - -int StartServer(void); - -u16 StartRFU(u16); - -void StartLink(u16 value) +// The GBA wireless RFU (see adapter3.txt) +// Just try to avert your eyes for now ^^ (note, it currently can be called, tho) +static void StartRFU(u16 siocnt) { - if (ioMem == NULL) - return; + switch (GetSIOMode(siocnt, READ16LE(&ioMem[COMM_RCNT]))) { + case NORMAL8: + rfu_polarity = 0; + break; - if (rfu_enabled) { - UPDATE_REG(COMM_SIOCNT, StartRFU(value)); - return; + case NORMAL32: + if (siocnt & 8) + siocnt &= 0xfffb; // A kind of acknowledge procedure + else + siocnt |= 4; + + if (siocnt & 0x80) + { + if ((siocnt&3) == 1) + rfu_transfer_end = 2048; + else + rfu_transfer_end = 256; + + u16 a = READ16LE(&ioMem[COMM_SIODATA32_H]); + + switch (rfu_state) { + case RFU_INIT: + if (READ32LE(&ioMem[COMM_SIODATA32_L]) == 0xb0bb8001) + rfu_state = RFU_COMM; // end of startup + + UPDATE_REG(COMM_SIODATA32_H, READ16LE(&ioMem[COMM_SIODATA32_L])); + UPDATE_REG(COMM_SIODATA32_L, a); + break; + + case RFU_COMM: + if (a == 0x9966) + { + rfu_cmd = ioMem[COMM_SIODATA32_L]; + if ((rfu_qsend=ioMem[0x121]) != 0) { + rfu_state = RFU_SEND; + rfu_counter = 0; + } + if (rfu_cmd == 0x25 || rfu_cmd == 0x24) { + linkmem->rfu_q[vbaid] = rfu_qsend; + } + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 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; + rfu_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) + ReleaseSemaphore(linksync[1-vbaid], 1, NULL); + 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; + rfu_counter = 0; + } + rfu_cmd |= 0x80; + break; + + case 0x24: // send data + if((numtransfers++)==0) linktime = 1; + linkmem->rfu_linktime[vbaid] = linktime; + if(linkmem->numgbas==2){ + ReleaseSemaphore(linksync[1-vbaid], 1, NULL); + WaitForSingleObject(linksync[vbaid], linktimeout); + } + 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; + rfu_transfer_end = 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)) + ReleaseSemaphore(linksync[1-vbaid], 1, NULL); + WaitForSingleObject(linksync[vbaid], linktimeout); + } + if ( linkid > 0) { + memcpy(rfu_masterdata, linkmem->rfu_data[1-vbaid], 128); + rfu_masterq = linkmem->rfu_q[1-vbaid]; + } + rfu_transfer_end = linkmem->rfu_linktime[1-vbaid] - linktime + 256; + + if (rfu_transfer_end < 256) + rfu_transfer_end = 256; + + linktime = -rfu_transfer_end; + rfu_polarity = 1; + rfu_cmd = 0x28; + break; + } + UPDATE_REG(COMM_SIODATA32_H, 0x9966); + UPDATE_REG(COMM_SIODATA32_L, (rfu_qrecv<<8) | rfu_cmd); + + } else { + + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + } + break; + + case RFU_SEND: + if(--rfu_qsend == 0) + rfu_state = RFU_COMM; + + switch (rfu_cmd) { + case 0x16: + linkmem->rfu_bdata[vbaid][rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x17: + linkid = 1; + break; + + case 0x1f: + linkmem->rfu_request[1-vbaid] = 1; + break; + + case 0x24: + case 0x25: + linkmem->rfu_data[vbaid][rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + } + UPDATE_REG(COMM_SIODATA32_L, 0); + UPDATE_REG(COMM_SIODATA32_H, 0x8000); + break; + + case RFU_RECV: + if (--rfu_qrecv == 0) + rfu_state = RFU_COMM; + + switch (rfu_cmd) { + case 0x9d: + case 0x9e: + if (rfu_counter == 0) { + UPDATE_REG(COMM_SIODATA32_L, 0x61f1); + UPDATE_REG(COMM_SIODATA32_H, 0); + rfu_counter++; + break; + } + UPDATE_REG(COMM_SIODATA32_L, linkmem->rfu_bdata[1-vbaid][rfu_counter-1]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, linkmem->rfu_bdata[1-vbaid][rfu_counter-1]>>16); + rfu_counter++; + break; + + case 0xa6: + if (linkid>0) { + UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); + } else { + UPDATE_REG(COMM_SIODATA32_L, linkmem->rfu_data[1-vbaid][rfu_counter]&0xffff); + UPDATE_REG(COMM_SIODATA32_H, linkmem->rfu_data[1-vbaid][rfu_counter++]>>16); + } + break; + + case 0x93: // it seems like the game doesn't care about this value + UPDATE_REG(COMM_SIODATA32_L, 0x1234); // put anything in here + UPDATE_REG(COMM_SIODATA32_H, 0x0200); // also here, but it should be 0200 + break; + + case 0xa0: + case 0xa1: + UPDATE_REG(COMM_SIODATA32_L, 0x641b); + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + case 0x9a: + UPDATE_REG(COMM_SIODATA32_L, 0x61f9); + UPDATE_REG(COMM_SIODATA32_H, 0); + break; + + case 0x91: + UPDATE_REG(COMM_SIODATA32_L, 0x00ff); + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + + default: + UPDATE_REG(COMM_SIODATA32_L, 0x0173); + UPDATE_REG(COMM_SIODATA32_H, 0x0000); + break; + } + break; + } + transfer = 1; + } + + if (rfu_polarity) + siocnt ^= 4; // sometimes it's the other way around + break; } + UPDATE_REG(COMM_SIOCNT, siocnt); +} + +static void StartCableIPC(u16 value) +{ switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { case MULTIPLAYER: { - bool start = (value & 0x80) && !linkid && !transfer && gba_link_enabled; - u16 si = value & 4; + bool start = (value & 0x80) && !linkid && !transfer; // clear start, seqno, si (RO on slave, start = pulse on master) value &= 0xff4b; // get current si. This way, on slaves, it is low during xfer @@ -263,27 +652,7 @@ void StartLink(u16 value) value |= READ16LE(&ioMem[COMM_SIOCNT]) & 4; } if (start) { - if (lanlink.active) - { - if (lanlink.connected) - { - linkdata[0] = READ16LE(&ioMem[COMM_SIODATA8]); - savedlinktime = linktime; - tspeed = value & 3; - ls.Send(); - transfer = 1; - linktime = 0; - UPDATE_REG(COMM_SIOMULTI0, linkdata[0]); - UPDATE_REG(COMM_SIOMULTI1, 0xffff); - WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); - if (lanlink.speed&&oncewait == false) - ls.howmanytimes++; - after = false; - value &= ~0x40; - } else - value |= 0x40; // comm error - } - else if (linkmem->numgbas > 1) + if (linkmem->numgbas > 1) { // find first active attached GBA // doing this first reduces the potential @@ -326,6 +695,8 @@ void StartLink(u16 value) WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); value &= ~0x40; + } else { + value |= 0x40; // comm error } } value |= (transfer != 0) << 7; @@ -352,6 +723,68 @@ void StartLink(u16 value) } } +void StartCableSocket(u16 value) +{ + switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { + case MULTIPLAYER: { + bool start = (value & 0x80) && !linkid && !transfer; + // clear start, seqno, si (RO on slave, start = pulse on master) + value &= 0xff4b; + // get current si. This way, on slaves, it is low during xfer + if(linkid) { + if(!transfer) + value |= 4; + else + value |= READ16LE(&ioMem[COMM_SIOCNT]) & 4; + } + if (start) { + linkdata[0] = READ16LE(&ioMem[COMM_SIODATA8]); + savedlinktime = linktime; + tspeed = value & 3; + ls.Send(); + transfer = 1; + linktime = 0; + UPDATE_REG(COMM_SIOMULTI0, linkdata[0]); + UPDATE_REG(COMM_SIOMULTI1, 0xffff); + WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); + if (lanlink.speed && oncewait == false) + ls.howmanytimes++; + after = false; + value &= ~0x40; + } + value |= (transfer != 0) << 7; + value |= (linkid && !transfer ? 0xc : 8); // set SD (high), SI (low on master) + value |= linkid << 4; // set seq + UPDATE_REG(COMM_SIOCNT, value); + if (linkid) + // SC low -> transfer in progress + // not sure why SO is low + UPDATE_REG(COMM_RCNT, transfer ? 6 : 7); + else + // SI is always low on master + // SO, SC always low during transfer + // not sure why SO low otherwise + UPDATE_REG(COMM_RCNT, transfer ? 2 : 3); + break; + } + case NORMAL8: + case NORMAL32: + case UART: + default: + UPDATE_REG(COMM_SIOCNT, value); + break; + } +} + +void StartLink(u16 siocnt) +{ + if (!linkDriver || !linkDriver->start) { + return; + } + + linkDriver->start(siocnt); +} + void StartGPLink(u16 value) { UPDATE_REG(COMM_RCNT, value); @@ -369,21 +802,27 @@ void StartGPLink(u16 value) break; case GP: - if (rfu_enabled) + if (GetLinkMode() == LINK_RFU_IPC) rfu_state = RFU_INIT; break; } } -void JoyBusConnect() +static ConnectionState JoyBusConnect() { delete dol; dol = NULL; dol = new GBASockClient(joybusHostAddr); + if (dol) { + return LINK_OK; + } + else { + return LINK_ERROR; + } } -void JoyBusShutdown() +static void JoyBusShutdown() { delete dol; dol = NULL; @@ -399,7 +838,7 @@ static u32 nextjoybusupdate = 0; static u32 lastcommand = 0; static bool booted = false; -void JoyBusUpdate(int ticks) +static void JoyBusUpdate(int ticks) { lastjoybusupdate += ticks; lastcommand += ticks; @@ -519,101 +958,11 @@ void JoyBusUpdate(int ticks) static void ReInitLink(); -void LinkUpdate(int ticks) +static void UpdateCableIPC(int ticks) { if (((READ16LE(&ioMem[COMM_RCNT])) >> 14) == 3) return; - // this actually gets called every single instruction, so keep default - // path as short as possible - - linktime += ticks; - - if (rfu_enabled) - { - rfu_transfer_end -= ticks; - if (transfer && rfu_transfer_end <= 0) - { - transfer = 0; - if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) - { - IF |= 0x80; - UPDATE_REG(0x202, IF); - } - UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 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[COMM_SIODATA8]); - - lc.Send(); - - UPDATE_REG(COMM_SIODATA32_L, linkdata[0]); - UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x80); - transfer = 1; - if (lc.numtransfers==1) - linktime = 0; - else - linktime -= savedlinktime; - } - - if (transfer && linktime >= trtimeend[lanlink.numslaves-1][tspeed]) - { - if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) - { - IF |= 0x80; - UPDATE_REG(0x202, IF); - } - - UPDATE_REG(COMM_SIOCNT, (READ16LE(&ioMem[COMM_SIOCNT]) & 0xff0f) | (linkid << 4)); - transfer = 0; - linktime -= trtimeend[lanlink.numslaves-1][tspeed]; - oncewait = false; - - if (!lanlink.speed) - { - if (linkid) - lc.Recv(); - else - ls.Recv(); // WTF is the point of this? - - UPDATE_REG(COMM_SIOMULTI1, linkdata[1]); - UPDATE_REG(COMM_SIOMULTI2, linkdata[2]); - UPDATE_REG(COMM_SIOMULTI3, linkdata[3]); - oncewait = true; - - } else { - - after = true; - if (lanlink.numslaves == 1) - { - UPDATE_REG(COMM_SIOMULTI1, linkdata[1]); - UPDATE_REG(COMM_SIOMULTI2, linkdata[2]); - UPDATE_REG(COMM_SIOMULTI3, linkdata[3]); - } - } - } - } - return; - } - // slave startup depends on detecting change in numtransfers // and syncing clock with master (after first transfer) // this will fail if > ~2 minutes have passed since last transfer due @@ -752,11 +1101,105 @@ void LinkUpdate(int ticks) UPDATE_REG(0x202, IF); } } - - return; } -inline int GetSIOMode(u16 siocnt, u16 rcnt) +static void UpdateRFUIPC(int ticks) +{ + rfu_transfer_end -= ticks; + + if (transfer && rfu_transfer_end <= 0) + { + transfer = 0; + if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); + } +} + +static void UpdateSocket(int ticks) +{ + if (after) + { + if (linkid && linktime > 6044) { + lc.Recv(); + oncewait = true; + } + else + return; + } + + if (linkid && !transfer && lc.numtransfers > 0 && linktime >= savedlinktime) + { + linkdata[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); + + lc.Send(); + + UPDATE_REG(COMM_SIODATA32_L, linkdata[0]); + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x80); + transfer = 1; + if (lc.numtransfers==1) + linktime = 0; + else + linktime -= savedlinktime; + } + + if (transfer && linktime >= trtimeend[lanlink.numslaves-1][tspeed]) + { + if (READ16LE(&ioMem[COMM_SIOCNT]) & 0x4000) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + + UPDATE_REG(COMM_SIOCNT, (READ16LE(&ioMem[COMM_SIOCNT]) & 0xff0f) | (linkid << 4)); + transfer = 0; + linktime -= trtimeend[lanlink.numslaves-1][tspeed]; + oncewait = false; + + if (!lanlink.speed) + { + if (linkid) + lc.Recv(); + else + ls.Recv(); // WTF is the point of this? + + UPDATE_REG(COMM_SIOMULTI1, linkdata[1]); + UPDATE_REG(COMM_SIOMULTI2, linkdata[2]); + UPDATE_REG(COMM_SIOMULTI3, linkdata[3]); + oncewait = true; + + } else { + + after = true; + if (lanlink.numslaves == 1) + { + UPDATE_REG(COMM_SIOMULTI1, linkdata[1]); + UPDATE_REG(COMM_SIOMULTI2, linkdata[2]); + UPDATE_REG(COMM_SIOMULTI3, linkdata[3]); + } + } + } +} + + +void LinkUpdate(int ticks) +{ + if (!linkDriver) { + return; + } + + // this actually gets called every single instruction, so keep default + // path as short as possible + + linktime += ticks; + + linkDriver->update(ticks); +} + +inline static int GetSIOMode(u16 siocnt, u16 rcnt) { if (!(rcnt & 0x8000)) { @@ -774,280 +1217,13 @@ inline int GetSIOMode(u16 siocnt, u16 rcnt) return GP; } -// The GBA wireless RFU (see adapter3.txt) -// Just try to avert your eyes for now ^^ (note, it currently can be called, tho) -u16 StartRFU(u16 value) -{ - switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { - 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) - rfu_transfer_end = 2048; - else - rfu_transfer_end = 256; - - u16 a = READ16LE(&ioMem[COMM_SIODATA32_H]); - - switch (rfu_state) { - case RFU_INIT: - if (READ32LE(&ioMem[COMM_SIODATA32_L]) == 0xb0bb8001) - rfu_state = RFU_COMM; // end of startup - - UPDATE_REG(COMM_SIODATA32_H, READ16LE(&ioMem[COMM_SIODATA32_L])); - UPDATE_REG(COMM_SIODATA32_L, a); - break; - - case RFU_COMM: - if (a == 0x9966) - { - rfu_cmd = ioMem[COMM_SIODATA32_L]; - if ((rfu_qsend=ioMem[0x121]) != 0) { - rfu_state = RFU_SEND; - rfu_counter = 0; - } - if (rfu_cmd == 0x25 || rfu_cmd == 0x24) { - linkmem->rfu_q[vbaid] = rfu_qsend; - } - UPDATE_REG(COMM_SIODATA32_L, 0); - UPDATE_REG(COMM_SIODATA32_H, 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; - rfu_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) - ReleaseSemaphore(linksync[1-vbaid], 1, NULL); - 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; - rfu_counter = 0; - } - rfu_cmd |= 0x80; - break; - - case 0x24: // send data - if((numtransfers++)==0) linktime = 1; - linkmem->rfu_linktime[vbaid] = linktime; - if(linkmem->numgbas==2){ - ReleaseSemaphore(linksync[1-vbaid], 1, NULL); - WaitForSingleObject(linksync[vbaid], linktimeout); - } - 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; - rfu_transfer_end = 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)) - ReleaseSemaphore(linksync[1-vbaid], 1, NULL); - WaitForSingleObject(linksync[vbaid], linktimeout); - } - if ( linkid > 0) { - memcpy(rfu_masterdata, linkmem->rfu_data[1-vbaid], 128); - rfu_masterq = linkmem->rfu_q[1-vbaid]; - } - rfu_transfer_end = linkmem->rfu_linktime[1-vbaid] - linktime + 256; - - if (rfu_transfer_end < 256) - rfu_transfer_end = 256; - - linktime = -rfu_transfer_end; - rfu_polarity = 1; - rfu_cmd = 0x28; - break; - } - UPDATE_REG(COMM_SIODATA32_H, 0x9966); - UPDATE_REG(COMM_SIODATA32_L, (rfu_qrecv<<8) | rfu_cmd); - - } else { - - UPDATE_REG(COMM_SIODATA32_L, 0); - UPDATE_REG(COMM_SIODATA32_H, 0x8000); - } - break; - - case RFU_SEND: - if(--rfu_qsend == 0) - rfu_state = RFU_COMM; - - switch (rfu_cmd) { - case 0x16: - linkmem->rfu_bdata[vbaid][rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); - break; - - case 0x17: - linkid = 1; - break; - - case 0x1f: - linkmem->rfu_request[1-vbaid] = 1; - break; - - case 0x24: - case 0x25: - linkmem->rfu_data[vbaid][rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); - break; - } - UPDATE_REG(COMM_SIODATA32_L, 0); - UPDATE_REG(COMM_SIODATA32_H, 0x8000); - break; - - case RFU_RECV: - if (--rfu_qrecv == 0) - rfu_state = RFU_COMM; - - switch (rfu_cmd) { - case 0x9d: - case 0x9e: - if (rfu_counter == 0) { - UPDATE_REG(COMM_SIODATA32_L, 0x61f1); - UPDATE_REG(COMM_SIODATA32_H, 0); - rfu_counter++; - break; - } - UPDATE_REG(COMM_SIODATA32_L, linkmem->rfu_bdata[1-vbaid][rfu_counter-1]&0xffff); - UPDATE_REG(COMM_SIODATA32_H, linkmem->rfu_bdata[1-vbaid][rfu_counter-1]>>16); - rfu_counter++; - break; - - case 0xa6: - if (linkid>0) { - UPDATE_REG(COMM_SIODATA32_L, rfu_masterdata[rfu_counter]&0xffff); - UPDATE_REG(COMM_SIODATA32_H, rfu_masterdata[rfu_counter++]>>16); - } else { - UPDATE_REG(COMM_SIODATA32_L, linkmem->rfu_data[1-vbaid][rfu_counter]&0xffff); - UPDATE_REG(COMM_SIODATA32_H, linkmem->rfu_data[1-vbaid][rfu_counter++]>>16); - } - break; - - case 0x93: // it seems like the game doesn't care about this value - UPDATE_REG(COMM_SIODATA32_L, 0x1234); // put anything in here - UPDATE_REG(COMM_SIODATA32_H, 0x0200); // also here, but it should be 0200 - break; - - case 0xa0: - case 0xa1: - UPDATE_REG(COMM_SIODATA32_L, 0x641b); - UPDATE_REG(COMM_SIODATA32_H, 0x0000); - break; - - case 0x9a: - UPDATE_REG(COMM_SIODATA32_L, 0x61f9); - UPDATE_REG(COMM_SIODATA32_H, 0); - break; - - case 0x91: - UPDATE_REG(COMM_SIODATA32_L, 0x00ff); - UPDATE_REG(COMM_SIODATA32_H, 0x0000); - break; - - default: - UPDATE_REG(COMM_SIODATA32_L, 0x0173); - UPDATE_REG(COMM_SIODATA32_H, 0x0000); - break; - } - break; - } - transfer = 1; - } - - if (rfu_polarity) - value ^= 4; // sometimes it's the other way around - - default: - return value; - } -} - -////////////////////////////////////////////////////////////////////////// -// Probably from here down needs to be replaced with SFML goodness :) -// tjm: what SFML goodness? SFML for network, yes, but not for IPC - -bool InitLink() -{ +static ConnectionState InitIPC() { linkid = 0; #if (defined __WIN32__ || defined _WIN32) if((mmf=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LINKDATA), LOCAL_LINK_NAME))==NULL){ systemMessage(0, N_("Error creating file mapping")); - return false; + return LINK_ERROR; } if(GetLastError() == ERROR_ALREADY_EXISTS) @@ -1059,7 +1235,7 @@ bool InitLink() if((linkmem=(LINKDATA *)MapViewOfFile(mmf, FILE_MAP_WRITE, 0, 0, sizeof(LINKDATA)))==NULL){ CloseHandle(mmf); systemMessage(0, N_("Error mapping file")); - return false; + return LINK_ERROR; } #else if((mmf = shm_open("/" LOCAL_LINK_NAME, O_RDWR|O_CREAT|O_EXCL, 0777)) < 0) { @@ -1109,7 +1285,7 @@ bool InitLink() close(mmf); #endif systemMessage(0, N_("5 or more GBAs not supported.")); - return false; + return LINK_ERROR; } if(vbaid == n) linkmem->numgbas = n + 1; @@ -1130,7 +1306,7 @@ bool InitLink() CloseHandle(linksync[j]); } systemMessage(0, N_("Error opening event")); - return false; + return LINK_ERROR; } #else if((linksync[i] = sem_open(linkevent, @@ -1148,13 +1324,227 @@ bool InitLink() } } systemMessage(0, N_("Error opening event")); - return false; + return LINK_ERROR; } #endif } - for(i=0;i<4;i++) + + return LINK_OK; +} + +static ConnectionState InitSocket() { + linkid = 0; + + for(int i = 0; i < 4; i++) linkdata[i] = 0xffff; - return true; + + if (lanlink.server) { + lanlink.connectedSlaves = 0; + // should probably use GetPublicAddress() + //sid->ShowServerIP(sf::IPAddress::GetLocalAddress()); + + // too bad Listen() doesn't take an address as well + // then again, old code used INADDR_ANY anyway + if (!lanlink.tcpsocket.Listen(IP_LINK_PORT)) + // Note: old code closed socket & retried once on bind failure + return LINK_ERROR; // FIXME: error code? + else + return LINK_NEEDS_UPDATE; + } else { + lc.serverport = IP_LINK_PORT; + + if (!lc.serveraddr.IsValid()) { + return LINK_ERROR; + } else { + lanlink.tcpsocket.SetBlocking(false); + sf::Socket::Status status = lanlink.tcpsocket.Connect(lc.serverport, lc.serveraddr); + + if (status == sf::Socket::Error || status == sf::Socket::Disconnected) + return LINK_ERROR; + else + return LINK_NEEDS_UPDATE; + } + } +} + +////////////////////////////////////////////////////////////////////////// +// Probably from here down needs to be replaced with SFML goodness :) +// tjm: what SFML goodness? SFML for network, yes, but not for IPC + +ConnectionState InitLink(LinkMode mode) +{ + // Do nothing if we are already connected + if (GetLinkMode() != LINK_DISCONNECTED) { + systemMessage(0, N_("Error, link already connected")); + return LINK_ERROR; + } + + // Find the link driver + linkDriver = NULL; + for (u8 i = 0; i < sizeof(linkDrivers) / sizeof(linkDrivers[0]); i++) { + if (linkDrivers[i].mode == mode) { + linkDriver = &linkDrivers[i]; + break; + } + } + + if (linkDriver == NULL) { + systemMessage(0, N_("Unable to find link driver")); + return LINK_ERROR; + } + + // Connect the link + gba_connection_state = linkDriver->connect(); + + if (gba_connection_state == LINK_ERROR) { + CloseLink(); + } + + return gba_connection_state; +} + +static ConnectionState ConnectUpdateSocket(char * const message, size_t size) { + ConnectionState newState = LINK_NEEDS_UPDATE; + + if (lanlink.server) { + sf::Selector fdset; + fdset.Add(lanlink.tcpsocket); + + if (fdset.Wait(0.1) == 1) { + int nextSlave = lanlink.connectedSlaves + 1; + + sf::Socket::Status st = lanlink.tcpsocket.Accept(ls.tcpsocket[nextSlave]); + + if (st == sf::Socket::Error) { + for (int j = 1; j < nextSlave; j++) + ls.tcpsocket[j].Close(); + + snprintf(message, size, N_("Network error.")); + newState = LINK_ERROR; + } else { + sf::Packet packet; + packet << static_cast(nextSlave) + << static_cast(lanlink.numslaves); + + ls.tcpsocket[nextSlave].Send(packet); + + snprintf(message, size, N_("Player %d connected"), nextSlave); + + lanlink.connectedSlaves++; + } + } + + if (lanlink.numslaves == lanlink.connectedSlaves) { + for (int i = 1; i <= lanlink.numslaves; i++) { + sf::Packet packet; + packet << true; + + ls.tcpsocket[i].Send(packet); + } + + snprintf(message, size, N_("All players connected")); + newState = LINK_OK; + } + } else { + + sf::Packet packet; + sf::Socket::Status status = lanlink.tcpsocket.Receive(packet); + + if (status == sf::Socket::Error || status == sf::Socket::Disconnected) { + snprintf(message, size, N_("Network error.")); + newState = LINK_ERROR; + } else if (status == sf::Socket::Done) { + + if (linkid == 0) { + sf::Uint16 receivedId, receivedSlaves; + packet >> receivedId >> receivedSlaves; + + if (packet) { + linkid = receivedId; + lanlink.numslaves = receivedSlaves; + + snprintf(message, size, N_("Connected as #%d, Waiting for %d players to join"), + linkid + 1, lanlink.numslaves - linkid); + } + } else { + bool gameReady; + packet >> gameReady; + + if (packet && gameReady) { + newState = LINK_OK; + snprintf(message, size, N_("All players joined.")); + } + } + + sf::Selector fdset; + fdset.Add(lanlink.tcpsocket); + fdset.Wait(0.1); + } + } + + return newState; +} + +ConnectionState ConnectLinkUpdate(char * const message, size_t size) +{ + message[0] = '\0'; + + if (!linkDriver || gba_connection_state != LINK_NEEDS_UPDATE) { + gba_connection_state = LINK_ERROR; + snprintf(message, size, N_("Link connection does not need updates.")); + + return LINK_ERROR; + } + + gba_connection_state = linkDriver->connectUpdate(message, size); + + return gba_connection_state; +} + +void EnableLinkServer(bool enable, int numSlaves) { + lanlink.server = enable; + lanlink.numslaves = numSlaves; +} + +void EnableSpeedHacks(bool enable) { + lanlink.speed = enable; +} + +bool SetLinkServerHost(const char *host) { + sf::IPAddress addr = sf::IPAddress(host); + + lc.serveraddr = addr; + joybusHostAddr = addr; + + return addr.IsValid(); +} + +void GetLinkServerHost(char * const host, size_t size) { + if (host == NULL || size == 0) + return; + + host[0] = '\0'; + + if (linkDriver && linkDriver->mode == LINK_GAMECUBE_DOLPHIN) + strncpy(host, joybusHostAddr.ToString().c_str(), size); + else if (lanlink.server) + strncpy(host, sf::IPAddress::GetLocalAddress().ToString().c_str(), size); + else + strncpy(host, lc.serveraddr.ToString().c_str(), size); +} + +void SetLinkTimeout(int value) { + linktimeout = value; +} + +int GetLinkPlayerId() { + if (GetLinkMode() == LINK_DISCONNECTED) { + return -1; + } else if (linkid > 0) { + return linkid; + } else { + return vbaid; + } } static void ReInitLink() @@ -1172,26 +1562,7 @@ static void ReInitLink() systemScreenMessage(_("Lost link; reconnected")); } -void CloseLink(void){ - if(lanlink.connected){ - if(linkid){ - char outbuffer[4]; - outbuffer[0] = 4; - outbuffer[1] = -32; - if(lanlink.type==0) lanlink.tcpsocket.Send(outbuffer, 4); - } else { - char outbuffer[12]; - int i; - outbuffer[0] = 12; - outbuffer[1] = -32; - for(i=1;i<=lanlink.numslaves;i++){ - if(lanlink.type==0){ - ls.tcpsocket[i].Send(outbuffer, 12); - } - ls.tcpsocket[i].Close(); - } - } - } +static void CloseIPC() { int f = linkmem->linkflags; f &= ~(1 << linkid); if(f & 0xf) { @@ -1231,6 +1602,37 @@ void CloseLink(void){ munmap(linkmem, sizeof(LINKDATA)); close(mmf); #endif +} + +static void CloseSocket() { + if(linkid){ + char outbuffer[4]; + outbuffer[0] = 4; + outbuffer[1] = -32; + if(lanlink.type==0) lanlink.tcpsocket.Send(outbuffer, 4); + } else { + char outbuffer[12]; + int i; + outbuffer[0] = 12; + outbuffer[1] = -32; + for(i=1;i<=lanlink.numslaves;i++){ + if(lanlink.type==0){ + ls.tcpsocket[i].Send(outbuffer, 12); + } + ls.tcpsocket[i].Close(); + } + } + lanlink.tcpsocket.Close(); +} + +void CloseLink(void){ + if (!linkDriver) { + return; // Nothing to do + } + + linkDriver->close(); + linkDriver = NULL; + return; } @@ -1257,81 +1659,6 @@ lserver::lserver(void){ oncewait = false; } -bool lserver::Init(ServerInfoDisplay *sid){ - // too bad Listen() doesn't take an address as well - // then again, old code used INADDR_ANY anyway - if(!lanlink.tcpsocket.Listen(IP_LINK_PORT)) - // Note: old code closed socket & retried once on bind failure - return false; // FIXME: error code? - - if(lanlink.thread!=NULL){ - lanlink.terminate = true; - WaitForSingleObject(linksync[vbaid], 500); - lanlink.thread = NULL; - } - lanlink.terminate = false; - linkid = 0; - - // should probably use GetPublicAddress() - sid->ShowServerIP(sf::IPAddress::GetLocalAddress()); - - lanlink.thread = new sf::Thread(LinkServerThread, sid); - lanlink.thread->Launch(); - - return true; - -} - -void LinkServerThread(void *_sid){ - ServerInfoDisplay *sid = (ServerInfoDisplay *)_sid; - sf::Selector fdset; - char inbuffer[256], outbuffer[256]; - s32 *intinbuffer = (s32*)inbuffer; - u16 *u16inbuffer = (u16*)inbuffer; - s32 *intoutbuffer = (s32*)outbuffer; - u16 *u16outbuffer = (u16*)outbuffer; - - i = 0; - - while(iShowConnect(i); - } - } - sid->Ping(); - } - - lanlink.connected = true; - - sid->Connected(); - - for(i=1;i<=lanlink.numslaves;i++){ - outbuffer[0] = 4; - ls.tcpsocket[i].Send(outbuffer, 4); - } - -CloseInfoDisplay: - delete sid; - return; -} - void lserver::Send(void){ if(lanlink.type==0){ // TCP if(savedlinktime==-1){ @@ -1398,7 +1725,6 @@ void lserver::Recv(void){ if(howmanytimes>1) memmove(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); systemScreenMessage(message); outbuffer[0] = 4; @@ -1409,6 +1735,7 @@ void lserver::Recv(void){ tcpsocket[i].Receive(inbuffer, 256, nr); tcpsocket[i].Close(); } + CloseLink(); return; } linkdata[i+1] = READ16LE(&u16inbuffer[1]); @@ -1419,6 +1746,13 @@ void lserver::Recv(void){ return; } +void CheckLinkConnection() { + if (GetLinkMode() == LINK_CABLE_SOCKET) { + if (linkid && lc.numtransfers == 0) { + lc.CheckConn(); + } + } +} // Client lclient::lclient(void){ @@ -1430,92 +1764,6 @@ lclient::lclient(void){ return; } -bool lclient::Init(sf::IPAddress addr, ClientInfoDisplay *cid){ - serveraddr = addr; - serverport = IP_LINK_PORT; - lanlink.tcpsocket.SetBlocking(false); - - if(lanlink.thread!=NULL){ - lanlink.terminate = true; - WaitForSingleObject(linksync[vbaid], 500); - lanlink.thread = NULL; - } - - cid->ConnectStart(addr); - lanlink.terminate = false; - lanlink.thread = new sf::Thread(LinkClientThread, cid); - lanlink.thread->Launch(); - return true; -} - -void LinkClientThread(void *_cid){ - ClientInfoDisplay *cid = (ClientInfoDisplay *)_cid; - sf::Selector fdset; - int numbytes; - char inbuffer[16]; - u16 *u16inbuffer = (u16*)inbuffer; - unsigned long block = 0; - - while(lanlink.tcpsocket.Connect(lc.serverport, lc.serveraddr) != sf::Socket::Done) { - // stupid SFML has no way of giving what sort of error occurred - // so we'll just have to do a retry loop, I guess. - cid->Ping(); - if(lanlink.terminate) - goto CloseInfoDisplay; - // old code had broken sleep on socket, which isn't - // even connected yet - // corrected sleep on socket worked, but this is more sane - // and probably less portable... works with mingw32 at least -#if (defined __WIN32__ || defined _WIN32) - Sleep(100); // in milliseconds -#else - usleep(100000); // in microseconds -#endif - } - - numbytes = 0; - size_t got; - while(numbytes<4) { - lanlink.tcpsocket.Receive(inbuffer+numbytes, 4 - numbytes, got); - numbytes += got; - fdset.Clear(); - fdset.Add(lanlink.tcpsocket); - fdset.Wait(0.1); - cid->Ping(); - if(lanlink.terminate) { - lanlink.tcpsocket.Close(); - goto CloseInfoDisplay; - } - } - linkid = (int)READ16LE(&u16inbuffer[0]); - lanlink.numslaves = (int)READ16LE(&u16inbuffer[1]); - - cid->ShowConnect(linkid + 1, lanlink.numslaves - linkid); - - numbytes = 0; - inbuffer[0] = 1; - while(numbytesPing(); - if(lanlink.terminate) { - lanlink.tcpsocket.Close(); - goto CloseInfoDisplay; - } - } - - lanlink.connected = true; - - cid->Connected(); - -CloseInfoDisplay: - delete cid; - return; -} - void lclient::CheckConn(void){ size_t nr; lanlink.tcpsocket.Receive(inbuffer, 1, nr); @@ -1528,8 +1776,8 @@ void lclient::CheckConn(void){ if(inbuffer[1]==-32){ outbuffer[0] = 4; lanlink.tcpsocket.Send(outbuffer, 4); - lanlink.connected = false; systemScreenMessage(_("Server disconnected.")); + CloseLink(); return; } numtransfers = 1; @@ -1567,8 +1815,8 @@ void lclient::Recv(void){ if(inbuffer[1]==-32){ outbuffer[0] = 4; lanlink.tcpsocket.Send(outbuffer, 4); - lanlink.connected = false; systemScreenMessage(_("Server disconnected.")); + CloseLink(); return; } tspeed = inbuffer[1] & 3; diff --git a/src/gba/GBALink.h b/src/gba/GBALink.h index e9291605..bce14942 100644 --- a/src/gba/GBALink.h +++ b/src/gba/GBALink.h @@ -1,32 +1,152 @@ #ifndef GBA_GBALINK_H #define GBA_GBALINK_H -#pragma once +/** + * Link modes to be passed to InitLink + */ +enum LinkMode +{ + LINK_DISCONNECTED, + LINK_CABLE_IPC, + LINK_CABLE_SOCKET, + LINK_RFU_IPC, + LINK_GAMECUBE_DOLPHIN +}; -// register definitions; these are always present +/** + * State of the connection attempt + */ +enum ConnectionState +{ + LINK_OK, + LINK_ERROR, + LINK_NEEDS_UPDATE, + LINK_ABORT +}; -#define UNSUPPORTED -1 -#define MULTIPLAYER 0 -#define NORMAL8 1 -#define NORMAL32 2 -#define UART 3 -#define JOYBUS 4 -#define GP 5 +/** + * Initialize GBA linking + * + * @param mode Device to emulate, plugged to the GBA link port. + * @return success + */ +extern ConnectionState InitLink(LinkMode mode); -#define RFU_INIT 0 -#define RFU_COMM 1 -#define RFU_SEND 2 -#define RFU_RECV 3 +/** + * Update a link connection request + * + * @param message Information message + * @param size Maximum message size + */ +extern ConnectionState ConnectLinkUpdate(char * const message, size_t size); +/** + * Get the currently enabled link mode + * + * @return link mode + */ +extern LinkMode GetLinkMode(); + +/** + * Is this instance going to host a LAN link server? + * + * @param enabled Server mode + * @param numSlaves Number of expected clients + */ +extern void EnableLinkServer(bool enable, int numSlaves); + +/** + * Should speed hacks be used? + * + * @param enabled Speed hacks + */ +extern void EnableSpeedHacks(bool enable); + +/** + * Set the host to connect to when in socket mode + * + * @return false if the address is invalid + */ +extern bool SetLinkServerHost(const char *host); + +/** + * Get the host relevant to context + * + * If in lan server mode, returns the external IP adress + * If in lan client mode, returns the IP adress of the host to connect to + * If in gamecube mode, returns the IP adress of the dolphin host + * + */ +extern void GetLinkServerHost(char * const host, size_t size); + +/** + * Set the value in milliseconds of the timeout after which a connection is + * deemed lost. + * + * @param value timeout + */ +extern void SetLinkTimeout(int value); + +/** + * Verify that the link between the emulators is still active + */ +extern void CheckLinkConnection(); + +/** + * Set the current link mode to LINK_DISCONNECTED + */ +extern void CloseLink(); + +/** + * Get the id of the player of this VBA instance + * + * @return id -1 means disconnected, 0 means master, > 0 means slave + */ +extern int GetLinkPlayerId(); + +/** + * Start a link transfer + * + * @param siocnt the value of SIOCNT to be written + */ +extern void StartLink(u16 siocnt); + +/** + * Start a general purpose link transfer + * + * @param rcnt the value of RCNT to be written + */ +extern void StartGPLink(u16 rcnt); + +/** + * Emulate the linked device + */ +extern void LinkUpdate(int); + +/** + * Clean up IPC shared memory + */ +extern void CleanLocalLink(); + +/** + * Append the current VBA ID to a filemane + * + * @param Input filename to complete + * @return completed filename + */ + +extern const char *MakeInstanceFilename(const char *Input); + +// register definitions #define COMM_SIODATA32_L 0x120 #define COMM_SIODATA32_H 0x122 #define COMM_SIOCNT 0x128 #define COMM_SIODATA8 0x12a -#define COMM_SIOMLT_SEND 0x12a -#define COMM_SIOMULTI0 0x120 -#define COMM_SIOMULTI1 0x122 -#define COMM_SIOMULTI2 0x124 -#define COMM_SIOMULTI3 0x126 +#define COMM_SIOMLT_SEND 0x12a +#define COMM_SIOMULTI0 0x120 +#define COMM_SIOMULTI1 0x122 +#define COMM_SIOMULTI2 0x124 +#define COMM_SIOMULTI3 0x126 #define COMM_RCNT 0x134 #define COMM_JOYCNT 0x140 #define COMM_JOY_RECV_L 0x150 @@ -43,146 +163,4 @@ #define JOYCNT_SEND_COMPLETE 4 #define JOYCNT_INT_ENABLE 0x40 -enum -{ - JOY_CMD_RESET = 0xff, - JOY_CMD_STATUS = 0x00, - JOY_CMD_READ = 0x14, - JOY_CMD_WRITE = 0x15 -}; - -extern const char *MakeInstanceFilename(const char *Input); - -#ifndef NO_LINK -// Link implementation -#include -#include - -class ServerInfoDisplay -{ -public: - virtual void ShowServerIP(const sf::IPAddress& addr) = 0; - virtual void ShowConnect(const int player) = 0; - virtual void Ping() = 0; - virtual void Connected() = 0; -}; - -typedef struct { - u16 linkdata[5]; - u16 linkcmd; - u16 numtransfers; - int lastlinktime; - u8 numgbas; - u8 trgbas; - u8 linkflags; - int rfu_q[4]; - u8 rfu_request[4]; - int rfu_linktime[4]; - u32 rfu_bdata[4][7]; - u32 rfu_data[4][32]; -} LINKDATA; - -class lserver{ - int numbytes; - sf::Selector fdset; - //timeval udptimeout; - char inbuffer[256], outbuffer[256]; - s32 *intinbuffer; - u16 *u16inbuffer; - s32 *intoutbuffer; - u16 *u16outbuffer; - int counter; - int done; -public: - int howmanytimes; - sf::SocketTCP tcpsocket[4]; - sf::IPAddress udpaddr[4]; - lserver(void); - bool Init(ServerInfoDisplay *); - void Send(void); - void Recv(void); -}; - -class ClientInfoDisplay { -public: - virtual void ConnectStart(const sf::IPAddress& addr) = 0; - virtual void Ping() = 0; - virtual void ShowConnect(const int player, const int togo) = 0; - virtual void Connected() = 0; -}; - -class lclient{ - sf::Selector fdset; - char inbuffer[256], outbuffer[256]; - s32 *intinbuffer; - u16 *u16inbuffer; - s32 *intoutbuffer; - u16 *u16outbuffer; - int numbytes; -public: - sf::IPAddress serveraddr; - unsigned short serverport; - sf::SocketTCP noblock; - int numtransfers; - lclient(void); - bool Init(sf::IPAddress, ClientInfoDisplay *); - void Send(void); - void Recv(void); - void CheckConn(void); -}; - -typedef struct { - sf::SocketTCP tcpsocket; - //sf::SocketUDP udpsocket; - int numslaves; - sf::Thread *thread; - int type; - bool server; - bool terminate; - bool connected; - bool speed; - bool active; -} LANLINKDATA; - -extern bool gba_joybus_enabled; -extern bool gba_joybus_active; -extern bool gba_link_enabled; - -extern sf::IPAddress joybusHostAddr; -extern void JoyBusConnect(); -extern void JoyBusShutdown(); -extern void JoyBusUpdate(int ticks); - -extern bool InitLink(); -extern void CloseLink(); -extern void StartLink(u16); -extern void StartGPLink(u16); -extern void LinkUpdate(int); -extern void CleanLocalLink(); -extern LANLINKDATA lanlink; -extern int vbaid; -extern bool rfu_enabled; -extern int linktimeout; -extern lclient lc; -extern lserver ls; -extern int linkid; - -#else - -// stubs to keep #ifdef's out of mainline -const bool gba_joybus_enabled = false; -const bool gba_link_enabled = false; - -inline void JoyBusConnect() { } -inline void JoyBusShutdown() { } -inline void JoyBusUpdate(int) { } - -inline bool InitLink() { return true; } -inline void CloseLink() { } -inline void StartLink(u16) { } -inline void StartGPLink(u16) { } -inline void LinkUpdate(int) { } -inline void CleanLocalLink() { } -#endif - #endif /* GBA_GBALINK_H */ diff --git a/src/gba/GBASockClient.h b/src/gba/GBASockClient.h index fb4bdd24..4c96367c 100644 --- a/src/gba/GBASockClient.h +++ b/src/gba/GBASockClient.h @@ -3,12 +3,13 @@ #include #include "../common/Types.h" -class GBASockClient : public sf::SocketTCP +class GBASockClient { public: - GBASockClient(sf::IPAddress server_addr); + GBASockClient(sf::IPAddress _server_addr); ~GBASockClient(); + bool Connect(sf::IPAddress server_addr); void Send(std::vector data); char ReceiveCmd(char* data_in, bool block); void ReceiveClock(bool block); diff --git a/src/win32/JoybusOptions.cpp b/src/win32/JoybusOptions.cpp deleted file mode 100644 index fe3e1c76..00000000 --- a/src/win32/JoybusOptions.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef NO_LINK - -#include "stdafx.h" -#include "vba.h" -#include "JoybusOptions.h" -#include "../gba/GBALink.h" - -// JoybusOptions dialog - -IMPLEMENT_DYNAMIC(JoybusOptions, CDialog) - -JoybusOptions::JoybusOptions(CWnd* pParent /*=NULL*/) - : CDialog(JoybusOptions::IDD, pParent) -{ -} - -JoybusOptions::~JoybusOptions() -{ -} - -void JoybusOptions::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - DDX_Control(pDX, IDC_JOYBUS_ENABLE, enable_check); - DDX_Control(pDX, IDC_JOYBUS_HOSTNAME, hostname); -} - -BEGIN_MESSAGE_MAP(JoybusOptions, CDialog) - ON_BN_CLICKED(IDC_JOYBUS_ENABLE, &JoybusOptions::OnBnClickedJoybusEnable) - ON_BN_CLICKED(IDOK, &JoybusOptions::OnBnClickedOk) -END_MESSAGE_MAP() - -BOOL JoybusOptions::OnInitDialog() -{ - CDialog::OnInitDialog(); - - enable_check.SetCheck(gba_joybus_enabled ? BST_CHECKED : BST_UNCHECKED); - - hostname.EnableWindow(enable_check.GetCheck() == BST_CHECKED); - - hostname.SetWindowText(joybusHostAddr.ToString().c_str()); - - return TRUE; -} - -void JoybusOptions::OnBnClickedJoybusEnable() -{ - hostname.EnableWindow(enable_check.GetCheck() == BST_CHECKED); -} - -void JoybusOptions::OnBnClickedOk() -{ - if ( (hostname.GetWindowTextLength() == 0) - && (enable_check.GetCheck() == BST_CHECKED) ) - { - hostname.SetWindowText("Enter IP or Hostname"); - return; - } - - gba_joybus_enabled = enable_check.GetCheck() == BST_CHECKED; - - CString address; - hostname.GetWindowText(address); - - sf::IPAddress new_server; - new_server = std::string(address); - - if (!new_server.IsValid()) - { - hostname.SetWindowText("Enter IP or Hostname"); - return; - } - - joybusHostAddr = new_server; - JoyBusConnect(); - - OnOK(); -} - -#endif // NO_LINK diff --git a/src/win32/JoybusOptions.h b/src/win32/JoybusOptions.h deleted file mode 100644 index bfbda832..00000000 --- a/src/win32/JoybusOptions.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "afxwin.h" - -// JoybusOptions dialog - -class JoybusOptions : public CDialog -{ - DECLARE_DYNAMIC(JoybusOptions) - -public: - JoybusOptions(CWnd* pParent = NULL); // standard constructor - virtual ~JoybusOptions(); - -// Dialog Data - enum { IDD = IDD_JOYBUS_DIALOG }; - -protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - - DECLARE_MESSAGE_MAP() -public: - virtual BOOL OnInitDialog(); - afx_msg void OnBnClickedJoybusEnable(); - CButton enable_check; - CEdit hostname; - afx_msg void OnBnClickedOk(); -}; diff --git a/src/win32/LinkOptions.cpp b/src/win32/LinkOptions.cpp index dd04a08e..463936df 100644 --- a/src/win32/LinkOptions.cpp +++ b/src/win32/LinkOptions.cpp @@ -5,39 +5,46 @@ #include "LinkOptions.h" #include "../gba/GBALink.h" -extern lserver ls; - #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif +template +void DDX_CBData(CDataExchange* pDX, int nIDC, T& data) +{ + HWND hWndCtrl = pDX->PrepareCtrl(nIDC); + if (pDX->m_bSaveAndValidate) + { + int index = static_cast(::SendMessage(hWndCtrl, CB_GETCURSEL, 0, 0L)); + data = (index == CB_ERR ? NULL : static_cast(::SendMessage(hWndCtrl, CB_GETITEMDATA, index, 0L))); + } + else + { + int count = static_cast(::SendMessage(hWndCtrl, CB_GETCOUNT, 0, 0L)); + for (int i = 0; i != count; ++i) + { + if (static_cast(::SendMessage(hWndCtrl, CB_GETITEMDATA, i, 0L)) == data) + { + ::SendMessage(hWndCtrl, CB_SETCURSEL, i, 0L); + return; + } + } + ::SendMessage(hWndCtrl, CB_SETCURSEL, -1, 0L); + } +} + ///////////////////////////////////////////////////////////////////////////// // LinkOptions dialog -CMyTabCtrl::CMyTabCtrl(){ - m_tabdialog[0] = new LinkGeneral; - m_tabdialog[1] = new LinkServer; - m_tabdialog[2] = new LinkClient; -} - -CMyTabCtrl::~CMyTabCtrl() -{ - m_tabdialog[0]->DestroyWindow(); - m_tabdialog[1]->DestroyWindow(); - m_tabdialog[2]->DestroyWindow(); - - delete m_tabdialog[0]; - delete m_tabdialog[1]; - delete m_tabdialog[2]; -} - LinkOptions::LinkOptions(CWnd* pParent /*=NULL*/) : CDialog(LinkOptions::IDD, pParent) { //{{AFX_DATA_INIT(LinkOptions) - // NOTE: the ClassWizard will add member initialization here + m_numplayers = 0; + m_type = theApp.linkMode; + m_server = FALSE; //}}AFX_DATA_INIT } @@ -46,55 +53,52 @@ void LinkOptions::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(LinkOptions) + DDX_CBData(pDX, IDC_LINK_MODE, m_type); + DDX_Control(pDX, IDC_LINKTIMEOUT, m_timeout); + DDX_Control(pDX, IDC_LINK_MODE, m_mode); + DDX_Control(pDX, IDC_SERVERIP, m_serverip); + DDX_Check(pDX, IDC_SSPEED, m_hacks); + DDX_Radio(pDX, IDC_LINK2P, m_numplayers); + DDX_Radio(pDX, IDC_LINK_CLIENT, m_server); //}}AFX_DATA_MAP } BOOL LinkOptions::OnInitDialog(){ - TCITEM tabitem; - char tabtext[3][8] = {"General", "Server", "Client"}; - int i; + char timeout[6]; CDialog::OnInitDialog(); - m_tabctrl.SubclassDlgItem(IDC_TAB1, this); + AddMode("Nothing (Disconnect)", LINK_DISCONNECTED); + AddMode("Cable - Single Computer", LINK_CABLE_IPC); + AddMode("Cable - Network", LINK_CABLE_SOCKET); + AddMode("GameCube - Dolphin", LINK_GAMECUBE_DOLPHIN); + AddMode("Wireless adapter - Single Computer", LINK_RFU_IPC); - tabitem.mask = TCIF_TEXT; + sprintf(timeout, "%d", theApp.linkTimeout); - for(i=0;i<3;i++){ - tabitem.pszText = tabtext[i]; - m_tabctrl.InsertItem(i, &tabitem); - } - m_tabctrl.m_tabdialog[0]->Create(IDD_LINKTAB1, this); - m_tabctrl.m_tabdialog[1]->Create(IDD_LINKTAB2, this); - m_tabctrl.m_tabdialog[2]->Create(IDD_LINKTAB3, this); + m_timeout.LimitText(5); + m_timeout.SetWindowText(timeout); - m_tabctrl.m_tabdialog[0]->ShowWindow(SW_SHOW); - m_tabctrl.m_tabdialog[1]->ShowWindow(SW_HIDE); - m_tabctrl.m_tabdialog[2]->ShowWindow(SW_HIDE); + m_serverip.SetWindowText(theApp.linkHost); - m_tabctrl.SetCurSel(0); - m_tabctrl.OnSwitchTabs(); + UpdateAvailability(); + + UpdateData(FALSE); return TRUE; } - BOOL LinkOptions::PreTranslateMessage(MSG* pMsg) - { - return m_tabctrl.TranslatePropSheetMsg(pMsg) ? TRUE : - CDialog::PreTranslateMessage(pMsg); - } - - - - -BEGIN_MESSAGE_MAP(LinkOptions, CDialog) + BEGIN_MESSAGE_MAP(LinkOptions, CDialog) //{{AFX_MSG_MAP(LinkOptions) - ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, OnSelchangeTab1) + ON_CBN_SELCHANGE(IDC_LINK_MODE, &LinkOptions::OnCbnSelchangeLinkMode) ON_BN_CLICKED(ID_OK, OnOk) ON_BN_CLICKED(ID_CANCEL, OnCancel) + ON_BN_CLICKED(IDC_LINK_SERVER, &LinkOptions::OnBnClickedLinkServer) + ON_BN_CLICKED(IDC_LINK_CLIENT, &LinkOptions::OnBnClickedLinkClient) //}}AFX_MSG_MAP -END_MESSAGE_MAP() + + END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // LinkOptions message handlers @@ -102,584 +106,123 @@ END_MESSAGE_MAP() // LinkGeneral dialog -LinkGeneral::LinkGeneral(CWnd* pParent /*=NULL*/) - : CDialog(LinkGeneral::IDD, pParent) -{ - //{{AFX_DATA_INIT(LinkGeneral) - //}}AFX_DATA_INIT +void LinkOptions::AddMode(LPCTSTR name, int value) { + m_mode.AddString(name); + int index = m_mode.FindStringExact(-1, name); + m_mode.SetItemData(index, value); } -void LinkGeneral::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - //{{AFX_DATA_MAP(LinkGeneral) - DDX_Radio(pDX, IDC_LINK_SINGLE, m_type); - DDX_Control(pDX, IDC_LINKTIMEOUT, m_timeout); - //}}AFX_DATA_MAP -} - - -BEGIN_MESSAGE_MAP(LinkGeneral, CDialog) - //{{AFX_MSG_MAP(LinkGeneral) - ON_BN_CLICKED(IDC_LINK_SINGLE, OnRadio1) - ON_BN_CLICKED(IDC_LINK_LAN, OnRadio2) - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// LinkGeneral message handlers -///////////////////////////////////////////////////////////////////////////// -// LinkServer dialog - - -LinkServer::LinkServer(CWnd* pParent /*=NULL*/) - : CDialog(LinkServer::IDD, pParent) -{ - //{{AFX_DATA_INIT(LinkServer) - m_numplayers = -1; - m_prottype = -1; - m_speed = FALSE; - //}}AFX_DATA_INIT -} - - -void LinkServer::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - //{{AFX_DATA_MAP(LinkServer) - DDX_Radio(pDX, IDC_LINK2P, m_numplayers); - DDX_Radio(pDX, IDC_LINKTCP, m_prottype); - DDX_Check(pDX, IDC_SSPEED, m_speed); - //}}AFX_DATA_MAP -} - - -BEGIN_MESSAGE_MAP(LinkServer, CDialog) - //{{AFX_MSG_MAP(LinkServer) - ON_BN_CLICKED(IDC_SERVERSTART, OnServerStart) - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// LinkServer message handlers - -LinkClient::LinkClient(CWnd* pParent /*=NULL*/) - : CDialog(LinkClient::IDD, pParent) -{ - //{{AFX_DATA_INIT(LinkClient) - m_prottype = -1; - m_hacks = -1; - //}}AFX_DATA_INIT -} - - -void LinkClient::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - //{{AFX_DATA_MAP(LinkClient) - DDX_Control(pDX, IDC_SERVERIP, m_serverip); - DDX_Radio(pDX, IDC_CLINKTCP, m_prottype); - DDX_Radio(pDX, IDC_SPEEDOFF, m_hacks); - //}}AFX_DATA_MAP -} - - -BEGIN_MESSAGE_MAP(LinkClient, CDialog) - //{{AFX_MSG_MAP(LinkClient) - ON_BN_CLICKED(IDC_LINKCONNECT, OnLinkConnect) - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// LinkClient message handlers - -BOOL LinkServer::OnInitDialog() -{ - CDialog::OnInitDialog(); - - m_numplayers = lanlink.numslaves; - m_prottype = lanlink.type; - m_speed = lanlink.speed; - - UpdateData(FALSE); - - return TRUE; -} - -void LinkOptions::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult) -{ - m_tabctrl.OnSwitchTabs(); - *pResult = 0; -} - -IMPLEMENT_DYNAMIC(CMyTabCtrl, CTabCtrl) -BEGIN_MESSAGE_MAP(CMyTabCtrl, CTabCtrl) - ON_NOTIFY_REFLECT(TCN_SELCHANGING, OnSelChanging) -END_MESSAGE_MAP() - -BOOL CMyTabCtrl::SubclassDlgItem(UINT nID, CWnd* pParent) -{ - if (!CTabCtrl::SubclassDlgItem(nID, pParent)) - return FALSE; - - ModifyStyle(0, TCS_OWNERDRAWFIXED); - - // If first tab is disabled, go to next enabled tab - if (!IsTabEnabled(0)) { - int iTab = NextEnabledTab(0, TRUE); - SetActiveTab(iTab); - } - return TRUE; -} - -BOOL CMyTabCtrl::IsTabEnabled(int iTab) -{ - if (!lanlink.active && iTab > 0) - return false; - return true; -} - -////////////////// -// Draw the tab: mimic SysTabControl32, except use gray if tab is disabled -// -void CMyTabCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) -{ - DRAWITEMSTRUCT& ds = *lpDrawItemStruct; - - int iItem = ds.itemID; - - // Get tab item info - char text[128]; - TCITEM tci; - tci.mask = TCIF_TEXT; - tci.pszText = text; - tci.cchTextMax = sizeof(text); - GetItem(iItem, &tci); - - // use draw item DC - CDC dc; - dc.Attach(ds.hDC); - - // calculate text rectangle and color - CRect rc = ds.rcItem; - rc += CPoint(1,4); // ?? by trial and error - - // draw the text - OnDrawText(dc, rc, text, !IsTabEnabled(iItem)); - - dc.Detach(); -} - -////////////////// -// Draw tab text. You can override to use different color/font. -// -void CMyTabCtrl::OnDrawText(CDC& dc, CRect rc, - CString sText, BOOL bDisabled) -{ - dc.SetTextColor(GetSysColor(bDisabled ? COLOR_3DHILIGHT : COLOR_BTNTEXT)); - dc.DrawText(sText, &rc, DT_CENTER|DT_VCENTER); - - if (bDisabled) { - // disabled: draw again shifted northwest for shadow effect - rc += CPoint(-1,-1); - dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT)); - dc.DrawText(sText, &rc, DT_CENTER|DT_VCENTER); - } -} - -////////////////// -// Selection is changing: disallow if tab is disabled -// -void CMyTabCtrl::OnSelChanging(NMHDR* pnmh, LRESULT* pRes) -{ - TRACE("CMyTabCtrl::OnSelChanging\n"); - - // Figure out index of new tab we are about to go to, as opposed - // to the current one we're at. Believe it or not, Windows doesn't - // pass this info - // - TC_HITTESTINFO htinfo; - GetCursorPos(&htinfo.pt); - ScreenToClient(&htinfo.pt); - int iNewTab = HitTest(&htinfo); - - if (iNewTab >= 0 && !IsTabEnabled(iNewTab)) - *pRes = TRUE; // tab disabled: prevent selection -} - -////////////////// -// Trap arrow-left key to skip disabled tabs. -// This is the only way to know where we're coming from--ie from -// arrow-left (prev) or arrow-right (next). -// -BOOL CMyTabCtrl::PreTranslateMessage(MSG* pMsg) -{ - if (pMsg->message == WM_KEYDOWN && - (pMsg->wParam == VK_LEFT || pMsg->wParam == VK_RIGHT)) { - - int iNewTab = (pMsg->wParam == VK_LEFT) ? - PrevEnabledTab(GetCurSel(), FALSE) : - NextEnabledTab(GetCurSel(), FALSE); - if (iNewTab >= 0) - SetActiveTab(iNewTab); - return TRUE; - } - return CTabCtrl::PreTranslateMessage(pMsg); -} - -//////////////// -// Translate parent property sheet message. Translates Control-Tab and -// Control-Shift-Tab keys. These are normally handled by the property -// sheet, so you must call this function from your prop sheet's -// PreTranslateMessage function. -// -BOOL CMyTabCtrl::TranslatePropSheetMsg(MSG* pMsg) -{ - WPARAM key = pMsg->wParam; - if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && - (key == VK_TAB || key == VK_PRIOR || key == VK_NEXT)) { - - int iNewTab = (key==VK_PRIOR || GetAsyncKeyState(VK_SHIFT) < 0) ? - PrevEnabledTab(GetCurSel(), TRUE) : - NextEnabledTab(GetCurSel(), TRUE); - if (iNewTab >= 0) - SetActiveTab(iNewTab); - return TRUE; - } - return FALSE; -} - -////////////////// -// Helper to set the active page, when moving backwards (left-arrow and -// Control-Shift-Tab). Must simulate Windows messages to tell parent I -// am changing the tab; SetCurSel does not do this!! -// -// In normal operation, this fn will always succeed, because I don't call it -// unless I already know IsTabEnabled() = TRUE; but if you call SetActiveTab -// with a random value, it could fail. -// -BOOL CMyTabCtrl::SetActiveTab(UINT iNewTab) -{ - TRACE("CMyTabCtrl::SetActiveTab\n"); - - // send the parent TCN_SELCHANGING - NMHDR nmh; - nmh.hwndFrom = m_hWnd; - nmh.idFrom = GetDlgCtrlID(); - nmh.code = TCN_SELCHANGING; - - if (GetParent()->SendMessage(WM_NOTIFY, nmh.idFrom, (LPARAM)&nmh) >=0) { - // OK to change: set the new tab - SetCurSel(iNewTab); - - // send parent TCN_SELCHANGE - nmh.code = TCN_SELCHANGE; - GetParent()->SendMessage(WM_NOTIFY, nmh.idFrom, (LPARAM)&nmh); - return TRUE; - } - return FALSE; -} - -///////////////// -// Return the index of the next enabled tab after a given index, or -1 if none -// (0 = first tab). -// If bWrap is TRUE, wrap from beginning to end; otherwise stop at zero. -// -int CMyTabCtrl::NextEnabledTab(int iCurrentTab, BOOL bWrap) -{ - int nTabs = GetItemCount(); - for (int iTab = iCurrentTab+1; iTab != iCurrentTab; iTab++) { - if (iTab >= nTabs) { - if (!bWrap) - return -1; - iTab = 0; - } - if (IsTabEnabled(iTab)) { - return iTab; - } - } - return -1; -} - -///////////////// -// Return the index of the previous enabled tab before a given index, or -1. -// (0 = first tab). -// If bWrap is TRUE, wrap from beginning to end; otherwise stop at zero. -// -int CMyTabCtrl::PrevEnabledTab(int iCurrentTab, BOOL bWrap) -{ - for (int iTab = iCurrentTab-1; iTab != iCurrentTab; iTab--) { - if (iTab < 0) { - if (!bWrap) - return -1; - iTab = GetItemCount() - 1; - } - if (IsTabEnabled(iTab)) { - return iTab; - } - } - return -1; -} - - -void CMyTabCtrl::OnSwitchTabs(void) -{ - CRect clientRect, wndRect; - int i; - - GetClientRect(clientRect); - AdjustRect(FALSE, clientRect); - GetWindowRect(wndRect); - GetParent()->ScreenToClient(wndRect); - clientRect.OffsetRect(wndRect.left, wndRect.top); - - if(lanlink.active==0) - SetCurSel(0); - - for(i=0;i<3;i++){ - if(i==GetCurSel()){ - m_tabdialog[i]->SetWindowPos(&wndTop, clientRect.left, clientRect.top, clientRect.Width(), clientRect.Height(), SWP_SHOWWINDOW); - } else { - m_tabdialog[i]->ShowWindow(SW_HIDE); - } - } - return; -} - - void LinkOptions::OnOk() { - GetAllData((LinkGeneral*)m_tabctrl.m_tabdialog[0]); + static const int length = 256; + int timeout; + CString timeoutStr; + CString host; + CString title; + CString addressMessage; + + UpdateData(TRUE); + + // Close any previous link + CloseLink(); + + m_serverip.GetWindowText(host); + m_timeout.GetWindowText(timeoutStr); + sscanf(timeoutStr, "%d", &timeout); + SetLinkTimeout(timeout); + + LinkMode newMode = (LinkMode) m_type; + bool needsServerHost = newMode == LINK_GAMECUBE_DOLPHIN || (newMode == LINK_CABLE_SOCKET && !m_server); + + if (needsServerHost) { + bool valid = SetLinkServerHost(host); + if (!valid) { + AfxMessageBox("You must enter a valid host name", MB_OK | MB_ICONSTOP); + return; + } + } + + EnableSpeedHacks(m_hacks); + EnableLinkServer(m_server, m_numplayers + 1); + + if (m_server) { + char localhost[length]; + GetLinkServerHost(localhost, length); + + title = "Waiting for clients..."; + addressMessage.Format("Server IP address is: %s\n", localhost); + } else { + title = "Waiting for connection..."; + addressMessage.Format("Connecting to %s\n", host); + } + + // Init link + ConnectionState state = InitLink(newMode); + + // Display a progress dialog while the connection is establishing + if (state == LINK_NEEDS_UPDATE) { + ServerWait *dlg = new ServerWait(); + dlg->Create(ServerWait::IDD, this); + dlg->m_plconn[1] = title; + dlg->m_serveraddress = addressMessage; + dlg->ShowWindow(SW_SHOW); + + while (state == LINK_NEEDS_UPDATE) { + // Ask the core for updates + char message[length]; + state = ConnectLinkUpdate(message, length); + + // Update the wait message + if (strlen(message)) { + dlg->m_plconn[1] = message; + } + + // Step the progress bar and update dialog data + dlg->m_prgctrl.StepIt(); + dlg->UpdateData(false); + + // Process Windows messages + MSG msg; + while (PeekMessage (&msg, 0, 0, 0, PM_NOREMOVE)) { + AfxGetApp()->PumpMessage(); + } + + // Check whether the user has aborted + if (dlg->m_userAborted) { + state = LINK_ABORT; + } + } + + delete dlg; + } + + // The user canceled the connection attempt + if (state == LINK_ABORT) { + CloseLink(); + return; + } + + // Something failed during init + if (state == LINK_ERROR) { + AfxMessageBox("Error occurred.\nPlease try again.", MB_OK | MB_ICONSTOP); + return; + } + + theApp.linkTimeout = timeout; + theApp.linkMode = GetLinkMode(); + theApp.linkHost = host; + CDialog::OnOK(); return; } -void LinkGeneral::OnRadio1() -{ - m_type = 0; - lanlink.active = 0; - GetParent()->Invalidate(); -} - -void LinkGeneral::OnRadio2() -{ - m_type = 1; - lanlink.active = 1; - GetParent()->Invalidate(); -} - -BOOL LinkGeneral::OnInitDialog(){ - - char timeout[6]; - - CDialog::OnInitDialog(); - - m_timeout.LimitText(5); - sprintf(timeout, "%d", linktimeout); - m_timeout.SetWindowText(timeout); - - m_type = lanlink.active; - - UpdateData(FALSE); - - return TRUE; -} - - void LinkOptions::OnCancel() { CDialog::OnCancel(); return; } -class Win32ServerInfoDisplay : public ServerInfoDisplay -{ -public: - Win32ServerInfoDisplay(ServerWait *_dlg) - { - dlg = _dlg; - } - ~Win32ServerInfoDisplay() - { - if (dlg) - { - // not connected - MessageBox(NULL, "Failed to connect.", "Link", MB_OK); - dlg->SendMessage(WM_CLOSE, 0, 0); - } - - delete dlg; - dlg = NULL; - } - - void ShowServerIP(const sf::IPAddress& addr) - { - dlg->m_serveraddress.Format("Server IP address is: %s", addr.ToString()); - } - - void ShowConnect(const int player) - { - dlg->m_plconn[0].Format("Player %d connected", player); - dlg->UpdateData(false); - } - - void Ping() - { - dlg->m_prgctrl.StepIt(); - } - - void Connected() - { - MessageBox(NULL, "All players connected", "Link", MB_OK); - dlg->SendMessage(WM_CLOSE, 0, 0); - delete dlg; - dlg = NULL; - } - -private: - ServerWait *dlg; -}; - -void LinkServer::OnServerStart() -{ - UpdateData(TRUE); - - lanlink.numslaves = m_numplayers+1; - lanlink.type = m_prottype; - lanlink.server = true; - lanlink.speed = m_speed==1 ? true : false; - sf::IPAddress addr; - - // These must be created on the heap - referenced from the connection thread - ServerWait *dlg = new ServerWait(); - dlg->Create(IDD_SERVERWAIT, this); - dlg->ShowWindow(SW_SHOW); - - // Owns the ServerWait* - Win32ServerInfoDisplay *dlginfo = new Win32ServerInfoDisplay(dlg); - - // ls thread will own the dlginfo - if (!ls.Init(dlginfo)) - { - // Thread didn't get created - delete dlginfo; - MessageBox("Error occurred.\nPlease try again.", "Error", MB_OK); - } - - return; -} - -BOOL LinkClient::OnInitDialog() -{ - CDialog::OnInitDialog(); - - m_prottype = lanlink.type; - m_hacks = lanlink.speed; - - UpdateData(FALSE); - - return TRUE; -} - -class Win32ClientInfoDisplay : public ClientInfoDisplay -{ -public: - Win32ClientInfoDisplay(ServerWait *_dlg) - { - dlg = _dlg; - } - - ~Win32ClientInfoDisplay() - { - if (dlg) - { - // not connected - MessageBox(NULL, "Failed to connect.", "Link", MB_OK); - dlg->SendMessage(WM_CLOSE, 0, 0); - } - - delete dlg; - dlg = NULL; - } - - void ConnectStart(const sf::IPAddress& addr) - { - dlg->SetWindowText("Connecting..."); - } - - void ShowConnect(const int player, const int togo) - { - dlg->m_serveraddress.Format("Connected as #%d", player); - if (togo) - dlg->m_plconn[0].Format("Waiting for %d players to join", togo); - else - dlg->m_plconn[0].Format("All players joined."); - } - - void Ping() - { - dlg->m_prgctrl.StepIt(); - } - - void Connected() - { - MessageBox(NULL, "Connected.", "Link", MB_OK); - dlg->SendMessage(WM_CLOSE, 0, 0); - delete dlg; - dlg = NULL; - } - -private: - ServerWait *dlg; -}; - -void LinkClient::OnLinkConnect() -{ - char ipaddress[31]; - - UpdateData(TRUE); - - lanlink.type = m_prottype; - lanlink.server = false; - lanlink.speed = m_hacks==1 ? true : false; - - m_serverip.GetWindowText(ipaddress, 30); - - // These must be created on the heap - referenced from the connection thread - ServerWait *dlg = new ServerWait(); - dlg->Create(IDD_SERVERWAIT, this); - dlg->ShowWindow(SW_SHOW); - - // Owns the ServerWait* - Win32ClientInfoDisplay *dlginfo = new Win32ClientInfoDisplay(dlg); - - // lc thread will own the dlginfo - if (!lc.Init(sf::IPAddress(std::string(ipaddress)), dlginfo)) - { - // Thread didn't get created - delete dlginfo; - MessageBox("Error occurred.\nPlease try again.", "Error", MB_OK); - } - - return; -} - -void LinkOptions::GetAllData(LinkGeneral *src) -{ - char timeout[6]; - - src->UpdateData(true); - - src->m_timeout.GetWindowText(timeout, 5); - sscanf(timeout, "%d", &linktimeout); - - if(src->m_type==0){ - lanlink.speed = 0; - } - - return; -} ///////////////////////////////////////////////////////////////////////////// // ServerWait dialog @@ -693,6 +236,8 @@ ServerWait::ServerWait(CWnd* pParent /*=NULL*/) m_plconn[1] = _T(""); m_plconn[2] = _T(""); //}}AFX_DATA_INIT + + m_userAborted = false; } @@ -719,35 +264,47 @@ END_MESSAGE_MAP() void ServerWait::OnCancel() { - lanlink.terminate = true; - CDialog::OnCancel(); + m_userAborted = true; + ShowWindow(SW_HIDE); } -BOOL LinkGeneral::PreTranslateMessage(MSG* pMsg) +void LinkOptions::OnCbnSelchangeLinkMode() { - if(pMsg->message==WM_KEYDOWN) - if(pMsg->wParam==VK_RETURN||pMsg->wParam==VK_ESCAPE) - pMsg->wParam = NULL; - - return CDialog::PreTranslateMessage(pMsg); + UpdateData(TRUE); + UpdateAvailability(); } -BOOL LinkClient::PreTranslateMessage(MSG* pMsg) +void LinkOptions::UpdateAvailability() { - if(pMsg->message==WM_KEYDOWN) - if(pMsg->wParam==VK_RETURN||pMsg->wParam==VK_ESCAPE) - pMsg->wParam = NULL; + bool isDisconnected = m_type == LINK_DISCONNECTED; + bool isNetwork = m_type == LINK_CABLE_SOCKET; + bool canHaveServer = (m_type == LINK_CABLE_SOCKET && !m_server) || m_type == LINK_GAMECUBE_DOLPHIN; + bool hasHacks = m_type == LINK_CABLE_SOCKET; - return CDialog::PreTranslateMessage(pMsg); -} + GetDlgItem(IDC_LINK_CLIENT)->EnableWindow(isNetwork); + GetDlgItem(IDC_LINK_SERVER)->EnableWindow(isNetwork); + GetDlgItem(IDC_SSPEED)->EnableWindow(isNetwork); -BOOL LinkServer::PreTranslateMessage(MSG* pMsg) -{ - if(pMsg->message==WM_KEYDOWN) - if(pMsg->wParam==VK_RETURN||pMsg->wParam==VK_ESCAPE) - pMsg->wParam = NULL; + m_serverip.EnableWindow(canHaveServer); + m_timeout.EnableWindow(!isDisconnected); - return CDialog::PreTranslateMessage(pMsg); + GetDlgItem(IDC_LINK2P)->EnableWindow(isNetwork && m_server); + GetDlgItem(IDC_LINK3P)->EnableWindow(isNetwork && m_server); + GetDlgItem(IDC_LINK4P)->EnableWindow(isNetwork && m_server); } #endif // NO_LINK + + +void LinkOptions::OnBnClickedLinkServer() +{ + UpdateData(TRUE); + UpdateAvailability(); +} + + +void LinkOptions::OnBnClickedLinkClient() +{ + UpdateData(TRUE); + UpdateAvailability(); +} diff --git a/src/win32/LinkOptions.h b/src/win32/LinkOptions.h index 17d1383c..2bbe3503 100644 --- a/src/win32/LinkOptions.h +++ b/src/win32/LinkOptions.h @@ -1,74 +1,5 @@ #pragma once -class CMyTabCtrl : public CTabCtrl { - DECLARE_DYNAMIC(CMyTabCtrl) -public: - CMyTabCtrl(void); - ~CMyTabCtrl(void); - - BOOL IsTabEnabled(int iTab); // you must override - BOOL TranslatePropSheetMsg(MSG* pMsg); // call from prop sheet - BOOL SubclassDlgItem(UINT nID, CWnd* pParent); // non-virtual override - - // helpers - int NextEnabledTab(int iTab, BOOL bWrap); // get next enabled tab - int PrevEnabledTab(int iTab, BOOL bWrap); // get prev enabled tab - BOOL SetActiveTab(UINT iNewTab); // set tab (fail if disabled) - - CDialog *m_tabdialog[3]; - - void OnSwitchTabs(void); -protected: - DECLARE_MESSAGE_MAP() - afx_msg void OnSelChanging(NMHDR* pNmh, LRESULT* pRes); - - // MFC overrides - virtual BOOL PreTranslateMessage(MSG* pMsg); - virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); - - // override to draw text only; eg, colored text or different font - virtual void OnDrawText(CDC& dc, CRect rc, CString sText, BOOL bDisabled); - -}; - -///////////////////////////////////////////////////////////////////////////// -// LinkGeneral dialog - -class LinkGeneral : public CDialog -{ -// Construction -public: - LinkGeneral(CWnd* pParent = NULL); // standard constructor - -// Dialog Data - //{{AFX_DATA(LinkGeneral) - enum { IDD = IDD_LINKTAB1 }; - int m_type; - CEdit m_timeout; - //}}AFX_DATA - - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(LinkGeneral) - public: - virtual BOOL PreTranslateMessage(MSG* pMsg); - protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - //}}AFX_VIRTUAL - -// Implementation -protected: - - // Generated message map functions - //{{AFX_MSG(LinkGeneral) - virtual BOOL OnInitDialog(); - afx_msg void OnRadio1(); - afx_msg void OnRadio2(); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() -}; - ///////////////////////////////////////////////////////////////////////////// // LinkOptions dialog @@ -77,19 +8,22 @@ class LinkOptions : public CDialog // Construction public: LinkOptions(CWnd* pParent = NULL); // standard constructor - void GetAllData(LinkGeneral*); // Dialog Data //{{AFX_DATA(LinkOptions) enum { IDD = IDD_LINKTAB }; - CMyTabCtrl m_tabctrl; + int m_type; + CEdit m_timeout; + CComboBox m_mode; + CEdit m_serverip; + BOOL m_server; + int m_numplayers; + BOOL m_hacks; //}}AFX_DATA // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(LinkOptions) - public: - virtual BOOL PreTranslateMessage(MSG* pMsg); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL @@ -97,88 +31,22 @@ public: // Implementation protected: + void AddMode(LPCTSTR name, int value); + void UpdateAvailability(); + // Generated message map functions //{{AFX_MSG(LinkOptions) - afx_msg void OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnCbnSelchangeLinkMode(); virtual BOOL OnInitDialog(); afx_msg void OnOk(); afx_msg void OnCancel(); //}}AFX_MSG DECLARE_MESSAGE_MAP() -}; - -///////////////////////////////////////////////////////////////////////////// -// LinkServer dialog - -class LinkServer : public CDialog -{ -// Construction public: - LinkServer(CWnd* pParent = NULL); // standard constructor - -// Dialog Data - //{{AFX_DATA(LinkServer) - enum { IDD = IDD_LINKTAB2 }; - int m_numplayers; - int m_prottype; - BOOL m_speed; - //}}AFX_DATA - - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(LinkServer) - public: - virtual BOOL PreTranslateMessage(MSG* pMsg); - protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - //}}AFX_VIRTUAL - -// Implementation -protected: - - // Generated message map functions - //{{AFX_MSG(LinkServer) - virtual BOOL OnInitDialog(); - afx_msg void OnServerStart(); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() + afx_msg void OnBnClickedLinkServer(); + afx_msg void OnBnClickedLinkClient(); }; -class LinkClient : public CDialog -{ -// Construction -public: - LinkClient(CWnd* pParent = NULL); // standard constructor - -// Dialog Data - //{{AFX_DATA(LinkClient) - enum { IDD = IDD_LINKTAB3 }; - CEdit m_serverip; - int m_prottype; - int m_hacks; - //}}AFX_DATA - - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(LinkClient) - public: - virtual BOOL PreTranslateMessage(MSG* pMsg); - protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - //}}AFX_VIRTUAL - -// Implementation -protected: - - // Generated message map functions - //{{AFX_MSG(LinkClient) - virtual BOOL OnInitDialog(); - afx_msg void OnLinkConnect(); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() -}; ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -196,10 +64,9 @@ public: CProgressCtrl m_prgctrl; CString m_serveraddress; CString m_plconn[3]; - //CString m_p2conn; - //CString m_p3conn; //}}AFX_DATA + bool m_userAborted; // Overrides // ClassWizard generated virtual function overrides diff --git a/src/win32/MainWnd.cpp b/src/win32/MainWnd.cpp index c84dce54..5e6e0d03 100644 --- a/src/win32/MainWnd.cpp +++ b/src/win32/MainWnd.cpp @@ -323,11 +323,6 @@ BEGIN_MESSAGE_MAP(MainWnd, CWnd) ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATOR_GAMEOVERRIDES, OnUpdateOptionsEmulatorGameoverrides) ON_COMMAND(ID_HELP_GNUPUBLICLICENSE, OnHelpGnupubliclicense) ON_COMMAND(ID_OPTIONS_LINK_OPTIONS, OnLinkOptions) - ON_COMMAND(ID_OPTIONS_LINK_WIRELESSADAPTER, OnOptionsLinkRFU) - ON_UPDATE_COMMAND_UI(ID_OPTIONS_LINK_WIRELESSADAPTER, OnUpdateOptionsLinkRFU) - ON_COMMAND(ID_OPTIONS_LINK_ENABLE, OnOptionsLinkEnable) - ON_UPDATE_COMMAND_UI(ID_OPTIONS_LINK_ENABLE, OnUpdateOptionsLinkEnable) - ON_COMMAND(ID_OPTIONS_JOYBUS, &MainWnd::OnOptionsJoybus) //}}AFX_MSG_MAP ON_COMMAND_EX_RANGE(ID_FILE_MRU_FILE1, ID_FILE_MRU_FILE10, OnFileRecentFile) diff --git a/src/win32/MainWnd.h b/src/win32/MainWnd.h index 49993323..79b895e5 100644 --- a/src/win32/MainWnd.h +++ b/src/win32/MainWnd.h @@ -330,11 +330,6 @@ protected: afx_msg void OnOptionsSoundHardwareacceleration(); afx_msg void OnUpdateOptionsSoundHardwareacceleration(CCmdUI *pCmdUI); afx_msg void OnLinkOptions(); - afx_msg void OnOptionsLinkRFU(); - afx_msg void OnUpdateOptionsLinkRFU(CCmdUI* pCmdUI); - afx_msg void OnOptionsLinkEnable(); - afx_msg void OnUpdateOptionsLinkEnable(CCmdUI* pCmdUI); - afx_msg void OnOptionsJoybus(); afx_msg void OnOutputapiDirectsound(); afx_msg void OnUpdateOutputapiDirectsound(CCmdUI *pCmdUI); diff --git a/src/win32/MainWndOptions.cpp b/src/win32/MainWndOptions.cpp index efce4465..4bb7eb8f 100644 --- a/src/win32/MainWndOptions.cpp +++ b/src/win32/MainWndOptions.cpp @@ -7,7 +7,6 @@ #include "FileDlg.h" #include "GameOverrides.h" #include "LinkOptions.h" -#include "JoybusOptions.h" #include "GBColorDlg.h" #include "Joypad.h" #include "MaxScale.h" @@ -1579,42 +1578,8 @@ void MainWnd::OnLinkOptions() dlg.DoModal(); } -void MainWnd::OnOptionsLinkRFU() -{ - if(rfu_enabled) rfu_enabled = false; - else { - rfu_enabled = true; - MessageBox("Please note this is the first version\nof RFU emulation code and it's not 100% bug free.\nAlso only 2 players single computer are supported at this time.", "Warning", MB_OK); - } -} - -void MainWnd::OnUpdateOptionsLinkEnable(CCmdUI* pCmdUI) -{ - pCmdUI->SetCheck(gba_link_enabled); -} - -void MainWnd::OnOptionsLinkEnable() -{ - gba_link_enabled = !gba_link_enabled; -} - -void MainWnd::OnUpdateOptionsLinkRFU(CCmdUI* pCmdUI) -{ - pCmdUI->SetCheck(rfu_enabled); -} - -void MainWnd::OnOptionsJoybus() -{ - JoybusOptions dlg; - dlg.DoModal(); -} #else void MainWnd::OnLinkOptions() { } -void MainWnd::OnOptionsLinkRFU() { } -void MainWnd::OnUpdateOptionsLinkEnable(CCmdUI*) { } -void MainWnd::OnOptionsLinkEnable() { } -void MainWnd::OnUpdateOptionsLinkRFU(CCmdUI*) { } -void MainWnd::OnOptionsJoybus() { } #endif void MainWnd::OnOptionsEmulatorGameoverrides() diff --git a/src/win32/VBA.cpp b/src/win32/VBA.cpp index 0a0c245e..88fa05fd 100644 --- a/src/win32/VBA.cpp +++ b/src/win32/VBA.cpp @@ -119,9 +119,6 @@ void winlog(const char *msg, ...); /* Link ---------------------*/ -extern bool InitLink(void); -extern void CloseLink(void); -//extern int linkid; extern char inifile[]; /* ------------------- */ #ifdef _DEBUG @@ -358,7 +355,7 @@ VBA::~VBA() regInit(winBuffer); - JoyBusShutdown(); + CloseLink(); saveSettings(); @@ -453,9 +450,6 @@ BOOL VBA::InitInstance() if(p) *p = 0; - if(!InitLink()) - return FALSE; - bool force = false; if (m_lpCmdLine[0]) @@ -485,7 +479,7 @@ BOOL VBA::InitInstance() loadSettings(); - if(!initDisplay()) { + if(!initDisplay()) { if(videoOption >= VIDEO_320x240) { regSetDwordValue("video", VIDEO_2X); } @@ -1280,8 +1274,8 @@ BOOL VBA::OnIdle(LONG lCount) emulator.emuMain(emulator.emuCount); #ifndef NO_LINK - if (lanlink.connected && linkid && lc.numtransfers == 0) - lc.CheckConn(); + if (GetLinkMode() != LINK_DISCONNECTED) + CheckLinkConnection(); #endif if(rewindSaveNeeded && rewindMemory && emulator.emuWriteMemState) { @@ -1647,18 +1641,12 @@ void VBA::loadSettings() updateThrottle( (unsigned short)regQueryDwordValue( "throttle", 0 ) ); #ifndef NO_LINK - linktimeout = regQueryDwordValue("LinkTimeout", 1000); + linkTimeout = regQueryDwordValue("LinkTimeout", 1000); - rfu_enabled = regQueryDwordValue("RFU", false) ? true : false; - gba_link_enabled = regQueryDwordValue("linkEnabled", false) ? true : false; - gba_joybus_enabled = regQueryDwordValue("joybusEnabled", false) ? true : false; - buffer = regQueryStringValue("joybusHostAddr", ""); + linkMode = regQueryDwordValue("LinkMode", LINK_DISCONNECTED); - if(!buffer.IsEmpty()) { - joybusHostAddr = std::string(buffer); - } + linkHost = regQueryStringValue("LinkHostAddr", "localhost"); - lanlink.active = regQueryDwordValue("LAN", 0) ? true : false; #endif Sm60FPS::bSaveMoreCPU = regQueryDwordValue("saveMoreCPU", 0); @@ -2585,11 +2573,9 @@ void VBA::saveSettings() regSetDwordValue("saveMoreCPU", Sm60FPS::bSaveMoreCPU); #ifndef NO_LINK - regSetDwordValue("LinkTimeout", linktimeout); - regSetDwordValue("RFU", rfu_enabled); - regSetDwordValue("linkEnabled", gba_link_enabled); - regSetDwordValue("joybusEnabled", gba_joybus_enabled); - regSetStringValue("joybusHostAddr", joybusHostAddr.ToString().c_str()); + regSetDwordValue("LinkTimeout", linkTimeout); + regSetDwordValue("LinkMode", linkMode); + regSetStringValue("LinkHostAddr", linkHost); #endif regSetDwordValue("lastFullscreen", lastFullscreen); diff --git a/src/win32/VBA.h b/src/win32/VBA.h index 96f5bba0..06f9a613 100644 --- a/src/win32/VBA.h +++ b/src/win32/VBA.h @@ -207,6 +207,10 @@ class VBA : public CWinApp CString wndClass; + int linkTimeout; + int linkMode; + CString linkHost; + public: VBA(); ~VBA(); diff --git a/src/win32/VBA.rc b/src/win32/VBA.rc index 2157b05a..1cf9078c 100644 --- a/src/win32/VBA.rc +++ b/src/win32/VBA.rc @@ -112,54 +112,28 @@ BEGIN LTEXT "Please select filter plugin:",IDC_STATIC,6,6,162,8 END -IDD_LINKTAB DIALOGEX 0, 0, 254, 203 +IDD_LINKTAB DIALOGEX 0, 0, 254, 198 STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Link Options" +CAPTION "Connect Link" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN - CONTROL "Tab1",IDC_TAB1,"SysTabControl32",0x0,9,7,240,162 - PUSHBUTTON "OK",ID_OK,57,180,60,15 - PUSHBUTTON "Cancel",ID_CANCEL,140,180,57,15 -END - -IDD_LINKTAB1 DIALOGEX 0, 0, 184, 79 -STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE -FONT 8, "MS Sans Serif", 0, 0, 0x1 -BEGIN - LTEXT "Link timeout (in milliseconds)",IDC_STATIC,17,12,92,16 - EDITTEXT IDC_LINKTIMEOUT,116,10,53,14,ES_AUTOHSCROLL | ES_NUMBER - CONTROL "Single Computer",IDC_LINK_SINGLE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,17,27,71,16 - CONTROL "Network",IDC_LINK_LAN,"Button",BS_AUTORADIOBUTTON,17,43,70,16 -END - -IDD_LINKTAB2 DIALOGEX 0, 0, 210, 113 -STYLE DS_SETFONT | WS_CHILD -FONT 8, "MS Sans Serif", 0, 0, 0x0 -BEGIN - CONTROL "2",IDC_LINK2P,"Button",BS_AUTORADIOBUTTON | WS_GROUP,46,16,21,13 - CONTROL "3",IDC_LINK3P,"Button",BS_AUTORADIOBUTTON,94,16,21,13 - CONTROL "4",IDC_LINK4P,"Button",BS_AUTORADIOBUTTON,142,16,21,13 - CONTROL "TCP/IP",IDC_LINKTCP,"Button",BS_AUTORADIOBUTTON | WS_GROUP,54,47,42,14 - CONTROL "UDP",IDC_LINKUDP,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,121,47,33,14 - PUSHBUTTON "Start!",IDC_SERVERSTART,79,89,50,17,WS_DISABLED - LTEXT "Select number of players:",IDC_STATIC,60,7,89,10 - LTEXT "Select protocol:",IDC_STATIC,78,33,53,11 - CONTROL "Speed hacks",IDC_SSPEED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,76,70,57,12 -END - -IDD_LINKTAB3 DIALOGEX 0, 0, 188, 108 -STYLE DS_SETFONT | WS_CHILD -FONT 8, "MS Sans Serif", 0, 0, 0x1 -BEGIN - CONTROL "TCP/IP",IDC_CLINKTCP,"Button",BS_AUTORADIOBUTTON | WS_GROUP,58,20,39,12 - CONTROL "UDP",IDC_CLINKUDP,"Button",BS_AUTORADIOBUTTON | WS_DISABLED,118,20,32,12 - EDITTEXT IDC_SERVERIP,84,39,79,12,ES_AUTOHSCROLL | WS_GROUP - PUSHBUTTON "Connect",IDC_LINKCONNECT,75,81,59,16,WS_DISABLED - LTEXT "Select protocol:",IDC_STATIC,78,7,53,9 - LTEXT "Server IP address or hostname:",IDC_STATIC,7,37,62,18 - LTEXT "Speed hacks:",IDC_STATIC,7,64,47,10 - CONTROL "Off (accurate)",IDC_SPEEDOFF,"Button",BS_AUTORADIOBUTTON | WS_GROUP,60,63,57,12 - CONTROL "On (fast)",IDC_SPEEDON,"Button",BS_AUTORADIOBUTTON,128,63,48,12 + CONTROL "Client",IDC_LINK_CLIENT,"Button",BS_AUTORADIOBUTTON | WS_GROUP,53,46,33,10 + CONTROL "Server",IDC_LINK_SERVER,"Button",BS_AUTORADIOBUTTON,95,46,37,10 + LTEXT "Link with",IDC_LINK_WITH,11,10,38,10 + COMBOBOX IDC_LINK_MODE,50,8,194,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Link timeout (in milliseconds)",IDC_STATIC_TIMEOUT,11,153,92,12 + EDITTEXT IDC_LINKTIMEOUT,111,150,53,14,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Server IP address or hostname:",IDC_STATIC,25,70,75,18 + EDITTEXT IDC_SERVERIP,114,73,105,12,ES_AUTOHSCROLL | WS_GROUP + LTEXT "Expected number of players:",IDC_STATIC,25,94,89,10 + CONTROL "2",IDC_LINK2P,"Button",BS_AUTORADIOBUTTON | WS_GROUP,53,106,21,13 + CONTROL "3",IDC_LINK3P,"Button",BS_AUTORADIOBUTTON,99,106,21,13 + CONTROL "4",IDC_LINK4P,"Button",BS_AUTORADIOBUTTON,145,106,21,13 + CONTROL "Enable speed hacks",IDC_SSPEED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,121,138,12 + PUSHBUTTON "OK",ID_OK,60,176,60,15 + PUSHBUTTON "Cancel",ID_CANCEL,136,176,57,15 + GROUPBOX "Network options",IDC_GROUP_NETWORK,11,28,231,113 + LTEXT "Role:",IDC_LINK_ROLE,25,46,18,8 END IDD_SERVERWAIT DIALOG 0, 0, 186, 90 @@ -1203,17 +1177,6 @@ BEGIN COMBOBOX IDC_SAMPLE_RATE,66,54,66,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP END -IDD_JOYBUS_DIALOG DIALOGEX 0, 0, 209, 57 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -EXSTYLE WS_EX_TOOLWINDOW -CAPTION "Joybus Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,152,36,50,14 - CONTROL "Enable Joybus Connection",IDC_JOYBUS_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,195,10 - EDITTEXT IDC_JOYBUS_HOSTNAME,7,20,195,14,ES_AUTOHSCROLL -END - ///////////////////////////////////////////////////////////////////////////// // @@ -1243,15 +1206,7 @@ BEGIN BEGIN END - IDD_LINKTAB1, DIALOG - BEGIN - END - - IDD_LINKTAB2, DIALOG - BEGIN - END - - IDD_LINKTAB3, DIALOG + IDD_SERVERWAIT, DIALOG BEGIN END @@ -1587,14 +1542,6 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 163 END - - IDD_JOYBUS_DIALOG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 202 - TOPMARGIN, 7 - BOTTOMMARGIN, 50 - END END #endif // APSTUDIO_INVOKED @@ -1653,6 +1600,8 @@ BEGIN MENUITEM "Open GB...", ID_FILE_OPEN_GB MENUITEM "Close", ID_FILE_CLOSE MENUITEM SEPARATOR + MENUITEM "&Link with...", ID_OPTIONS_LINK_OPTIONS + MENUITEM SEPARATOR POPUP "Recent" BEGIN MENUITEM "&Reset", ID_FILE_RECENT_RESET @@ -1977,14 +1926,6 @@ BEGIN MENUITEM SEPARATOR MENUITEM "&Colors...", ID_OPTIONS_GAMEBOY_COLORS END - POPUP "&Link" - BEGIN - MENUITEM "Enable GBA Linking", ID_OPTIONS_LINK_ENABLE - MENUITEM "&Wireless Adapter", ID_OPTIONS_LINK_WIRELESSADAPTER - MENUITEM "&Options...", ID_OPTIONS_LINK_OPTIONS - MENUITEM SEPARATOR - MENUITEM "&Joybus Options...", ID_OPTIONS_JOYBUS - END END POPUP "&Cheats" BEGIN diff --git a/src/win32/resource.h b/src/win32/resource.h index dc6d820e..286ce5eb 100644 --- a/src/win32/resource.h +++ b/src/win32/resource.h @@ -107,7 +107,6 @@ #define IDD_FULLSCREEN 162 #define IDD_XAUDIO2_CONFIG 163 #define IDD_AUDIO_CORE_SETTINGS 164 -#define IDD_JOYBUS_DIALOG 165 #define IDC_R0 1000 #define IDC_EDIT_UP 1000 #define IDC_R1 1001 @@ -564,8 +563,13 @@ #define IDC_SOUND_FILTERING 1294 #define IDC_COMBO1 1296 #define IDC_SAMPLE_RATE 1296 -#define IDC_JOYBUS_HOSTNAME 1297 -#define IDC_JOYBUS_ENABLE 1298 +#define IDC_LINK_MODE 1296 +#define IDC_LINK_WITH 1300 +#define IDC_STATIC_TIMEOUT 1301 +#define IDC_LINK_SERVER 1302 +#define IDC_LINK_CLIENT 1303 +#define IDC_GROUP_NETWORK 1304 +#define IDC_LINK_ROLE 1305 #define IDS_OAL_NODEVICE 2000 #define IDS_OAL_NODLL 2001 #define IDS_AVI_CANNOT_CREATE_AVI 2002 @@ -815,34 +819,26 @@ #define ID_OPTIONS_SOUND_PCMINTERPOLATION_CUBIC 40296 #define ID_OPTIONS_SOUND_PCMINTERPOLATION_FIR 40297 #define ID_OPTIONS_SOUND_PCMINTERPOLATION_LIBRESAMPLE 40298 -#define IDD_LINKTAB1 40300 #define IDD_LINKTAB 40301 -#define IDD_LINKTAB2 40302 -#define IDD_LINKTAB3 40303 #define IDD_SERVERWAIT 40304 #define IDC_TAB1 40305 #define IDC_LINK_SINGLE 40306 #define IDC_LINK_TIMEOUT 40307 +#define IDC_LINK_DISCONNECTED 40307 #define IDC_LINK_LAN 40308 #define IDC_LINK2P 40309 -#define IDC_LINKTCP 40310 +#define IDC_LINK_RFU 40309 +#define IDC_LINK_GAMECUBE 40310 #define IDC_SSPEED 40311 -#define IDC_SERVERSTART 40312 #define IDC_SERVERIP 40313 #define IDC_CLINKIP 40314 -#define IDC_SPEEDOFF 40315 -#define IDC_LINKCONNECT 40316 #define ID_OPTIONS_LINK_OPTIONS 40318 #define ID_OPTIONS_LINK_LOG 40319 #define ID_OPTIONS_LINK_WIRELESSADAPTER 40320 #define IDC_LINKTIMEOUT 40321 -#define IDC_CLINKTCP 40322 #define IDC_SERVERWAIT 40323 -#define IDC_LINKUDP 40324 #define IDC_LINK3P 40325 #define IDC_LINK4P 40326 -#define IDC_CLINKUDP 40327 -#define IDC_SPEEDON 40328 #define ID_OPTIONS_EMULATOR_REMOVEINTROSGBA 40331 #define ID_Menu 40332 #define ID_OPTIONS_VIDEO_RENDEROPTIONS_GLANISOTROPIC 40333 diff --git a/src/wx/cmdevents.cpp b/src/wx/cmdevents.cpp index 90fe5896..88b7c84a 100644 --- a/src/wx/cmdevents.cpp +++ b/src/wx/cmdevents.cpp @@ -1159,16 +1159,18 @@ EVT_HANDLER(JoypadAutofireR, "Autofire R (toggle)") EVT_HANDLER_MASK(LanLink, "Start LAN link", CMDEN_LINK_ANY) { #ifndef NO_LINK - if(lanlink.connected) { - // while we could deactivate the command when connected, it is more - // user-friendly to display a message indidcating why - wxLogError(_("LAN link is already active. Disable link mode to disconnect.")); - return; + LinkMode mode = GetLinkMode(); + + if (mode == LINK_CABLE_SOCKET) { + // while we could deactivate the command when connected, it is more + // user-friendly to display a message indidcating why + wxLogError(_("LAN link is already active. Disable link mode to disconnect.")); + return; } - if(rfu_enabled) { - // see above comment - wxLogError(_("RFU is currently only supported in local mode.")); - return; + if (mode == LINK_RFU_IPC || mode == LINK_GAMECUBE_DOLPHIN) { + // see above comment + wxLogError(_("RFU and Joybus are only supported in local mode.")); + return; } wxDialog *dlg = GetXRCDialog("NetLink"); ShowModal(dlg); @@ -2118,33 +2120,38 @@ EVT_HANDLER(JoypadConfigure, "Joypad options...") EVT_HANDLER(LinkConfigure, "Link options...") { #ifndef NO_LINK - bool jb = gba_joybus_enabled; wxString jh = gopts.joybus_host; wxDialog *dlg = GetXRCDialog("LinkConfig"); - if(ShowModal(dlg) != wxID_OK) - return; - update_opts(); - if(jb != gba_joybus_enabled) { - if(gba_joybus_enabled) - JoyBusConnect(); - else - JoyBusShutdown(); - } else if(jh != gopts.joybus_host) { - joybusHostAddr = std::string(gopts.joybus_host.mb_str()); - JoyBusConnect(); - } - if(gba_link_enabled != did_link_init) { - if(gba_link_enabled) { - if((did_link_init = InitLink())) - cmd_enable |= CMDEN_LINK_ANY; - } else { - did_link_init = false; - CloseLink(); - lanlink.active = false; - cmd_enable &= ~CMDEN_LINK_ANY; + if (ShowModal(dlg) != wxID_OK) + return; + + bool valid = SetLinkServerHost(gopts.joybus_host.mb_str()); + if (!valid) { + wxMessageBox(_("You must enter a valid host name"), + _("Host name invalid"), wxICON_ERROR | wxOK); + return; } - enable_menus(); + + update_opts(); + + SetLinkTimeout(gopts.linktimeout); + + LinkMode oldLinkMode = GetLinkMode(); + LinkMode newLinkMode = getOptionsLinkMode(); + bool dolphinHostChanged = jh != gopts.joybus_host; + + if (newLinkMode != oldLinkMode || dolphinHostChanged) { + CloseLink(); + InitLink(newLinkMode); } + + cmd_enable &= ~CMDEN_LINK_ANY; + + if (GetLinkMode() != LINK_DISCONNECTED) { + cmd_enable |= CMDEN_LINK_ANY; + } + + enable_menus(); #endif } diff --git a/src/wx/guiinit.cpp b/src/wx/guiinit.cpp index b61594fe..b0551bb9 100644 --- a/src/wx/guiinit.cpp +++ b/src/wx/guiinit.cpp @@ -50,7 +50,8 @@ static class NetLink_t : public wxEvtHandler public: wxDialog *dlg; int n_players; - NetLink_t() : n_players(2) {} + bool server; + NetLink_t() : n_players(2), server(false) {} wxButton *okb; void ServerOKButton(wxCommandEvent &ev) { @@ -63,151 +64,80 @@ public: // attached to OK, so skip when OK void NetConnect(wxCommandEvent &ev) { + static const int length = 256; if(!dlg->Validate() || !dlg->TransferDataFromWindow()) return; + + if (!server) { + bool valid = SetLinkServerHost(gopts.link_host.mb_str()); + if (!valid) { + wxMessageBox(_("You must enter a valid host name"), + _("Host name invalid"), wxICON_ERROR | wxOK); + return; + } + } + update_opts(); // save fast flag and client host - wxString connmsg, pmsg; + // Close any previous link + CloseLink(); - wxMutex lock; - wxCondition sig(lock); - lock.Lock(); + wxString connmsg; + wxString title; - bool done = false; + SetLinkTimeout(gopts.linktimeout); + EnableSpeedHacks(gopts.lanlink_speed); + EnableLinkServer(server, n_players - 1); - if(lanlink.server) { - lanlink.numslaves = n_players - 1; - class sid_t : public ServerInfoDisplay - { - wxMutex *lock; - wxCondition *sig; - wxString *connmsg, *pmsg; - bool *done; - bool conn[3]; - public: - sid_t(wxMutex *m, wxCondition *c, wxString *cm, wxString *pm, - bool *d) : - lock(m), sig(c), connmsg(cm), pmsg(pm), done(d) {} - void ShowServerIP(const sf::IPAddress &addr) { - wxString addr_s(addr.ToString().c_str(), wxConvLibc); - wxString msg; - msg.Printf(_("Server IP address is: %s\n"), addr_s.c_str()); - connmsg->append(msg); - conn[0] = conn[1] = conn[2] = false; - } - void ShowConnect(int player) { - wxString msg; - conn[player - 1] = true; - lock->Lock(); - pmsg->clear(); - for(int i = 0; i < 3; i++) - if(conn[i]) { - msg.Printf(_("Player %d connected\n"), i + 2); - pmsg->append(msg); - } - sig->Signal(); - lock->Unlock(); - } - void Ping() { - lock->Lock(); - sig->Signal(); - if(*done) - lanlink.terminate = true; - lock->Unlock(); - } - void Connected() { - lock->Lock(); - *done = true; - sig->Signal(); - lock->Unlock(); - } - }; - - sid_t* sid = new sid_t(&lock, &sig, &connmsg, &pmsg, &done); - - if (!ls.Init(sid)) { - wxLogError(_("Error occurred.\nPlease try again.")); - lock.Unlock(); - delete sid; - return; - } - - wxProgressDialog - pdlg(_("Waiting for clients..."), connmsg, - 100, dlg, wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ELAPSED_TIME); - - while(!done) { - if(!pdlg.Pulse(connmsg + pmsg)) - done = true; - sig.Wait(); - } + if (server) { + char host[length]; + GetLinkServerHost(host, length); + + title.Printf(_("Waiting for clients...")); + connmsg.Printf(_("Server IP address is: %s\n"), wxString(host, wxConvLibc).c_str()); } else { - class cid_t : public ClientInfoDisplay - { - wxMutex *lock; - wxCondition *sig; - wxString *connmsg, *pmsg; - bool *done; - public: - cid_t(wxMutex *m, wxCondition *c, wxString *cm, wxString *pm, - bool *d) : - lock(m), sig(c), connmsg(cm), pmsg(pm), done(d) {} - void ConnectStart(const sf::IPAddress &addr) { - wxString addr_s(addr.ToString().c_str(), wxConvLibc); - connmsg->Printf(_("Connecting to %s\n"), addr_s.c_str()); - } - void ShowConnect(int player, int togo) { - wxString msg; - lock->Lock(); - pmsg->Printf(_("Connected as #%d\n"), player); - if(togo) - msg.Printf(_("Waiting for %d players to join"), togo); - else - msg = _("All players joined."); - pmsg->append(msg); - sig->Signal(); - lock->Unlock(); - } - void Ping() { - lock->Lock(); - sig->Signal(); - if(*done) - lanlink.terminate = true; - lock->Unlock(); - } - void Connected() { - lock->Lock(); - *done = true; - sig->Signal(); - lock->Unlock(); - } - }; - - cid_t* cid = new cid_t(&lock, &sig, &connmsg, &pmsg, &done); - - if (!lc.Init(sf::IPAddress(std::string(gopts.link_host.mb_str())), cid)) { - wxLogError(_("Error occurred.\nPlease try again.")); - lock.Unlock(); - delete cid; - return; - } - - wxProgressDialog - pdlg(_("Waiting for connection..."), connmsg, - 100, dlg, wxPD_APP_MODAL|wxPD_CAN_ABORT|wxPD_ELAPSED_TIME); - - while(!done) { - if(!pdlg.Pulse(connmsg + pmsg)) - done = true; - sig.Wait(); - } + title.Printf(_("Waiting for connection...")); + connmsg.Printf(_("Connecting to %s\n"), gopts.link_host.c_str()); } - lock.Unlock(); - if(lanlink.connected) { - pmsg.Replace(wxT("\n"), wxT(" ")); - systemScreenMessage(pmsg); - lanlink.active = true; - ev.Skip(); // all OK + + // Init link + ConnectionState state = InitLink(LINK_CABLE_SOCKET); + + // Display a progress dialog while the connection is establishing + if (state == LINK_NEEDS_UPDATE) { + wxProgressDialog pdlg(title, connmsg, + 100, dlg, wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME); + + while (state == LINK_NEEDS_UPDATE) { + // Ask the core for updates + char message[length]; + state = ConnectLinkUpdate(message, length); + + connmsg = wxString(message, wxConvLibc); + + // Does the user want to abort? + if (!pdlg.Pulse(connmsg)) { + state = LINK_ABORT; + } + } + } + + // The user canceled the connection attempt + if (state == LINK_ABORT) { + CloseLink(); + } + + // Something failed during init + if (state == LINK_ERROR) { + CloseLink(); + wxLogError(_("Error occurred.\nPlease try again.")); + } + + if(GetLinkMode() != LINK_DISCONNECTED) { + connmsg.Replace(wxT("\n"), wxT(" ")); + systemScreenMessage(connmsg); + + ev.Skip(); // all OK } } } net_link_handler; @@ -1438,45 +1368,6 @@ public: } } JoyPadConfigHandler[4]; -#ifndef NO_LINK -// tc validator for IP addresses using SFML for validation instead of wx -class IPHostValidator : public wxValidator -{ - wxString *valp; -public: - IPHostValidator(wxString *v) : wxValidator(), valp(v) {} - IPHostValidator(const IPHostValidator &e) : wxValidator(), valp(e.valp) {} - wxObject *Clone() const { return new IPHostValidator(*this); } - bool Validate(wxWindow *p) { - wxTextCtrl *tc = wxStaticCast(GetWindow(), wxTextCtrl); - if(!tc->IsEnabled()) - return true; - wxString val = tc->GetValue(); - bool isv = true; - if(val.empty()) - isv = false; - else { - sf::IPAddress srv = std::string(val.mb_str()); - isv = srv.IsValid(); - } - if(!isv) - wxMessageBox(_("You must enter a valid host name"), - _("Host name invalid"), wxICON_ERROR|wxOK); - return isv; - } - bool TransferToWindow() { - wxTextCtrl *tc = wxStaticCast(GetWindow(), wxTextCtrl); - tc->SetValue(*valp); - return true; - } - bool TransferFromWindow() { - wxTextCtrl *tc = wxStaticCast(GetWindow(), wxTextCtrl); - *valp = tc->GetValue(); - return true; - } -}; -#endif - // manage fullscreen mode widget // technically, it's more than a validator: it modifies the widget as well class ScreenModeList : public wxValidator @@ -2227,7 +2118,7 @@ bool MainFrame::InitMore(void) // so just set individual flags here cmd_enable = CMDEN_NGDB_ANY | CMDEN_NREC_ANY; update_state_ts(true); - enable_menus(); + // set pointers for checkable menu items // and set initial checked status if(checkable_mi.size()) { @@ -2464,8 +2355,8 @@ bool MainFrame::InitMore(void) #ifndef NO_LINK { net_link_handler.dlg = d; - getrbbe("Server", lanlink.server); - getrbbd("Client", lanlink.server); + getrbbe("Server", net_link_handler.server); + getrbbd("Client", net_link_handler.server); getlab("PlayersLab"); addrber(lab, false); getrbi("Link2P", net_link_handler.n_players, 2); @@ -2478,9 +2369,8 @@ bool MainFrame::InitMore(void) addrber(lab, true); gettc("ServerIP", gopts.link_host); addrber(tc, true); - tc->SetValidator(IPHostValidator(&gopts.link_host)); - getrbbr("SpeedOff", lanlink.speed); - getrbb("SpeedOn", lanlink.speed); + getrbbr("SpeedOff", gopts.lanlink_speed); + getrbb("SpeedOn", gopts.lanlink_speed); wxWindow *okb = d->FindWindow(wxID_OK); if(okb) { // may be gone if style guidlines removed it net_link_handler.okb = wxStaticCast(okb, wxButton); @@ -2847,8 +2737,8 @@ bool MainFrame::InitMore(void) addbier(lab, true); /// Boot ROM - getcbbe("BootRomEn", gopts.gb_use_bios); - getfp("BootRom", gopts.gb_bios); + getcbbe("BootRomEn", gopts.gba_use_bios); + getfp("BootRom", gopts.gba_bios); addbe(fp); getlab("BootRomLab"); addbe(lab); @@ -3072,18 +2962,17 @@ bool MainFrame::InitMore(void) #ifndef NO_LINK LoadXRCDialog("LinkConfig"); { - getcbbe("Joybus", gba_joybus_enabled); + getcbbe("Joybus", gopts.gba_joybus_enabled); getlab("JoybusHostLab"); addbe(lab); gettc("JoybusHost", gopts.joybus_host); - tc->SetValidator(IPHostValidator(&gopts.joybus_host)); addbe(tc); - getcbbe("Link", gba_link_enabled); - getcbb("RFU", rfu_enabled); + getcbbe("Link", gopts.gba_link_enabled); + getcbb("RFU", gopts.rfu_enabled); addbe(cb); getlab("LinkTimeoutLab"); addbe(lab); - getsc("LinkTimeout", linktimeout); + getsc("LinkTimeout", gopts.linktimeout); addbe(sc); } #endif @@ -3205,23 +3094,33 @@ bool MainFrame::InitMore(void) panel->ShowFullScreen(true); #ifndef NO_LINK - if(gba_joybus_enabled) { - bool isv = !gopts.joybus_host.empty(); - if(isv) { - joybusHostAddr = std::string(gopts.joybus_host.mb_str()); - isv = joybusHostAddr.IsValid(); - } - if(!isv) { - wxLogError(_("JoyBus host invalid; disabling")); - gba_joybus_enabled = false; - } else - JoyBusConnect(); - } - if(gba_link_enabled) - if((did_link_init = InitLink())) - cmd_enable |= CMDEN_LINK_ANY; + LinkMode linkMode = getOptionsLinkMode(); + if (linkMode == LINK_GAMECUBE_DOLPHIN) { + bool isv = !gopts.joybus_host.empty(); + if(isv) { + isv = SetLinkServerHost(gopts.joybus_host.mb_str()); + } + + if(!isv) { + wxLogError(_("JoyBus host invalid; disabling")); + gopts.gba_joybus_enabled = false; + } else { + linkMode = LINK_DISCONNECTED; + } + } + + ConnectionState linkState = InitLink(linkMode); + if (linkState != LINK_OK) { + CloseLink(); + } + + if (GetLinkMode() != LINK_DISCONNECTED) + cmd_enable |= CMDEN_LINK_ANY; #endif + + enable_menus(); + panel->SetFrameTitle(); // All OK; activate idle loop diff --git a/src/wx/panel.cpp b/src/wx/panel.cpp index 33f9ae86..98477511 100644 --- a/src/wx/panel.cpp +++ b/src/wx/panel.cpp @@ -279,9 +279,10 @@ void GameArea::LoadGame(const wxString &name) wxString bname = loaded_game.GetFullName(); #ifndef NO_LINK // MakeInstanceFilename doesn't do wxString, so just add slave ID here - if(vbaid) { + int playerId = GetLinkPlayerId(); + if(playerId >= 0) { bname.append(wxT('-')); - bname.append(wxChar(wxT('1') + vbaid)); + bname.append(wxChar(wxT('1') + playerId)); } #endif bname.append(wxT(".sav")); @@ -331,10 +332,11 @@ void GameArea::SetFrameTitle() } else tit = wxT("VisualBoyAdvance-M " VERSION); #ifndef NO_LINK - if(vbaid > 0 || linkid > 0) { - tit.append(_(" player ")); - tit.append(wxChar(wxT('1') + (linkid > 0 ? linkid : vbaid))); - } + int playerId = GetLinkPlayerId(); + if (playerId >= 0) { + tit.append(_(" player ")); + tit.append(wxChar(wxT('1') + playerId)); + } #endif wxGetApp().frame->SetTitle(tit); } @@ -516,10 +518,11 @@ void GameArea::SaveBattery(bool quiet) // MakeInstanceFilename doesn't do wxString, so just add slave ID here wxString bname = game_name(); #ifndef NO_LINK - if(vbaid) { - bname.append(wxT('-')); - bname.append(wxChar(wxT('1') + vbaid)); - } + int playerId = GetLinkPlayerId(); + if (playerId >= 0) { + bname.append(wxT('-')); + bname.append(wxChar(wxT('1') + playerId)); + } #endif bname.append(wxT(".sav")); wxFileName bat(batdir, bname); @@ -836,8 +839,8 @@ void GameArea::OnIdle(wxIdleEvent &event) } emusys->emuMain(emusys->emuCount); #ifndef NO_LINK - if(loaded == IMAGE_GBA && lanlink.connected && linkid && lc.numtransfers == 0) - lc.CheckConn(); + if (loaded == IMAGE_GBA && GetLinkMode() != LINK_DISCONNECTED) + CheckLinkConnection(); #endif } else { was_paused = true; diff --git a/src/wx/wxvbam.cpp b/src/wx/wxvbam.cpp index e4b1b113..33f5a7f0 100644 --- a/src/wx/wxvbam.cpp +++ b/src/wx/wxvbam.cpp @@ -391,12 +391,11 @@ bool wxvbamApp::OnCmdLineParsed(wxCmdLineParser &cl) return true; } -MainFrame::MainFrame() : wxFrame(), did_link_init(false), focused(false), +MainFrame::MainFrame() : wxFrame(), focused(false), paused(false), menus_opened(0), dialog_opened(0) {} MainFrame::~MainFrame() { - if(did_link_init) CloseLink(); } @@ -642,6 +641,18 @@ void MainFrame::StopModal() panel->Resume(); } +LinkMode MainFrame::getOptionsLinkMode() { + if (gopts.gba_joybus_enabled) { + return LINK_GAMECUBE_DOLPHIN; + } else if (gopts.rfu_enabled) { + return LINK_RFU_IPC; + } else if (gopts.gba_link_enabled) { + return LINK_CABLE_IPC; + } + + return LINK_DISCONNECTED; +} + // global event filter // apparently required for win32; just setting accel table still misses // a few keys (e.g. only ctrl-x works for exit, but not esc & ctrl-q; diff --git a/src/wx/wxvbam.h b/src/wx/wxvbam.h index f6404e20..d3477c44 100644 --- a/src/wx/wxvbam.h +++ b/src/wx/wxvbam.h @@ -205,7 +205,6 @@ public: // required for event handling DECLARE_EVENT_TABLE(); private: - bool did_link_init; GameArea *panel; // the various reasons the game might be paused @@ -244,6 +243,8 @@ private: void OnDropFile(wxDropFilesEvent&); // pop up menu in fullscreen mode void OnMenu(wxContextMenuEvent &); + // Returns the link mode to set according to the options + LinkMode getOptionsLinkMode(); #include "cmdhandlers.h" };