diff --git a/src/gba/GBALink.cpp b/src/gba/GBALink.cpp index 9dce96d3..e05f65d8 100644 --- a/src/gba/GBALink.cpp +++ b/src/gba/GBALink.cpp @@ -43,6 +43,9 @@ const char *MakeInstanceFilename(const char *Input) #define _(x) x #endif #define N_(x) x + + + #if (defined __WIN32__ || defined _WIN32) #include #else @@ -51,6 +54,8 @@ const char *MakeInstanceFilename(const char *Input) #include #include #include + + #define ReleaseSemaphore(sem, nrel, orel) do { \ for(int i = 0; i < nrel; i++) \ sem_post(sem); \ @@ -165,6 +170,48 @@ int WaitForSingleObject(sem_t *s, int t) #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, @@ -237,12 +284,12 @@ public: void CheckConn(void); }; -static LinkMode gba_link_mode = LINK_DISCONNECTED; +static const LinkDriver *linkDriver = NULL; static ConnectionState gba_connection_state = LINK_OK; LinkMode GetLinkMode() { - if (gba_connection_state == LINK_OK) - return gba_link_mode; + if (linkDriver && gba_connection_state == LINK_OK) + return linkDriver->mode; else return LINK_DISCONNECTED; } @@ -316,18 +363,269 @@ static const int trtimeend[3][4] = { static int GetSIOMode(u16, u16); -static 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 (GetLinkMode() == LINK_RFU_IPC) { - 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; @@ -341,23 +639,7 @@ void StartLink(u16 value) value |= READ16LE(&ioMem[COMM_SIOCNT]) & 4; } if (start) { - if (GetLinkMode() == LINK_CABLE_SOCKET) - { - 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 if (GetLinkMode() == LINK_CABLE_IPC && linkmem->numgbas > 1) + if (linkmem->numgbas > 1) { // find first active attached GBA // doing this first reduces the potential @@ -428,6 +710,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); @@ -473,7 +817,7 @@ static void JoyBusShutdown() dol = NULL; } -static void JoyBusUpdate() +static void JoyBusUpdate(int ticks) { static int lastjoybusupdate = 0; @@ -533,101 +877,8 @@ static void JoyBusUpdate() static void ReInitLink(); -void LinkUpdate(int ticks) +static void UpdateCableIPC(int ticks) { - // this actually gets called every single instruction, so keep default - // path as short as possible - - linktime += ticks; - - if (GetLinkMode() == LINK_RFU_IPC) - { - 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 (GetLinkMode() == LINK_GAMECUBE_DOLPHIN) - { - JoyBusUpdate(); - return; - } - - if (GetLinkMode() == LINK_CABLE_SOCKET) - { - 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 @@ -766,8 +1017,102 @@ void LinkUpdate(int ticks) UPDATE_REG(0x202, IF); } } +} - return; +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) @@ -788,268 +1133,6 @@ inline static 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) -static 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; - } -} - static ConnectionState InitIPC() { linkid = 0; @@ -1165,6 +1248,41 @@ static ConnectionState InitIPC() { return LINK_OK; } +static ConnectionState InitSocket() { + linkid = 0; + + for(int i = 0; i < 4; i++) + linkdata[i] = 0xffff; + + 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 @@ -1177,49 +1295,23 @@ ConnectionState InitLink(LinkMode mode) return LINK_ERROR; } - gba_connection_state = LINK_OK; - - if (mode == LINK_GAMECUBE_DOLPHIN) { - gba_connection_state = JoyBusConnect(); - } else if (mode == LINK_CABLE_IPC || mode == LINK_RFU_IPC) { - gba_connection_state = InitIPC(); - } else if (mode == LINK_CABLE_SOCKET) { - linkid = 0; - - for(int i = 0; i < 4; i++) - linkdata[i] = 0xffff; - - 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 - gba_connection_state = LINK_ERROR; // FIXME: error code? - else - gba_connection_state = LINK_NEEDS_UPDATE; - } else { - lc.serverport = IP_LINK_PORT; - - if (!lc.serveraddr.IsValid()) { - gba_connection_state = 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) - gba_connection_state = LINK_ERROR; - else - gba_connection_state = LINK_NEEDS_UPDATE; - } + // Find the link driver + linkDriver = NULL; + for (uint i = 0; i < sizeof(linkDrivers) / sizeof(linkDrivers[0]); i++) { + if (linkDrivers[i].mode == mode) { + linkDriver = &linkDrivers[i]; + break; } } - // Save the link mode - gba_link_mode = mode; + 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(); } @@ -1227,18 +1319,10 @@ ConnectionState InitLink(LinkMode mode) return gba_connection_state; } -ConnectionState ConnectLinkUpdate(char * const message, size_t size) -{ - message[0] = '\0'; +static ConnectionState ConnectUpdateSocket(char * const message, size_t size) { + ConnectionState newState = LINK_NEEDS_UPDATE; - if (gba_connection_state != LINK_NEEDS_UPDATE) { - gba_connection_state = LINK_ERROR; - snprintf(message, size, N_("Link connection does not need updates.")); - - return LINK_ERROR; - } - - if (gba_link_mode == LINK_CABLE_SOCKET && lanlink.server) { + if (lanlink.server) { sf::Selector fdset; fdset.Add(lanlink.tcpsocket); @@ -1252,7 +1336,7 @@ ConnectionState ConnectLinkUpdate(char * const message, size_t size) ls.tcpsocket[j].Close(); snprintf(message, size, N_("Network error.")); - gba_connection_state = LINK_ERROR; + newState = LINK_ERROR; } else { sf::Packet packet; packet << static_cast(nextSlave) @@ -1275,16 +1359,16 @@ ConnectionState ConnectLinkUpdate(char * const message, size_t size) } snprintf(message, size, N_("All players connected")); - gba_connection_state = LINK_OK; + newState = LINK_OK; } - } else if (gba_link_mode == LINK_CABLE_SOCKET && !lanlink.server) { + } 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.")); - gba_connection_state = LINK_ERROR; + newState = LINK_ERROR; } else if (status == sf::Socket::Done) { if (linkid == 0) { @@ -1303,7 +1387,7 @@ ConnectionState ConnectLinkUpdate(char * const message, size_t size) packet >> gameReady; if (packet && gameReady) { - gba_connection_state = LINK_OK; + newState = LINK_OK; snprintf(message, size, N_("All players joined.")); } } @@ -1314,6 +1398,22 @@ ConnectionState ConnectLinkUpdate(char * const message, size_t size) } } + 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; } @@ -1341,7 +1441,7 @@ void GetLinkServerHost(char * const host, size_t size) { host[0] = '\0'; - if (gba_link_mode == LINK_GAMECUBE_DOLPHIN) + 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); @@ -1378,79 +1478,76 @@ static void ReInitLink() systemScreenMessage(_("Lost link; reconnected")); } +static void CloseIPC() { + int f = linkmem->linkflags; + f &= ~(1 << linkid); + if(f & 0xf) { + linkmem->linkflags = f; + int n = linkmem->numgbas; + for(int i = 0; i < n; i--) + if(f <= (1 << (i + 1)) - 1) { + linkmem->numgbas = i + 1; + break; + } + } + + for(i=0;i<4;i++){ + if(linksync[i]!=NULL){ +#if (defined __WIN32__ || defined _WIN32) + ReleaseSemaphore(linksync[i], 1, NULL); + CloseHandle(linksync[i]); +#else + sem_close(linksync[i]); + if(!(f & 0xf)) { + linkevent[sizeof(linkevent)-2]=(char)i+'1'; + sem_unlink(linkevent); + } +#endif + } + } +#if (defined __WIN32__ || defined _WIN32) + CloseHandle(mmf); + UnmapViewOfFile(linkmem); + + // FIXME: move to caller + // (but there are no callers, so why bother?) + //regSetDwordValue("LAN", lanlink.active); +#else + if(!(f & 0xf)) + shm_unlink("/" LOCAL_LINK_NAME); + 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 (gba_link_mode == LINK_DISCONNECTED) { - return; // Nothing to do - } - - if (gba_link_mode == LINK_GAMECUBE_DOLPHIN) { - JoyBusShutdown(); - } - - if (gba_link_mode == LINK_CABLE_SOCKET) { - 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(); + if (!linkDriver) { + return; // Nothing to do } - if (gba_link_mode == LINK_CABLE_IPC || gba_link_mode == LINK_RFU_IPC) { - int f = linkmem->linkflags; - f &= ~(1 << linkid); - if(f & 0xf) { - linkmem->linkflags = f; - int n = linkmem->numgbas; - for(int i = 0; i < n; i--) - if(f <= (1 << (i + 1)) - 1) { - linkmem->numgbas = i + 1; - break; - } - } - - for(i=0;i<4;i++){ - if(linksync[i]!=NULL){ -#if (defined __WIN32__ || defined _WIN32) - ReleaseSemaphore(linksync[i], 1, NULL); - CloseHandle(linksync[i]); -#else - sem_close(linksync[i]); - if(!(f & 0xf)) { - linkevent[sizeof(linkevent)-2]=(char)i+'1'; - sem_unlink(linkevent); - } -#endif - } - } -#if (defined __WIN32__ || defined _WIN32) - CloseHandle(mmf); - UnmapViewOfFile(linkmem); - - // FIXME: move to caller - // (but there are no callers, so why bother?) - //regSetDwordValue("LAN", lanlink.active); -#else - if(!(f & 0xf)) - shm_unlink("/" LOCAL_LINK_NAME); - munmap(linkmem, sizeof(LINKDATA)); - close(mmf); -#endif - } - - gba_link_mode = LINK_DISCONNECTED; + linkDriver->close(); + linkDriver = NULL; return; }