From 14d94c32f6e8123279c89245a7335ad3fb6556bc Mon Sep 17 00:00:00 2001 From: skidau Date: Tue, 14 Apr 2015 10:21:27 +0000 Subject: [PATCH] Preliminary implementation of the GBA wireless adapter using TCP (based on the IPC version). --- src/gba/GBALink.cpp | 1418 ++++++++++++++++++++++++++++++++++--- src/gba/GBALink.h | 4 +- src/win32/LinkOptions.cpp | 7 +- 3 files changed, 1309 insertions(+), 120 deletions(-) diff --git a/src/gba/GBALink.cpp b/src/gba/GBALink.cpp index e64b6eb2..24bf00c9 100644 --- a/src/gba/GBALink.cpp +++ b/src/gba/GBALink.cpp @@ -40,6 +40,17 @@ enum RECEIVING = true }; +enum siocnt_lo_32bit +{ + SIO_INT_CLOCK = 0x0001, + SIO_INT_CLOCK_SEL_2MHZ = 0x0002, + SIO_TRANS_FLAG_RECV_ENABLE = 0x0004, + SIO_TRANS_FLAG_SEND_DISABLE = 0x0008, + SIO_TRANS_START = 0x0080, + SIO_TRANS_32BIT = 0x1000, + SIO_IRQ_ENABLE = 0x4000 +}; + // The usual min/max functions for built-in types. // // template T min( T x, T y ) { return x < y ? x : y; } @@ -228,7 +239,10 @@ static ConnectionState JoyBusConnect(); static void JoyBusUpdate(int ticks); static void JoyBusShutdown(); -#if (defined __WIN32__ || defined _WIN32) +static ConnectionState ConnectUpdateRFUSocket(char * const message, size_t size); +static void StartRFUSocket(u16 siocnt); +bool LinkRFUUpdateSocket(); +static void UpdateRFUSocket(int ticks); #define RFU_INIT 0 #define RFU_COMM 1 @@ -244,20 +258,17 @@ typedef struct { u8 trgbas; u8 linkflags; - u8 rfu_recvcmd[5]; //last received command u8 rfu_proto[5]; // 0=UDP-like, 1=TCP-like protocols to see whether the data important or not (may or may not be received successfully by the other side) u16 rfu_qid[5]; s32 rfu_q[5]; u32 rfu_signal[5]; - u8 rfu_request[5]; //request to join + u8 rfu_is_host[5]; //request to join //u8 rfu_joined[5]; //bool //currenlty joined u16 rfu_reqid[5]; //id to join u16 rfu_clientidx[5]; //only used by clients s32 rfu_linktime[5]; - s32 rfu_latency[5]; - u32 rfu_bdata[5][7]; //for 0x16/0x1d/0x1e? + u32 rfu_broadcastdata[5][7]; //for 0x16/0x1d/0x1e? u32 rfu_gdata[5]; //for 0x17/0x19?/0x1e? - u32 rfu_data[5][255]; //[32]; //for 0x24-0x26 s32 rfu_state[5]; //0=none, 1=waiting for ACK u8 rfu_listfront[5]; u8 rfu_listback[5]; @@ -269,9 +280,43 @@ typedef struct { u32 rfu_timelist[5][256];*/ } LINKDATA; + +class RFUServer{ + int numbytes; + sf::SocketSelector fdset; + int counter; + int done; + u8 current_host; +public: + sf::TcpSocket tcpsocket[5]; + sf::IpAddress udpaddr[5]; + RFUServer(void); + sf::Packet& Serialize(sf::Packet& packet, int slave); + void DeSerialize(sf::Packet packet, int slave); + void Send(void); + void Recv(void); + +}; + +class RFUClient{ + sf::SocketSelector fdset; + int numbytes; +public: + sf::IpAddress serveraddr; + unsigned short serverport; + bool transferring; + RFUClient(void); + void Send(void); + void Recv(void); + sf::Packet& Serialize(sf::Packet& packet); + void DeSerialize(sf::Packet packet); + void CheckConn(void); +}; + // RFU crap (except for numtransfers note...should probably check that out) static LINKDATA *linkmem = NULL; -static u8 rfu_cmd, rfu_qsend, rfu_qrecv; +static LINKDATA rfu_data; +static u8 rfu_cmd, rfu_qsend, rfu_qrecv_broadcast_data_len; 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? @@ -279,17 +324,23 @@ static int rfu_transfer_end; // in local comm, setting this keeps slaves from trying to communicate even // when master isn't static u16 numtransfers = 0; -static u32 rfu_masterdata[255]; +// time until next broadcast +static int rfu_last_broadcast_time; + +// timer to sync data +static int rfu_last_host_send_time; + +static u32 rfu_masterdata[255]; bool rfu_enabled = false; bool rfu_initialized = false; bool rfu_waiting = false; -u8 rfu_qsend2, rfu_cmd2, rfu_lastcmd, rfu_lastcmd2, rfu_lastcmd3; +u8 rfu_qsend2, rfu_cmd2, rfu_lastcmd, rfu_lastcmd2; u16 rfu_id, rfu_idx; static int gbaid = 0; static int gbaidx = 0; -bool rfu_ishost, rfu_isfirst, rfu_cansend; -DWORD rfu_lasttime; +bool rfu_ishost, rfu_cansend; +int rfu_lasttime; u32 rfu_buf; u16 PrevVAL = 0; u32 PrevCOM = 0, PrevDAT = 0; @@ -297,6 +348,11 @@ u8 rfu_numclients = 0; u8 rfu_curclient = 0; u32 rfu_clientlist[5]; +static RFUServer rfu_server; +static RFUClient rfu_client; + +#if (defined __WIN32__ || defined _WIN32) + static ConnectionState InitIPC(); static void StartCableIPC(u16 siocnt); static void ReconnectCableIPC(); @@ -334,12 +390,13 @@ static sf::IpAddress joybusHostAddr = sf::IpAddress::LocalHost; static const LinkDriver linkDrivers[] = { #if (defined __WIN32__ || defined _WIN32) - { LINK_CABLE_IPC, InitIPC, NULL, StartCableIPC, UpdateCableIPC, CloseIPC, false }, - { LINK_RFU_IPC, InitIPC, NULL, StartRFU, UpdateRFUIPC, CloseIPC, false }, - { LINK_GAMEBOY, InitIPC, NULL, NULL, NULL, CloseIPC, false }, + { LINK_CABLE_IPC, InitIPC, NULL, StartCableIPC, UpdateCableIPC, CloseIPC, false }, + { LINK_RFU_IPC, InitIPC, NULL, StartRFU, UpdateRFUIPC, CloseIPC, false }, + { LINK_GAMEBOY, InitIPC, NULL, NULL, NULL, CloseIPC, false }, #endif - { LINK_CABLE_SOCKET, InitSocket, ConnectUpdateSocket, StartCableSocket, UpdateCableSocket, CloseSocket, true }, - { LINK_GAMECUBE_DOLPHIN, JoyBusConnect, NULL, NULL, JoyBusUpdate, JoyBusShutdown, false } + { LINK_CABLE_SOCKET, InitSocket, ConnectUpdateSocket, StartCableSocket, UpdateCableSocket, CloseSocket, true }, + { LINK_RFU_SOCKET, InitSocket, ConnectUpdateRFUSocket, StartRFUSocket, UpdateRFUSocket, CloseSocket, true }, + { LINK_GAMECUBE_DOLPHIN, JoyBusConnect, NULL, NULL, JoyBusUpdate, JoyBusShutdown, false } }; enum @@ -360,7 +417,7 @@ typedef struct { bool speed; //speedhack } LANLINKDATA; -class lserver{ +class CableServer{ int numbytes; sf::SocketSelector fdset; //timeval udptimeout; @@ -374,12 +431,12 @@ class lserver{ public: sf::TcpSocket tcpsocket[4]; sf::IpAddress udpaddr[4]; - lserver(void); + CableServer(void); void Send(void); void Recv(void); }; -class lclient{ +class CableClient{ sf::SocketSelector fdset; char inbuffer[256], outbuffer[256]; s32 *intinbuffer; @@ -391,7 +448,7 @@ public: sf::IpAddress serveraddr; unsigned short serverport; bool transferring; - lclient(void); + CableClient(void); void Send(void); void Recv(void); void CheckConn(void); @@ -400,9 +457,9 @@ public: static int i, j; static int linktimeout = 1000; static LANLINKDATA lanlink; -static u16 linkdata[4]; -static lserver ls; -static lclient lc; +static u16 cable_data[4]; +static CableServer ls; +static CableClient lc; // time to end of single GBA's transfer, in 16.78 MHz clock ticks // first index is GBA # @@ -527,7 +584,7 @@ void BootLink(int m_type, const char *hostAddr, int timeout, bool m_hacks, int m LinkMode mode = (LinkMode)m_type; - if (mode == LINK_DISCONNECTED || mode == LINK_CABLE_SOCKET) { + if (mode == LINK_DISCONNECTED || mode == LINK_CABLE_SOCKET || mode == LINK_RFU_SOCKET) { return; } @@ -693,17 +750,17 @@ void CloseLink(void) { } // Server -lserver::lserver(void) { +CableServer::CableServer(void) { intinbuffer = (s32*)inbuffer; u16inbuffer = (u16*)inbuffer; intoutbuffer = (s32*)outbuffer; u16outbuffer = (u16*)outbuffer; } -void lserver::Send(void) { +void CableServer::Send(void) { if(lanlink.type==0) { // TCP outbuffer[1] = tspeed; - WRITE16LE(&u16outbuffer[1], linkdata[0]); + WRITE16LE(&u16outbuffer[1], cable_data[0]); WRITE32LE(&intoutbuffer[1], transfer_start_time_from_master); if(lanlink.numslaves==1) { if(lanlink.type==0) { @@ -712,22 +769,22 @@ void lserver::Send(void) { } } else if(lanlink.numslaves==2) { - WRITE16LE(&u16outbuffer[4], linkdata[2]); + WRITE16LE(&u16outbuffer[4], cable_data[2]); if(lanlink.type==0) { outbuffer[0] = 10; tcpsocket[1].send(outbuffer, 10); - WRITE16LE(&u16outbuffer[4], linkdata[1]); + WRITE16LE(&u16outbuffer[4], cable_data[1]); tcpsocket[2].send(outbuffer, 10); } } else { if(lanlink.type==0) { outbuffer[0] = 12; - WRITE16LE(&u16outbuffer[4], linkdata[2]); - WRITE16LE(&u16outbuffer[5], linkdata[3]); + WRITE16LE(&u16outbuffer[4], cable_data[2]); + WRITE16LE(&u16outbuffer[5], cable_data[3]); tcpsocket[1].send(outbuffer, 12); - WRITE16LE(&u16outbuffer[4], linkdata[1]); + WRITE16LE(&u16outbuffer[4], cable_data[1]); tcpsocket[2].send(outbuffer, 12); - WRITE16LE(&u16outbuffer[5], linkdata[2]); + WRITE16LE(&u16outbuffer[5], cable_data[2]); tcpsocket[3].send(outbuffer, 12); } } @@ -736,7 +793,7 @@ void lserver::Send(void) { } // Receive data from all slaves to master -void lserver::Recv(void) { +void CableServer::Recv(void) { int numbytes; if(lanlink.type==0) { // TCP fdset.clear(); @@ -772,14 +829,14 @@ void lserver::Recv(void) { CloseLink(); return; } - linkdata[i+1] = READ16LE(&u16inbuffer[1]); + cable_data[i+1] = READ16LE(&u16inbuffer[1]); } } return; } // Client -lclient::lclient(void) { +CableClient::CableClient(void) { intinbuffer = (s32*)inbuffer; u16inbuffer = (u16*)inbuffer; intoutbuffer = (s32*)outbuffer; @@ -788,7 +845,7 @@ lclient::lclient(void) { return; } -void lclient::CheckConn(void) { +void CableClient::CheckConn(void) { size_t nr; lanlink.tcpsocket.receive(inbuffer, 1, nr); numbytes = nr; @@ -806,18 +863,18 @@ void lclient::CheckConn(void) { } transferring = true; transfer_start_time_from_master = 0; - linkdata[0] = READ16LE(&u16inbuffer[1]); + cable_data[0] = READ16LE(&u16inbuffer[1]); tspeed = inbuffer[1] & 3; for(i=1, numbytes=4;i<=lanlink.numslaves;i++) if(i!=linkid) { - linkdata[i] = READ16LE(&u16inbuffer[numbytes]); + cable_data[i] = READ16LE(&u16inbuffer[numbytes]); numbytes++; } } return; } -void lclient::Recv(void) { +void CableClient::Recv(void) { fdset.clear(); // old code used socket # instead of mask again fdset.add(lanlink.tcpsocket); @@ -842,20 +899,20 @@ void lclient::Recv(void) { return; } tspeed = inbuffer[1] & 3; - linkdata[0] = READ16LE(&u16inbuffer[1]); + cable_data[0] = READ16LE(&u16inbuffer[1]); transfer_start_time_from_master = (s32)READ32LE(&intinbuffer[1]); for (i = 1, numbytes = 4; i= transfer_start_time_from_master) { - linkdata[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); + cable_data[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); lc.Send(); - - UPDATE_REG(COMM_SIODATA32_L, linkdata[0]); + UPDATE_REG(COMM_SIODATA32_L, cable_data[0]); UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) | 0x80); transfer_direction = RECEIVING; linktime = 0; @@ -1063,10 +1119,9 @@ static void UpdateCableSocket(int ticks) { ls.Recv(); // Receive data from all of the slaves } - - UPDATE_REG(COMM_SIOMULTI1, linkdata[1]); - UPDATE_REG(COMM_SIOMULTI2, linkdata[2]); - UPDATE_REG(COMM_SIOMULTI3, linkdata[3]); + UPDATE_REG(COMM_SIOMULTI1, cable_data[1]); + UPDATE_REG(COMM_SIOMULTI2, cable_data[2]); + UPDATE_REG(COMM_SIOMULTI3, cable_data[3]); } } @@ -1244,6 +1299,1144 @@ static void JoyBusShutdown() dol = NULL; } +#define MAX_CLIENTS lanlink.numslaves + 1 + +// Server +RFUServer::RFUServer(void) { + for (int j = 0; j < 5; j++) + rfu_data.rfu_signal[j] = 0; +} + +sf::Packet& RFUServer::Serialize(sf::Packet& packet, int slave) +{ + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (i != slave) + { + packet << (i == current_host); + packet << rfu_data.rfu_reqid[i]; + if (i == current_host) + { + for (int j = 0; j < 7; j++) + packet << rfu_data.rfu_broadcastdata[i][j]; + } + } + + if (i == slave) + { + packet << rfu_data.rfu_clientidx[i]; + packet << rfu_data.rfu_is_host[i]; + packet << rfu_data.rfu_listback[i]; + + if (rfu_data.rfu_listback[i] > 0) + log("num_data_packets from %d to %d = %d\n", linkid, i, rfu_data.rfu_listback[i]); + + for (int j = 0; j <= rfu_data.rfu_listback[i]; j++) + { + packet << rfu_data.rfu_datalist[i][j & 0xff].len; + + for (int k = 0; k < rfu_data.rfu_datalist[i][j & 0xff].len; k++) + packet << rfu_data.rfu_datalist[i][j & 0xff].data[k]; + + packet << rfu_data.rfu_datalist[i][j & 0xff].gbaid; + } + } + } + + packet << linktime; // Synchronise clocks by setting slave clock to master clock + return packet; +} + +void RFUServer::DeSerialize(sf::Packet packet, int slave) +{ + bool slave_is_host = false; + packet >> slave_is_host; + packet >> rfu_data.rfu_reqid[slave]; + if (slave_is_host) + { + current_host = slave; + for (int j = 0; j < 7; j++) + packet >> rfu_data.rfu_broadcastdata[slave][j]; + } + + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (i != slave) + { + u8 num_data_sent = 0; + packet >> rfu_data.rfu_clientidx[i]; + packet >> rfu_data.rfu_is_host[i]; + packet >> num_data_sent; + + for (int j = rfu_data.rfu_listback[i]; j <= (rfu_data.rfu_listback[i] + num_data_sent); j++) + { + packet >> rfu_data.rfu_datalist[i][j & 0xff].len; + + for (int k = 0; k < rfu_data.rfu_datalist[i][j & 0xff].len; k++) + packet >> rfu_data.rfu_datalist[i][j & 0xff].data[k]; + + packet >> rfu_data.rfu_datalist[i][j & 0xff].gbaid; + } + + rfu_data.rfu_listback[i] = (rfu_data.rfu_listback[i] + num_data_sent) & 0xff; + } + } +} + +void RFUServer::Send(void) { + if (lanlink.type == 0) { // TCP + sf::Packet packet; + if (lanlink.numslaves == 1) { + if (lanlink.type == 0) { + tcpsocket[1].send(Serialize(packet, 1)); + } + } + else if (lanlink.numslaves == 2) { + if (lanlink.type == 0) { + tcpsocket[1].send(Serialize(packet, 1)); + tcpsocket[2].send(Serialize(packet, 2)); + } + } + else { + if (lanlink.type == 0) { + tcpsocket[1].send(Serialize(packet, 1)); + tcpsocket[2].send(Serialize(packet, 2)); + tcpsocket[3].send(Serialize(packet, 3)); + } + } + } +} + +// Receive data from all slaves to master +void RFUServer::Recv(void) { + //int numbytes; + if (lanlink.type == 0) { // TCP + fdset.clear(); + + for (i = 0; i < lanlink.numslaves; i++) + fdset.add(tcpsocket[i + 1]); + + //bool all_ready = false; + //while (!all_ready) + //{ + // fdset.wait(sf::milliseconds(1)); + // int count = 0; + // for (int sl = 0; sl < lanlink.numslaves; sl++) + // { + // if (fdset.isReady(tcpsocket[sl + 1])) + // count++; + // } + // if (count == lanlink.numslaves) + // all_ready = true; + //} + + for (i = 0; i < lanlink.numslaves; i++) { + sf::Packet packet; + tcpsocket[i + 1].setBlocking(false); + sf::Socket::Status status = tcpsocket[i + 1].receive(packet); + if (status == sf::Socket::Disconnected) { + char message[30]; + sprintf(message, _("Player %d disconnected."), i + 1); + systemScreenMessage(message); + //tcpsocket[i + 1].disconnect(); + //CloseLink(); + //return; + } + DeSerialize(packet, i + 1); + } + } +} + +// Client +RFUClient::RFUClient(void) { + transferring = false; + + for (int j = 0; j < 5; j++) + rfu_data.rfu_signal[j] = 0; +} + +sf::Packet& RFUClient::Serialize(sf::Packet& packet) +{ + packet << rfu_ishost; + packet << rfu_data.rfu_reqid[linkid]; + if (rfu_ishost) + { + for (int j = 0; j < 7; j++) + packet << rfu_data.rfu_broadcastdata[linkid][j]; + } + + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (i != linkid) + { + packet << rfu_data.rfu_clientidx[i]; + packet << rfu_data.rfu_is_host[i]; + packet << rfu_data.rfu_listback[i]; + + if (rfu_data.rfu_listback[i] > 0) + log("num_data_packets from %d to %d = %d\n", linkid, i, rfu_data.rfu_listback[i]); + + for (int j = 0; j <= rfu_data.rfu_listback[i]; j++) + { + packet << rfu_data.rfu_datalist[i][j].len; + + for (int k = 0; k < rfu_data.rfu_datalist[i][j].len; k++) + packet << rfu_data.rfu_datalist[i][j].data[k]; + + packet << rfu_data.rfu_datalist[i][j].gbaid; + } + } + } + return packet; +} + +void RFUClient::DeSerialize(sf::Packet packet) +{ + bool is_current_host = false; + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (i != linkid) + { + packet >> is_current_host; + packet >> rfu_data.rfu_reqid[i]; + if (is_current_host) + { + for (int j = 0; j < 7; j++) + packet >> rfu_data.rfu_broadcastdata[i][j]; + } + } + + if (i == linkid) + { + u8 num_data_sent = 0; + packet >> rfu_data.rfu_clientidx[i]; + packet >> rfu_data.rfu_is_host[i]; + packet >> num_data_sent; + + for (int j = rfu_data.rfu_listback[i]; j <= (rfu_data.rfu_listback[i] + num_data_sent); j++) + { + packet >> rfu_data.rfu_datalist[i][j & 0xff].len; + + for (int k = 0; k < rfu_data.rfu_datalist[i][j & 0xff].len; k++) + packet >> rfu_data.rfu_datalist[i][j & 0xff].data[k]; + + packet >> rfu_data.rfu_datalist[i][j & 0xff].gbaid; + } + + rfu_data.rfu_listback[i] = (rfu_data.rfu_listback[i] + num_data_sent) & 0xff; + } + } + + packet >> linktime; // Synchronise clocks by setting slave clock to master clock +} + +void RFUClient::Send() { + sf::Packet packet; + lanlink.tcpsocket.send(Serialize(packet)); +} + + +void RFUClient::Recv(void) { + if (rfu_data.numgbas < 2) + return; + + fdset.clear(); + // old code used socket # instead of mask again + lanlink.tcpsocket.setBlocking(false); + fdset.add(lanlink.tcpsocket); + if (fdset.wait(sf::milliseconds(166)) == 0) + { + systemScreenMessage(_("Server timed out.")); + //transferring = false; + //return; + } + size_t nr; + sf::Packet packet; + sf::Socket::Status status = lanlink.tcpsocket.receive(packet); + if (status == sf::Socket::Disconnected) { + systemScreenMessage(_("Server disconnected.")); + CloseLink(); + return; + } + DeSerialize(packet); +} + +static ConnectionState ConnectUpdateRFUSocket(char * const message, size_t size) { + ConnectionState newState = LINK_NEEDS_UPDATE; + + if (lanlink.server) { + sf::SocketSelector fdset; + fdset.add(lanlink.tcplistener); + + if (fdset.wait(sf::milliseconds(150))) { + int nextSlave = lanlink.connectedSlaves + 1; + + sf::Socket::Status st = lanlink.tcplistener.accept(rfu_server.tcpsocket[nextSlave]); + + if (st == sf::Socket::Error) { + for (int j = 1; j < nextSlave; j++) + rfu_server.tcpsocket[j].disconnect(); + + snprintf(message, size, N_("Network error.")); + newState = LINK_ERROR; + } + else { + sf::Packet packet; + packet << static_cast(nextSlave) + << static_cast(lanlink.numslaves); + + rfu_server.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; + + rfu_server.tcpsocket[i].send(packet); + rfu_server.tcpsocket[i].setBlocking(false); + } + + snprintf(message, size, N_("All players connected")); + newState = LINK_OK; + } + } + else { + + sf::Packet packet; + lanlink.tcpsocket.setBlocking(false); + 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::SocketSelector fdset; + fdset.add(lanlink.tcpsocket); + fdset.wait(sf::milliseconds(150)); + } + } + + rfu_data.numgbas = lanlink.numslaves + 1; + log("num gbas: %d\n", rfu_data.numgbas); + + return newState; +} + +// The GBA wireless RFU (see adapter3.txt) +static void StartRFUSocket(u16 value) +{ + int siomode = GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT])); + + if (value) + rfu_enabled = (siomode == NORMAL32); + + if (((READ16LE(&ioMem[COMM_SIOCNT]) & 0x5080) == SIO_TRANS_32BIT) && ((value & 0x5080) == (SIO_TRANS_32BIT | SIO_IRQ_ENABLE | SIO_TRANS_START))) { //RFU Reset, may also occur before cable link started + rfu_data.rfu_listfront[linkid] = 0; + rfu_data.rfu_listback[linkid] = 0; + } + + if (!rfu_enabled) + { + if ((value & 0x5080) == (SIO_TRANS_32BIT | SIO_IRQ_ENABLE | SIO_TRANS_START)) { //0x5083 //game tried to send wireless command but w/o the adapter + if (READ16LE(&ioMem[COMM_SIOCNT]) & SIO_IRQ_ENABLE) //IRQ Enable + { + IF |= 0x80; //Serial Communication + UPDATE_REG(0x202, IF); //Interrupt Request Flags / IRQ Acknowledge + } + value &= ~SIO_TRANS_START; //Start bit.7 reset //may cause the game to retry sending again + value |= SIO_TRANS_FLAG_SEND_DISABLE; //SO bit.3 set automatically upon transfer completion + transfer_direction = SENDING; + } + return; + } + + static bool logstartd; + u32 CurCOM = 0, CurDAT = 0; + bool rfulogd = (READ16LE(&ioMem[COMM_SIOCNT]) != value); + + switch (GetSIOMode(value, READ16LE(&ioMem[COMM_RCNT]))) { + case NORMAL8: + rfu_polarity = 0; + UPDATE_REG(COMM_SIOCNT, value); + return; + break; + case NORMAL32: + //don't do anything if previous cmd aren't sent yet, may fix Boktai2 Not Detecting wireless adapter + //if (transfer_direction == RECEIVING) + //{ + // UPDATE_REG(COMM_SIOCNT, value); + // return; + //} + + //Moving this to the bottom might prevent Mario Golf Adv from Occasionally Not Detecting wireless adapter + if (value & SIO_TRANS_FLAG_SEND_DISABLE) //Transfer Enable Flag Send (SO.bit.3, 1=Disable Transfer/Not Ready) + value &= ~SIO_TRANS_FLAG_RECV_ENABLE; //Transfer enable flag receive (0=Enable Transfer/Ready, SI.bit.2=SO.bit.3 of otherside) // A kind of acknowledge procedure + else //(SO.Bit.3, 0=Enable Transfer/Ready) + value |= SIO_TRANS_FLAG_RECV_ENABLE; //SI.bit.2=1 (otherside is Not Ready) + + if ((value & (SIO_INT_CLOCK | SIO_TRANS_FLAG_RECV_ENABLE)) == SIO_INT_CLOCK) + value |= SIO_INT_CLOCK_SEL_2MHZ; //wireless always use 2Mhz speed right? this will fix MarioGolfAdv Not Detecting wireless + + if (value & SIO_TRANS_START) //start/busy bit + { + if ((value & (SIO_INT_CLOCK | SIO_INT_CLOCK_SEL_2MHZ)) == SIO_INT_CLOCK) + rfu_transfer_end = 2048; + else + rfu_transfer_end = 256; + + u16 siodata_h = 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 + rfu_initialized = true; + value &= ~SIO_TRANS_FLAG_RECV_ENABLE; //0xff7b; //Bit.2 need to be 0 to indicate a finished initialization to fix MarioGolfAdv from occasionally Not Detecting wireless adapter (prevent it from sending 0x7FFE8001 comm)? + rfu_polarity = 0; //not needed? + } + rfu_buf = (READ16LE(&ioMem[COMM_SIODATA32_L]) << 16) | siodata_h; + break; + case RFU_COMM: + CurCOM = READ32LE(&ioMem[COMM_SIODATA32_L]); + if (siodata_h == 0x9966) //initialize cmd + { + u8 tmpcmd = CurCOM; + if (tmpcmd != 0x10 && tmpcmd != 0x11 && tmpcmd != 0x13 && tmpcmd != 0x14 && tmpcmd != 0x16 && tmpcmd != 0x17 && tmpcmd != 0x19 && tmpcmd != 0x1a && tmpcmd != 0x1b && tmpcmd != 0x1c && tmpcmd != 0x1d && tmpcmd != 0x1e && tmpcmd != 0x1f && tmpcmd != 0x20 && tmpcmd != 0x21 && tmpcmd != 0x24 && tmpcmd != 0x25 && tmpcmd != 0x26 && tmpcmd != 0x27 && tmpcmd != 0x30 && tmpcmd != 0x32 && tmpcmd != 0x33 && tmpcmd != 0x34 && tmpcmd != 0x3d && tmpcmd != 0xa8 && tmpcmd != 0xee) { + } + rfu_counter = 0; + if ((rfu_qsend2 = rfu_qsend = ioMem[0x121]) != 0) { //COMM_SIODATA32_L+1, following data [to send] + rfu_state = RFU_SEND; + } + if (ioMem[COMM_SIODATA32_L] == 0xee) { //0xee cmd shouldn't override previous cmd + rfu_lastcmd = rfu_cmd2; + rfu_cmd2 = ioMem[COMM_SIODATA32_L]; + } + else{ + rfu_lastcmd = rfu_cmd; + rfu_cmd = ioMem[COMM_SIODATA32_L]; + rfu_cmd2 = 0; + int maskid; + if (rfu_cmd == 0x27 || rfu_cmd == 0x37) { + rfu_lastcmd2 = rfu_cmd; + rfu_lasttime = linktime; + } + else + if (rfu_cmd == 0x24) { //non-important data shouldn't overwrite important data from 0x25 + rfu_lastcmd2 = rfu_cmd; + rfu_cansend = false; + if (rfu_ishost) + maskid = ~rfu_data.rfu_is_host[linkid]; + else + maskid = ~(1 << gbaid); + //previous important data need to be received successfully before sending another important data + rfu_lasttime = linktime; //just to mark the last time a data being sent + if (rfu_data.rfu_q[linkid] < 2) + { //can overwrite now + rfu_cansend = true; + rfu_data.rfu_q[linkid] = 0; //rfu_qsend; + rfu_data.rfu_qid[linkid] = 0; + } + else if (!speedhack)rfu_waiting = true; //don't wait with speedhack + } + else + if (rfu_cmd == 0x25 || rfu_cmd == 0x35) { + rfu_lastcmd2 = rfu_cmd; + rfu_cansend = false; + if (rfu_ishost) + maskid = ~rfu_data.rfu_is_host[linkid]; else + maskid = ~(1 << gbaid); + //previous important data need to be received successfully before sending another important data + rfu_lasttime = linktime; + if (rfu_data.rfu_q[linkid] < 2) { + rfu_cansend = true; + rfu_data.rfu_q[linkid] = 0; //rfu_qsend; + rfu_data.rfu_qid[linkid] = 0; + } + else if (!speedhack)rfu_waiting = true; //don't wait with speedhack + } + else + if (rfu_cmd == 0xa8 || rfu_cmd == 0xb6) { + //wait for [important] data when previously sent is important data, might only need to wait for the 1st 0x25 cmd + bool ok = false; + } + else + if (rfu_cmd == 0x11 || rfu_cmd == 0x1a || rfu_cmd == 0x26) { + if (rfu_lastcmd2 == 0x24) + rfu_waiting = true; + } + } + if (rfu_waiting) + rfu_buf = READ32LE(&ioMem[COMM_SIODATA32_L]); + else + rfu_buf = 0x80000000; + } + else if (siodata_h == 0x8000) //finalize cmd, the game will send this when polarity reversed (expecting something) + { + rfu_qrecv_broadcast_data_len = 0; + if (rfu_cmd2 == 0xee) { + if (rfu_masterdata[0] == 2) //is this value of 2 related to polarity? + rfu_polarity = 0; //to normalize polarity after finalize looks more proper + rfu_buf = 0x99660000 | (rfu_qrecv_broadcast_data_len << 8) | (rfu_cmd2 ^ 0x80); + } + else{ + switch (rfu_cmd) { + case 0x1a: // check if someone joined + if (rfu_data.rfu_is_host[linkid]) { + gbaidx = gbaid; + + do + { + gbaidx = (gbaidx + 1) % rfu_data.numgbas; // check this numgbas = 3, gbaid = 0, gbaidx = 1, + if (gbaidx != linkid && rfu_data.rfu_reqid[gbaidx] == (linkid << 3) + 0x61f1) + { + rfu_masterdata[rfu_qrecv_broadcast_data_len++] = (gbaidx << 3) + 0x61f1; + } + } while (gbaidx != gbaid&&rfu_data.numgbas >= 2); + + if (rfu_qrecv_broadcast_data_len > 0) { + bool ok = false; + for (int i = 0; i < rfu_numclients; i++) + if ((rfu_clientlist[i] & 0xffff) == rfu_masterdata[0]) { ok = true; break; } + if (!ok) { + rfu_curclient = rfu_numclients; + rfu_data.rfu_clientidx[(rfu_masterdata[0] - 0x61f1) >> 3] = rfu_numclients; + rfu_clientlist[rfu_numclients] = rfu_masterdata[0] | (rfu_numclients++ << 16); + gbaid = (rfu_masterdata[0] - 0x61f1) >> 3; + rfu_data.rfu_signal[gbaid] = 0xffffffff >> ((3 - (rfu_numclients - 1)) << 3); + } + if (gbaid == linkid) { + gbaid = (rfu_masterdata[0] - 0x61f1) >> 3; + } + rfu_state = RFU_RECV; + } + } + if (rfu_numclients > 0) { + for (int i = 0; i < rfu_numclients; i++)rfu_masterdata[i] = rfu_clientlist[i]; + } + rfu_id = (gbaid << 3) + 0x61f1; + rfu_cmd ^= 0x80; + break; + case 0x1f: // join a room as client + // TODO: to fix infinte send&recv w/o giving much cance to update the screen when both side acting as client + // on MarioGolfAdv lobby(might be due to leftover data when switching from host to join mode at the same time?) + rfu_id = rfu_masterdata[0]; + gbaid = (rfu_id - 0x61f1) >> 3; + rfu_idx = rfu_id; + gbaidx = gbaid; + rfu_lastcmd2 = 0; + numtransfers = 0; + rfu_data.rfu_q[linkid] = 0; //to prevent leftover data from previous session received immediately in the new session + rfu_data.rfu_reqid[linkid] = rfu_id; + // TODO:might failed to reset rfu_request when being accessed by otherside at the same time, sometimes both acting + // as client but one of them still have request[linkid]!=0 //to prevent both GBAs from acting as Host, client can't + // be a host at the same time + rfu_data.rfu_is_host[linkid] = 0; + if (linkid != gbaid) { + rfu_data.rfu_signal[linkid] = 0x00ff; + rfu_data.rfu_is_host[gbaid] |= 1 << linkid; // tells the other GBA(a host) that someone(a client) is joining + log("%09d: joining room: signal: %d linkid: %d gbaid: %d\n", linktime, rfu_data.rfu_signal[linkid], linkid, gbaid); + } + rfu_cmd ^= 0x80; + break; + case 0x1e: // receive broadcast data + numtransfers = 0; + rfu_numclients = 0; + rfu_data.rfu_is_host[linkid] = 0; //to prevent both GBAs from acting as Host and thinking both of them have Client? + rfu_data.rfu_q[linkid] = 0; //to prevent leftover data from previous session received immediately in the new session + case 0x1d: // no visible difference + rfu_data.rfu_is_host[linkid] = 0; + memset(rfu_masterdata, 0, sizeof(rfu_data.rfu_broadcastdata[linkid])); + rfu_qrecv_broadcast_data_len = 0; + for (int i = 0; i < rfu_data.numgbas; i++) + { + if (i != linkid&&rfu_data.rfu_broadcastdata[i][0]) { + memcpy(&rfu_masterdata[rfu_qrecv_broadcast_data_len], rfu_data.rfu_broadcastdata[i], sizeof(rfu_data.rfu_broadcastdata[i])); + rfu_qrecv_broadcast_data_len += 7; + } + } + // is this needed? to prevent MarioGolfAdv from joining it's own room when switching + // from host to client mode due to left over room data in the game buffer? + // if(rfu_qrecv==0) rfu_qrecv = 7; + if (rfu_qrecv_broadcast_data_len > 0) + { + log("%09d: switching to RFU_RECV (broadcast)\n", linktime); + rfu_state = RFU_RECV; + } + rfu_polarity = 0; + rfu_counter = 0; + rfu_cmd ^= 0x80; + break; + case 0x16: // send broadcast data (ie. room name) + //start broadcasting here may cause client to join other client in pokemon coloseum + rfu_cmd ^= 0x80; + break; + case 0x11: // get signal strength + //Switch remote id + //check signal + if (rfu_data.numgbas >= 2 && (rfu_data.rfu_is_host[linkid] | rfu_data.rfu_is_host[gbaid])) //signal only good when connected + if (rfu_ishost) { //update, just incase there are leaving clients + u8 rfureq = rfu_data.rfu_is_host[linkid]; + u8 oldnum = rfu_numclients; + rfu_numclients = 0; + for (int i = 0; i<8; i++) { + if (rfureq & 1)rfu_numclients++; + rfureq >>= 1; + } + if (rfu_numclients>oldnum)rfu_numclients = oldnum; //must not be higher than old value, which means the new client haven't been processed by 0x1a cmd yet + rfu_data.rfu_signal[linkid] = 0xffffffff >> ((4 - rfu_numclients) << 3); + } + else rfu_data.rfu_signal[linkid] = rfu_data.rfu_signal[gbaid]; + else rfu_data.rfu_signal[linkid] = 0; + if (rfu_qrecv_broadcast_data_len == 0) { + rfu_qrecv_broadcast_data_len = 1; + rfu_masterdata[0] = (u32)rfu_data.rfu_signal[linkid]; + } + if (rfu_qrecv_broadcast_data_len > 0) { + rfu_state = RFU_RECV; + rfu_masterdata[rfu_qrecv_broadcast_data_len - 1] = (u32)rfu_data.rfu_signal[gbaid]; + } + rfu_cmd ^= 0x80; + break; + case 0x33: // rejoin status check? + if (rfu_data.rfu_signal[linkid] || numtransfers == 0) + rfu_masterdata[0] = 0; else //0=success + rfu_masterdata[0] = (u32)-1; //0xffffffff; //1=failed, 2++ = reserved/invalid, we use invalid value to let the game retries 0x33 until signal restored + rfu_cmd ^= 0x80; + rfu_state = RFU_RECV; + rfu_qrecv_broadcast_data_len = 1; + break; + case 0x14: // reset current client index and error check? + if ((rfu_data.rfu_signal[linkid] || numtransfers == 0) && gbaid != linkid) + rfu_masterdata[0] = ((!rfu_ishost ? 0x100 : 0 + rfu_data.rfu_clientidx[gbaid]) << 16) | ((gbaid << 3) + 0x61f1); + rfu_masterdata[0] = 0; //0=error, non-zero=good? + rfu_cmd ^= 0x80; + rfu_state = RFU_RECV; + rfu_qrecv_broadcast_data_len = 1; + break; + case 0x13: // error check? + if (rfu_data.rfu_signal[linkid] || numtransfers == 0 || rfu_initialized) + { + rfu_masterdata[0] = ((rfu_ishost ? 0x100 : 0 + rfu_data.rfu_clientidx[linkid]) << 16) | ((linkid << 3) + 0x61f1); + } + else //high word should be 0x0200 ? is 0x0200 means 1st client and 0x4000 means 2nd client? + { + log("%09d: error status\n", linktime); + rfu_masterdata[0] = 0; //0=error, non-zero=good? + } + rfu_cmd ^= 0x80; + rfu_state = RFU_RECV; + rfu_qrecv_broadcast_data_len = 1; + break; + case 0x20: // client, this has something to do with 0x1f + rfu_masterdata[0] = (rfu_data.rfu_clientidx[linkid]) << 16; //needed for client + rfu_masterdata[0] |= (linkid << 3) + 0x61f1; //0x1234; //0x641b; //max id value? Encryption key or Station Mode? (0xFBD9/0xDEAD=Access Point mode?) + rfu_data.rfu_q[linkid] = 0; //to prevent leftover data from previous session received immediately in the new session + rfu_data.rfu_is_host[linkid] = 0; //TODO:may not works properly, sometimes both acting as client but one of them still have request[linkid]!=0 //to prevent both GBAs from acting as Host, client can't be a host at the same time + if (rfu_data.rfu_signal[gbaid] < rfu_data.rfu_signal[linkid]) + rfu_data.rfu_signal[gbaid] = rfu_data.rfu_signal[linkid]; + + rfu_polarity = 0; + rfu_state = RFU_RECV; + rfu_qrecv_broadcast_data_len = 1; + rfu_cmd ^= 0x80; + break; + case 0x21: // client, this too + rfu_masterdata[0] = (rfu_data.rfu_clientidx[linkid]) << 16; //not needed? + rfu_masterdata[0] |= (linkid << 3) + 0x61f1; //0x641b; //max id value? Encryption key or Station Mode? (0xFBD9/0xDEAD=Access Point mode?) + rfu_data.rfu_q[linkid] = 0; //to prevent leftover data from previous session received immediately in the new session + rfu_data.rfu_is_host[linkid] = 0; //TODO:may not works properly, sometimes both acting as client but one of them still have request[linkid]!=0 //to prevent both GBAs from acting as Host, client can't be a host at the same time + rfu_polarity = 0; + rfu_state = RFU_RECV; //3; + rfu_qrecv_broadcast_data_len = 1; + rfu_cmd ^= 0x80; + break; + + case 0x19: // server bind/start listening for client to join, may be used in the middle of host<->client communication w/o causing clients to dc? + rfu_data.rfu_q[linkid] = 0; //to prevent leftover data from previous session received immediately in the new session + rfu_data.rfu_broadcastdata[linkid][0] = (linkid << 3) + 0x61f1; //start broadcasting room name + rfu_data.rfu_clientidx[linkid] = 0; + rfu_ishost = true; + rfu_cmd ^= 0x80; + break; + + case 0x1c: //client, might reset some data? + rfu_ishost = false; //TODO: prevent both GBAs act as client but one of them have rfu_request[linkid]!=0 on MarioGolfAdv lobby + rfu_numclients = 0; + rfu_curclient = 0; + rfu_data.rfu_listfront[linkid] = 0; + rfu_data.rfu_listback[linkid] = 0; + rfu_data.rfu_q[linkid] = 0; //to prevent leftover data from previous session received immediately in the new session + case 0x1b: //host, might reset some data? may be used in the middle of host<->client communication w/o causing clients to dc? + rfu_data.rfu_broadcastdata[linkid][0] = 0; //0 may cause player unable to join in pokemon union room? + rfu_cmd ^= 0x80; + break; + + case 0x30: //reset some data + if (linkid != gbaid) { //(rfu_data.numgbas >= 2) + rfu_data.rfu_is_host[gbaid] &= ~(1 << linkid); //rfu_data.rfu_request[gbaid] = 0; + } + while (rfu_data.rfu_signal[linkid]) { + rfu_data.rfu_signal[linkid] = 0; + rfu_data.rfu_is_host[linkid] = 0; //There is a possibility where rfu_request/signal didn't get zeroed here when it's being read by the other GBA at the same time + } + rfu_data.rfu_listfront[linkid] = 0; + rfu_data.rfu_listback[linkid] = 0; + rfu_data.rfu_q[linkid] = 0; //to prevent leftover data from previous session received immediately in the new session + rfu_data.rfu_proto[linkid] = 0; + rfu_data.rfu_reqid[linkid] = 0; + rfu_data.rfu_linktime[linkid] = 0; + rfu_data.rfu_gdata[linkid] = 0; + rfu_data.rfu_broadcastdata[linkid][0] = 0; + rfu_polarity = 0; //is this included? + numtransfers = 0; + rfu_numclients = 0; + rfu_curclient = 0; + rfu_cmd ^= 0x80; + break; + + case 0x3d: // init/reset rfu data + rfu_initialized = false; + case 0x10: // init/reset rfu data + if (linkid != gbaid) { //(rfu_data.numgbas >= 2) + rfu_data.rfu_is_host[gbaid] &= ~(1 << linkid); //rfu_data.rfu_request[gbaid] = 0; + } + while (rfu_data.rfu_signal[linkid]) { + rfu_data.rfu_signal[linkid] = 0; + rfu_data.rfu_is_host[linkid] = 0; //There is a possibility where rfu_request/signal didn't get zeroed here when it's being read by the other GBA at the same time + } + rfu_data.rfu_listfront[linkid] = 0; + rfu_data.rfu_listback[linkid] = 0; + rfu_data.rfu_q[linkid] = 0; //to prevent leftover data from previous session received immediately in the new session + rfu_data.rfu_proto[linkid] = 0; + rfu_data.rfu_reqid[linkid] = 0; + rfu_data.rfu_linktime[linkid] = 0; + rfu_data.rfu_gdata[linkid] = 0; + rfu_data.rfu_broadcastdata[linkid][0] = 0; + rfu_polarity = 0; //is this included? + numtransfers = 0; + rfu_numclients = 0; + rfu_curclient = 0; + rfu_id = 0; + rfu_idx = 0; + gbaid = linkid; + gbaidx = gbaid; + rfu_ishost = false; + rfu_qrecv_broadcast_data_len = 0; + rfu_cmd ^= 0x80; + break; + + case 0x36: //does it expect data returned? + case 0x26: + //Switch remote id to available data + bool ok; + int ctr; + ctr = 0; + if (rfu_data.rfu_listfront[linkid] != rfu_data.rfu_listback[linkid]) //data existed + do { + u8 qdata_len = rfu_data.rfu_datalist[linkid][rfu_data.rfu_listfront[linkid]].len; //(u8)rfu_data.rfu_qlist[linkid][rfu_data.rfu_listfront[linkid]]; + ok = false; + if (qdata_len != rfu_qrecv_broadcast_data_len) ok = true; else + for (int i = 0; i 1) + { + if (rfu_qrecv_broadcast_data_len>1) { //stop here if next data is different than currently unprocessed non-ping data + //break; + } + + if (qdata_len >= rfu_qrecv_broadcast_data_len) { + rfu_masterq = rfu_qrecv_broadcast_data_len = qdata_len; + gbaid = rfu_data.rfu_datalist[linkid][rfu_data.rfu_listfront[linkid]].gbaid; + rfu_id = (gbaid << 3) + 0x61f1; + if (rfu_ishost) + { + rfu_curclient = (u8)rfu_data.rfu_clientidx[gbaid]; + } + if (rfu_qrecv_broadcast_data_len != 0) { //data size > 0 + memcpy(rfu_masterdata, rfu_data.rfu_datalist[linkid][rfu_data.rfu_listfront[linkid]].data, std::min(rfu_masterq << 2, (int)sizeof(rfu_masterdata))); + } + } + } + + rfu_data.rfu_listfront[linkid]++; ctr++; + + ok = (rfu_data.rfu_listfront[linkid] != rfu_data.rfu_listback[linkid] && rfu_data.rfu_datalist[linkid][rfu_data.rfu_listfront[linkid]].gbaid == gbaid); + } while (ok); + + if (rfu_qrecv_broadcast_data_len > 0) { //data was available + rfu_state = RFU_RECV; + rfu_counter = 0; + rfu_lastcmd2 = 0; + + //Switch remote id to next remote id + } + rfu_cmd ^= 0x80; + break; + + case 0x24: // send [non-important] data (used by server often) + rfu_data.rfu_linktime[linkid] = linktime; //save the ticks before reseted to zero + + if (rfu_cansend && rfu_qsend2 >= 0) { + if (rfu_ishost) { + for (int j = 0; j 0) { + if (rfu_ishost) { + for (int j = 0; j> 24) != 0x7ff) + rfu_state = RFU_INIT; //to prevent the next reinit words from getting in finalization processing (here), may cause MarioGolfAdv to show Linking error when this occurs instead of continuing with COMM cmd + rfu_buf = (READ16LE(&ioMem[COMM_SIODATA32_L]) << 16) | siodata_h; + } + break; + + case RFU_SEND: //data following after initialize cmd + CurDAT = READ32LE(&ioMem[COMM_SIODATA32_L]); + if (--rfu_qsend == 0) { + rfu_state = RFU_COMM; + } + + switch (rfu_cmd) { + case 0x16: + rfu_data.rfu_broadcastdata[linkid][1 + rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x17: + rfu_masterdata[rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x1f: + rfu_masterdata[rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + case 0x24: + //if(rfu_data.rfu_proto[linkid]) break; //important data from 0x25 shouldn't be overwritten by 0x24 + case 0x25: + case 0x35: + rfu_masterdata[rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + + default: + rfu_masterdata[rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + break; + } + rfu_buf = 0x80000000; + break; + + case RFU_RECV: //data following after finalize cmd + if (--rfu_qrecv_broadcast_data_len == 0) + { + rfu_state = RFU_COMM; + } + + switch (rfu_cmd) { + case 0x9d: + case 0x9e: + rfu_buf = rfu_masterdata[rfu_counter++]; + break; + + case 0xb6: + case 0xa6: + rfu_buf = rfu_masterdata[rfu_counter++]; + break; + + case 0x91: //signal strength + rfu_buf = rfu_masterdata[rfu_counter++]; + break; + + case 0xb3: //rejoin error code? + case 0x94: //last error code? //it seems like the game doesn't care about this value + case 0x93: //last error code? //it seems like the game doesn't care about this value + rfu_buf = rfu_masterdata[rfu_counter++]; + break; + + case 0xa0: + //max id value? Encryption key or Station Mode? (0xFBD9/0xDEAD=Access Point mode?) + //high word 0 = a success indication? + rfu_buf = rfu_masterdata[rfu_counter++]; + break; + case 0xa1: + //max id value? the same with 0xa0 cmd? + //high word 0 = a success indication? + rfu_buf = rfu_masterdata[rfu_counter++]; + break; + + case 0x9a: + rfu_buf = rfu_masterdata[rfu_counter++]; + break; + + default: //unknown data (should use 0 or -1 as default), usually returning 0 might cause the game to think there is something wrong with the connection (ie. 0x11/0x13 cmd) + //0x0173 //not 0x0000 as default? + //0x0000 + //rfu_buf = 0xffffffff; //rfu_masterdata[rfu_counter++]; + rfu_buf = rfu_masterdata[rfu_counter++]; + break; + } + break; + } + transfer_direction = RECEIVING; + + PrevVAL = value; + PrevDAT = CurDAT; + PrevCOM = CurCOM; + } + + if (rfu_polarity) + value ^= 4; // sometimes it's the other way around + default: + UPDATE_REG(COMM_SIOCNT, value); + return; + } + UPDATE_REG(COMM_SIOCNT, value); +} + +bool LinkRFUUpdateSocket() +{ + if (rfu_enabled) { + if (transfer_direction == RECEIVING && rfu_transfer_end <= 0) + { + if (rfu_waiting) { + bool ok = false; + if (rfu_state != RFU_INIT) + { + if (rfu_cmd == 0x24 || rfu_cmd == 0x25 || rfu_cmd == 0x35) { + if (rfu_data.rfu_q[linkid] < 2 || rfu_qsend>1) + { + rfu_cansend = true; + rfu_data.rfu_q[linkid] = 0; + rfu_data.rfu_qid[linkid] = 0; + } + rfu_buf = 0x80000000; + } + else{ + if (rfu_cmd == 0xa5 || rfu_cmd == 0xa7 || rfu_cmd == 0xb5 || rfu_cmd == 0xb7 || rfu_cmd == 0xee) + rfu_polarity = 1; + if (rfu_cmd == 0xa5 || rfu_cmd == 0xa7) + rfu_cmd = 0x28; + else if (rfu_cmd == 0xb5 || rfu_cmd == 0xb7) + rfu_cmd = 0x36; + + if (READ32LE(&ioMem[COMM_SIODATA32_L]) == 0x80000000) + rfu_buf = 0x99660000 | (rfu_qrecv_broadcast_data_len << 8) | rfu_cmd; + else + rfu_buf = 0x80000000; + } + rfu_waiting = false; + } + } + UPDATE_REG(COMM_SIODATA32_L, rfu_buf); + UPDATE_REG(COMM_SIODATA32_H, rfu_buf >> 16); + } + } + return true; +} + +static void UpdateRFUSocket(int ticks) +{ + rfu_last_broadcast_time -= ticks; + + if (rfu_last_broadcast_time < 0) + { + if (linkid == 0) + { + linktime = 0; + rfu_server.Recv(); // recv broadcast data + rfu_server.Send(); // send broadcast data + } + else + { + rfu_client.Send(); // send broadcast data + rfu_client.Recv(); // recv broadcast data + } + { + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (i != linkid) + { + rfu_data.rfu_listback[i] = 0; // Flush the queue + } + } + } + rfu_transfer_end = 0; + + if (rfu_last_broadcast_time < 0) + rfu_last_broadcast_time = 3000; + //rfu_last_broadcast_time = 5600; // Upper physical limit of 5600? 3000 packets/sec + } + + if (rfu_enabled) + { + if (LinkRFUUpdateSocket()) + { + if (transfer_direction == RECEIVING && rfu_transfer_end <= 0) + { + transfer_direction = SENDING; + u16 value = READ16LE(&ioMem[COMM_SIOCNT]); + if (value & SIO_IRQ_ENABLE) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + + //if (rfu_polarity) value ^= 4; + value &= ~SIO_TRANS_FLAG_RECV_ENABLE; + value |= (value & 1) << 2; //this will automatically set the correct polarity, even w/o rfu_polarity since the game will be the one who change the polarity instead of the adapter + + UPDATE_REG(COMM_SIOCNT, (value & ~SIO_TRANS_START) | SIO_TRANS_FLAG_SEND_DISABLE); //Start bit.7 reset, SO bit.3 set automatically upon transfer completion? + } + return; + } + } +} + #if (defined __WIN32__ || defined _WIN32) static ConnectionState InitIPC() { @@ -1545,7 +2738,7 @@ static void UpdateCableIPC(int ticks) } } // now that value is available, store it - UPDATE_REG((COMM_SIOMULTI0 - 2) + (transfer_direction<<1), linkmem->linkdata[transfer_direction-1]); + UPDATE_REG((COMM_SIOMULTI0 - 2) + (transfer_direction << 1), linkmem->linkdata[transfer_direction - 1]); // transfer machine's value at start of its transfer cycle if(linkid == transfer_direction) { @@ -1700,7 +2893,6 @@ static void StartRFU(u16 value) if ((rfu_qsend2 = rfu_qsend = ioMem[0x121]) != 0) { //COMM_SIODATA32_L+1, following data [to send] rfu_state = RFU_SEND; } - if ((rfu_cmd | 0x80) != 0x91 && (rfu_cmd | 0x80) != 0x93 && ((rfu_cmd | 0x80) < 0xa4 || (rfu_cmd | 0x80) > 0xa8))rfu_lastcmd3 = rfu_cmd; if (ioMem[COMM_SIODATA32_L] == 0xee) { //0xee cmd shouldn't override previous cmd rfu_lastcmd = rfu_cmd2; rfu_cmd2 = ioMem[COMM_SIODATA32_L]; @@ -1720,7 +2912,7 @@ static void StartRFU(u16 value) rfu_lastcmd2 = rfu_cmd; rfu_cansend = false; if (rfu_ishost) - maskid = ~linkmem->rfu_request[vbaid]; else + maskid = ~linkmem->rfu_is_host[vbaid]; else maskid = ~(1 << gbaid); //previous important data need to be received successfully before sending another important data rfu_lasttime = GetTickCount(); //just to mark the last time a data being sent @@ -1737,7 +2929,7 @@ static void StartRFU(u16 value) if (j != vbaid)SetEvent(linksync[j]); WaitForSingleObject(linksync[vbaid], 1); //linktimeout //wait until this gba allowed to move (to prevent both GBAs from using 0x25 at the same time) ResetEvent(linksync[vbaid]); //lock this gba, don't allow this gba to move (prevent sending another data too fast w/o giving the other side chances to read it) - if (!rfu_ishost&&linkmem->rfu_request[vbaid]) { linkmem->rfu_request[vbaid] = 0; break; } //workaround for a bug where rfu_request failed to reset when GBA act as client + if (!rfu_ishost&&linkmem->rfu_is_host[vbaid]) { linkmem->rfu_is_host[vbaid] = 0; break; } //workaround for a bug where rfu_request failed to reset when GBA act as client } } //SetEvent(linksync[vbaid]); //set again to reduce the lag since it will be waited again during finalization cmd @@ -1763,7 +2955,7 @@ static void StartRFU(u16 value) rfu_lastcmd2 = rfu_cmd; rfu_cansend = false; if (rfu_ishost) - maskid = ~linkmem->rfu_request[vbaid]; else + maskid = ~linkmem->rfu_is_host[vbaid]; else maskid = ~(1 << gbaid); //previous important data need to be received successfully before sending another important data rfu_lasttime = GetTickCount(); @@ -1781,7 +2973,7 @@ static void StartRFU(u16 value) if (j != vbaid)SetEvent(linksync[j]); WaitForSingleObject(linksync[vbaid], 1); //linktimeout //wait until this gba allowed to move (to prevent both GBAs from using 0x25 at the same time) ResetEvent(linksync[vbaid]); //lock this gba, don't allow this gba to move (prevent sending another data too fast w/o giving the other side chances to read it) - if (!rfu_ishost&&linkmem->rfu_request[vbaid]) { linkmem->rfu_request[vbaid] = 0; break; } //workaround for a bug where rfu_request failed to reset when GBA act as client + if (!rfu_ishost&&linkmem->rfu_is_host[vbaid]) { linkmem->rfu_is_host[vbaid] = 0; break; } //workaround for a bug where rfu_request failed to reset when GBA act as client } } //SetEvent(linksync[vbaid]); //set again to reduce the lag since it will be waited again during finalization cmd @@ -1819,22 +3011,23 @@ static void StartRFU(u16 value) } else if (siodata_h == 0x8000) //finalize cmd, the game will send this when polarity reversed (expecting something) { - rfu_qrecv = 0; + rfu_qrecv_broadcast_data_len = 0; if (rfu_cmd2 == 0xee) { if (rfu_masterdata[0] == 2) //is this value of 2 related to polarity? rfu_polarity = 0; //to normalize polarity after finalize looks more proper - rfu_buf = 0x99660000 | (rfu_qrecv << 8) | (rfu_cmd2 ^ 0x80); + rfu_buf = 0x99660000 | (rfu_qrecv_broadcast_data_len << 8) | (rfu_cmd2 ^ 0x80); } else{ switch (rfu_cmd) { case 0x1a: // check if someone joined - if (linkmem->rfu_request[vbaid]) { + if (linkmem->rfu_is_host[vbaid]) { gbaidx = gbaid; do{ gbaidx = (gbaidx + 1) % linkmem->numgbas; - if (gbaidx != vbaid&&linkmem->rfu_reqid[gbaidx] == (vbaid << 3) + 0x61f1)rfu_masterdata[rfu_qrecv++] = (gbaidx << 3) + 0x61f1; + if (gbaidx != vbaid&&linkmem->rfu_reqid[gbaidx] == (vbaid << 3) + 0x61f1)rfu_masterdata[rfu_qrecv_broadcast_data_len++] = (gbaidx << 3) + 0x61f1; + log("qrecv++ %d\n", rfu_qrecv_broadcast_data_len); } while (gbaidx != gbaid&&linkmem->numgbas >= 2); - if (rfu_qrecv > 0) { + if (rfu_qrecv_broadcast_data_len > 0) { bool ok = false; for (int i = 0; i < rfu_numclients; i++) if ((rfu_clientlist[i] & 0xffff) == rfu_masterdata[0]) { ok = true; break; } @@ -1871,34 +3064,33 @@ static void StartRFU(u16 value) // TODO:might failed to reset rfu_request when being accessed by otherside at the same time, sometimes both acting // as client but one of them still have request[vbaid]!=0 //to prevent both GBAs from acting as Host, client can't // be a host at the same time - linkmem->rfu_request[vbaid] = 0; + linkmem->rfu_is_host[vbaid] = 0; if (vbaid != gbaid) { - if (!linkmem->rfu_request[gbaid])rfu_isfirst = true; // No other client already joined linkmem->rfu_signal[vbaid] = 0x00ff; - linkmem->rfu_request[gbaid] |= 1 << vbaid; // tells the other GBA(a host) that someone(a client) is joining + linkmem->rfu_is_host[gbaid] |= 1 << vbaid; // tells the other GBA(a host) that someone(a client) is joining } rfu_cmd ^= 0x80; break; case 0x1e: // receive broadcast data numtransfers = 0; rfu_numclients = 0; - linkmem->rfu_request[vbaid] = 0; //to prevent both GBAs from acting as Host and thinking both of them have Client? + linkmem->rfu_is_host[vbaid] = 0; //to prevent both GBAs from acting as Host and thinking both of them have Client? linkmem->rfu_q[vbaid] = 0; //to prevent leftover data from previous session received immediately in the new session case 0x1d: // no visible difference - linkmem->rfu_request[vbaid] = 0; - memset(rfu_masterdata, 0, sizeof(linkmem->rfu_bdata[vbaid])); - rfu_qrecv = 0; + linkmem->rfu_is_host[vbaid] = 0; + memset(rfu_masterdata, 0, sizeof(linkmem->rfu_broadcastdata[vbaid])); + rfu_qrecv_broadcast_data_len = 0; for (int i = 0; i < linkmem->numgbas; i++) { - if (i != vbaid&&linkmem->rfu_bdata[i][0]) { - memcpy(&rfu_masterdata[rfu_qrecv], linkmem->rfu_bdata[i], sizeof(linkmem->rfu_bdata[i])); - rfu_qrecv += 7; + if (i != vbaid&&linkmem->rfu_broadcastdata[i][0]) { + memcpy(&rfu_masterdata[rfu_qrecv_broadcast_data_len], linkmem->rfu_broadcastdata[i], sizeof(linkmem->rfu_broadcastdata[i])); + rfu_qrecv_broadcast_data_len += 7; } } // is this needed? to prevent MarioGolfAdv from joining it's own room when switching // from host to client mode due to left over room data in the game buffer? // if(rfu_qrecv==0) rfu_qrecv = 7; - if (rfu_qrecv > 0) + if (rfu_qrecv_broadcast_data_len > 0) rfu_state = RFU_RECV; rfu_polarity = 0; rfu_counter = 0; @@ -1912,7 +3104,7 @@ static void StartRFU(u16 value) break; case 0x11: // get signal strength //Switch remote id - if (linkmem->rfu_request[vbaid]) { //is a host + if (linkmem->rfu_is_host[vbaid]) { //is a host /*//gbaid = 1-vbaid; //linkmem->rfu_request[vbaid] & 1; gbaidx = gbaid; do { @@ -1929,9 +3121,9 @@ static void StartRFU(u16 value) }*/ } //check signal - if (linkmem->numgbas >= 2 && (linkmem->rfu_request[vbaid] | linkmem->rfu_request[gbaid])) //signal only good when connected + if (linkmem->numgbas >= 2 && (linkmem->rfu_is_host[vbaid] | linkmem->rfu_is_host[gbaid])) //signal only good when connected if (rfu_ishost) { //update, just incase there are leaving clients - u8 rfureq = linkmem->rfu_request[vbaid]; + u8 rfureq = linkmem->rfu_is_host[vbaid]; u8 oldnum = rfu_numclients; rfu_numclients = 0; for (int i = 0; i<8; i++) { @@ -1966,15 +3158,15 @@ static void StartRFU(u16 value) }*/ //rfu_masterdata[0] = (u32)linkmem->rfu_signal[vbaid]; } - if (rfu_qrecv == 0) { - rfu_qrecv = 1; + if (rfu_qrecv_broadcast_data_len == 0) { + rfu_qrecv_broadcast_data_len = 1; rfu_masterdata[0] = (u32)linkmem->rfu_signal[vbaid]; } - if (rfu_qrecv > 0) { + if (rfu_qrecv_broadcast_data_len > 0) { rfu_state = RFU_RECV; int hid = vbaid; if (!rfu_ishost)hid = gbaid; - rfu_masterdata[rfu_qrecv - 1] = (u32)linkmem->rfu_signal[hid]; + rfu_masterdata[rfu_qrecv_broadcast_data_len - 1] = (u32)linkmem->rfu_signal[hid]; } rfu_cmd ^= 0x80; //rfu_polarity = 0; @@ -1994,7 +3186,7 @@ static void StartRFU(u16 value) rfu_masterdata[0] = (u32)-1; //0xffffffff; //1=failed, 2++ = reserved/invalid, we use invalid value to let the game retries 0x33 until signal restored rfu_cmd ^= 0x80; rfu_state = RFU_RECV; - rfu_qrecv = 1; + rfu_qrecv_broadcast_data_len = 1; break; case 0x14: // reset current client index and error check? if ((linkmem->rfu_signal[vbaid] || numtransfers == 0) && gbaid != vbaid) @@ -2002,7 +3194,7 @@ static void StartRFU(u16 value) rfu_masterdata[0] = 0; //0=error, non-zero=good? rfu_cmd ^= 0x80; rfu_state = RFU_RECV; - rfu_qrecv = 1; + rfu_qrecv_broadcast_data_len = 1; break; case 0x13: // error check? if (linkmem->rfu_signal[vbaid] || numtransfers == 0 || rfu_initialized) @@ -2010,35 +3202,35 @@ static void StartRFU(u16 value) rfu_masterdata[0] = 0; //0=error, non-zero=good? rfu_cmd ^= 0x80; rfu_state = RFU_RECV; - rfu_qrecv = 1; + rfu_qrecv_broadcast_data_len = 1; break; case 0x20: // client, this has something to do with 0x1f rfu_masterdata[0] = (linkmem->rfu_clientidx[vbaid]) << 16; //needed for client rfu_masterdata[0] |= (vbaid << 3) + 0x61f1; //0x1234; //0x641b; //max id value? Encryption key or Station Mode? (0xFBD9/0xDEAD=Access Point mode?) linkmem->rfu_q[vbaid] = 0; //to prevent leftover data from previous session received immediately in the new session - linkmem->rfu_request[vbaid] = 0; //TODO:may not works properly, sometimes both acting as client but one of them still have request[vbaid]!=0 //to prevent both GBAs from acting as Host, client can't be a host at the same time + linkmem->rfu_is_host[vbaid] = 0; //TODO:may not works properly, sometimes both acting as client but one of them still have request[vbaid]!=0 //to prevent both GBAs from acting as Host, client can't be a host at the same time if (linkmem->rfu_signal[gbaid] < linkmem->rfu_signal[vbaid]) linkmem->rfu_signal[gbaid] = linkmem->rfu_signal[vbaid]; rfu_polarity = 0; rfu_state = RFU_RECV; - rfu_qrecv = 1; + rfu_qrecv_broadcast_data_len = 1; rfu_cmd ^= 0x80; break; case 0x21: // client, this too rfu_masterdata[0] = (linkmem->rfu_clientidx[vbaid]) << 16; //not needed? rfu_masterdata[0] |= (vbaid << 3) + 0x61f1; //0x641b; //max id value? Encryption key or Station Mode? (0xFBD9/0xDEAD=Access Point mode?) linkmem->rfu_q[vbaid] = 0; //to prevent leftover data from previous session received immediately in the new session - linkmem->rfu_request[vbaid] = 0; //TODO:may not works properly, sometimes both acting as client but one of them still have request[vbaid]!=0 //to prevent both GBAs from acting as Host, client can't be a host at the same time + linkmem->rfu_is_host[vbaid] = 0; //TODO:may not works properly, sometimes both acting as client but one of them still have request[vbaid]!=0 //to prevent both GBAs from acting as Host, client can't be a host at the same time rfu_polarity = 0; rfu_state = RFU_RECV; //3; - rfu_qrecv = 1; + rfu_qrecv_broadcast_data_len = 1; rfu_cmd ^= 0x80; break; case 0x19: // server bind/start listening for client to join, may be used in the middle of host<->client communication w/o causing clients to dc? //linkmem->rfu_request[vbaid] = 0; //to prevent both GBAs from acting as Host and thinking both of them have Client? linkmem->rfu_q[vbaid] = 0; //to prevent leftover data from previous session received immediately in the new session - linkmem->rfu_bdata[vbaid][0] = (vbaid << 3) + 0x61f1; //start broadcasting room name + linkmem->rfu_broadcastdata[vbaid][0] = (vbaid << 3) + 0x61f1; //start broadcasting room name linkmem->rfu_clientidx[vbaid] = 0; //numtransfers = 0; //rfu_numclients = 0; @@ -2046,7 +3238,6 @@ static void StartRFU(u16 value) //rfu_lastcmd2 = 0; //rfu_polarity = 0; rfu_ishost = true; - rfu_isfirst = false; rfu_cmd ^= 0x80; break; @@ -2065,7 +3256,7 @@ static void StartRFU(u16 value) //c_s.Unlock(); case 0x1b: //host, might reset some data? may be used in the middle of host<->client communication w/o causing clients to dc? //linkmem->rfu_request[vbaid] = 0; //to prevent both GBAs from acting as Client and thinking one of them is a Host? - linkmem->rfu_bdata[vbaid][0] = 0; //0 may cause player unable to join in pokemon union room? + linkmem->rfu_broadcastdata[vbaid][0] = 0; //0 may cause player unable to join in pokemon union room? //numtransfers = 0; //linktime = 1; rfu_cmd ^= 0x80; @@ -2074,14 +3265,14 @@ static void StartRFU(u16 value) case 0x30: //reset some data if (vbaid != gbaid) { //(linkmem->numgbas >= 2) //linkmem->rfu_signal[gbaid] = 0; - linkmem->rfu_request[gbaid] &= ~(1 << vbaid); //linkmem->rfu_request[gbaid] = 0; + linkmem->rfu_is_host[gbaid] &= ~(1 << vbaid); //linkmem->rfu_request[gbaid] = 0; SetEvent(linksync[gbaid]); //allow other gba to move } //WaitForSingleObject(linksync[vbaid], 40/*linktimeout*/); while (linkmem->rfu_signal[vbaid]) { WaitForSingleObject(linksync[vbaid], 1/*linktimeout*/); linkmem->rfu_signal[vbaid] = 0; - linkmem->rfu_request[vbaid] = 0; //There is a possibility where rfu_request/signal didn't get zeroed here when it's being read by the other GBA at the same time + linkmem->rfu_is_host[vbaid] = 0; //There is a possibility where rfu_request/signal didn't get zeroed here when it's being read by the other GBA at the same time //SleepEx(1,true); } //c_s.Lock(); @@ -2093,7 +3284,7 @@ static void StartRFU(u16 value) linkmem->rfu_reqid[vbaid] = 0; linkmem->rfu_linktime[vbaid] = 0; linkmem->rfu_gdata[vbaid] = 0; - linkmem->rfu_bdata[vbaid][0] = 0; + linkmem->rfu_broadcastdata[vbaid][0] = 0; //c_s.Unlock(); rfu_polarity = 0; //is this included? //linkid = -1; //0; @@ -2116,14 +3307,14 @@ static void StartRFU(u16 value) case 0x10: // init/reset rfu data if (vbaid != gbaid) { //(linkmem->numgbas >= 2) //linkmem->rfu_signal[gbaid] = 0; - linkmem->rfu_request[gbaid] &= ~(1 << vbaid); //linkmem->rfu_request[gbaid] = 0; + linkmem->rfu_is_host[gbaid] &= ~(1 << vbaid); //linkmem->rfu_request[gbaid] = 0; SetEvent(linksync[gbaid]); //allow other gba to move } //WaitForSingleObject(linksync[vbaid], 40/*linktimeout*/); while (linkmem->rfu_signal[vbaid]) { WaitForSingleObject(linksync[vbaid], 1/*linktimeout*/); linkmem->rfu_signal[vbaid] = 0; - linkmem->rfu_request[vbaid] = 0; //There is a possibility where rfu_request/signal didn't get zeroed here when it's being read by the other GBA at the same time + linkmem->rfu_is_host[vbaid] = 0; //There is a possibility where rfu_request/signal didn't get zeroed here when it's being read by the other GBA at the same time //SleepEx(1,true); } //c_s.Lock(); @@ -2135,7 +3326,7 @@ static void StartRFU(u16 value) linkmem->rfu_reqid[vbaid] = 0; linkmem->rfu_linktime[vbaid] = 0; linkmem->rfu_gdata[vbaid] = 0; - linkmem->rfu_bdata[vbaid][0] = 0; + linkmem->rfu_broadcastdata[vbaid][0] = 0; //c_s.Unlock(); rfu_polarity = 0; //is this included? //linkid = -1; //0; @@ -2148,8 +3339,7 @@ static void StartRFU(u16 value) gbaid = vbaid; gbaidx = gbaid; rfu_ishost = false; - rfu_isfirst = false; - rfu_qrecv = 0; + rfu_qrecv_broadcast_data_len = 0; SetEvent(linksync[vbaid]); //may not be needed rfu_cmd ^= 0x80; break; @@ -2186,7 +3376,7 @@ static void StartRFU(u16 value) //Read data when available /*if((linkmem->rfu_qid[gbaid] & (1<rfu_q[gbaid])!=0) { //data size > 0 - memcpy(rfu_masterdata, linkmem->rfu_data[gbaid], min(rfu_masterq<<2,sizeof(rfu_masterdata))); //128 //read data from other GBA + memcpy(rfu_masterdata, linkmem->linkdata[gbaid], min(rfu_masterq<<2,sizeof(rfu_masterdata))); //128 //read data from other GBA linkmem->rfu_qid[gbaid] &= ~(1<rfu_request[gbaid]) linkmem->rfu_qid[gbaid] &= linkmem->rfu_request[gbaid]; //remask if it's host, just incase there are client leaving multiplayer if(!linkmem->rfu_qid[gbaid]) linkmem->rfu_q[gbaid] = 0; //mark that it has been fully received @@ -2203,27 +3393,27 @@ static void StartRFU(u16 value) do { u8 tmpq = linkmem->rfu_datalist[vbaid][linkmem->rfu_listfront[vbaid]].len; //(u8)linkmem->rfu_qlist[vbaid][linkmem->rfu_listfront[vbaid]]; ok = false; - if (tmpq != rfu_qrecv) ok = true; else + if (tmpq != rfu_qrecv_broadcast_data_len) ok = true; else for (int i = 0; irfu_datalist[vbaid][linkmem->rfu_listfront[vbaid]].data[i] != rfu_masterdata[i]) { ok = true; break; } if (tmpq == 0 && ctr == 0) ok = true; //0-size data if (ok) //next data is not a duplicate of currently unprocessed data - if (rfu_qrecv<2 || tmpq>1) + if (rfu_qrecv_broadcast_data_len<2 || tmpq>1) { - if (rfu_qrecv>1) { //stop here if next data is different than currently unprocessed non-ping data + if (rfu_qrecv_broadcast_data_len>1) { //stop here if next data is different than currently unprocessed non-ping data linkmem->rfu_linktime[gbaid] = linkmem->rfu_datalist[vbaid][linkmem->rfu_listfront[vbaid]].time; break; } - if (tmpq >= rfu_qrecv) { - rfu_masterq = rfu_qrecv = tmpq; + if (tmpq >= rfu_qrecv_broadcast_data_len) { + rfu_masterq = rfu_qrecv_broadcast_data_len = tmpq; gbaid = linkmem->rfu_datalist[vbaid][linkmem->rfu_listfront[vbaid]].gbaid; rfu_id = (gbaid << 3) + 0x61f1; if (rfu_ishost) rfu_curclient = (u8)linkmem->rfu_clientidx[gbaid]; - if (rfu_qrecv != 0) { //data size > 0 + if (rfu_qrecv_broadcast_data_len != 0) { //data size > 0 memcpy(rfu_masterdata, linkmem->rfu_datalist[vbaid][linkmem->rfu_listfront[vbaid]].data, std::min(rfu_masterq << 2, (int)sizeof(rfu_masterdata))); } } @@ -2235,7 +3425,7 @@ static void StartRFU(u16 value) } while (ok); //SetEvent(linksync[vbaid]); //unlock it so anyone can access it - if (rfu_qrecv>0) { //data was available + if (rfu_qrecv_broadcast_data_len>0) { //data was available rfu_state = RFU_RECV; rfu_counter = 0; rfu_lastcmd2 = 0; @@ -2435,7 +3625,7 @@ static void StartRFU(u16 value) break; } if (!rfu_waiting) - rfu_buf = 0x99660000 | (rfu_qrecv << 8) | rfu_cmd; + rfu_buf = 0x99660000 | (rfu_qrecv_broadcast_data_len << 8) | rfu_cmd; else rfu_buf = READ32LE(&ioMem[COMM_SIODATA32_L]); } } @@ -2460,7 +3650,7 @@ static void StartRFU(u16 value) switch (rfu_cmd) { case 0x16: - linkmem->rfu_bdata[vbaid][1 + rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); + linkmem->rfu_broadcastdata[vbaid][1 + rfu_counter++] = READ32LE(&ioMem[COMM_SIODATA32_L]); break; case 0x17: @@ -2490,7 +3680,7 @@ static void StartRFU(u16 value) case RFU_RECV: //data following after finalize cmd //if(rfu_qrecv==0) {rfu_state = RFU_COMM; break;} - if (--rfu_qrecv == 0) + if (--rfu_qrecv_broadcast_data_len == 0) rfu_state = RFU_COMM; switch (rfu_cmd) { @@ -2628,7 +3818,7 @@ bool LinkRFUUpdate() rfu_cmd = 0x36; if (READ32LE(&ioMem[COMM_SIODATA32_L]) == 0x80000000) - rfu_buf = 0x99660000 | (rfu_qrecv << 8) | rfu_cmd; + rfu_buf = 0x99660000 | (rfu_qrecv_broadcast_data_len << 8) | rfu_cmd; else rfu_buf = 0x80000000; } @@ -2666,7 +3856,7 @@ static void UpdateRFUIPC(int ticks) //UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & 0xff7f); UPDATE_REG(COMM_SIOCNT, (value & 0xff7f) | 0x0008); //Start bit.7 reset, SO bit.3 set automatically upon transfer completion? - //log("SIOn32 : %04X %04X %08X (VCOUNT = %d) %d %d\n", READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ32LE(&ioMem[COMM_SIODATA32_L]), VCOUNT, GetTickCount(), linktime2); + //log("SIOn32 : %04X %04X %08X (VCOUNT = %d) %d %d\n", READ16LE(&ioMem[COMM_RCNT]), READ16LE(&ioMem[COMM_SIOCNT]), READ32LE(&ioMem[COMM_SIODATA32_L]), VCOUNT); } return; } diff --git a/src/gba/GBALink.h b/src/gba/GBALink.h index 048dae37..cc0a3e41 100644 --- a/src/gba/GBALink.h +++ b/src/gba/GBALink.h @@ -10,6 +10,7 @@ enum LinkMode LINK_CABLE_IPC, LINK_CABLE_SOCKET, LINK_RFU_IPC, + LINK_RFU_SOCKET, LINK_GAMECUBE_DOLPHIN, LINK_GAMEBOY }; @@ -186,10 +187,7 @@ extern const char *MakeInstanceFilename(const char *Input); typedef struct { u8 len; //data len in 32bit words - u8 idx; //client idx u8 gbaid; //source id - u8 qid; //target ids - u32 sign; //signal u32 time; //linktime u32 data[255]; } rfu_datarec; diff --git a/src/win32/LinkOptions.cpp b/src/win32/LinkOptions.cpp index 5888943a..c927df48 100644 --- a/src/win32/LinkOptions.cpp +++ b/src/win32/LinkOptions.cpp @@ -73,6 +73,7 @@ BOOL LinkOptions::OnInitDialog(){ AddMode("Cable - Network", LINK_CABLE_SOCKET); AddMode("GameCube - Dolphin", LINK_GAMECUBE_DOLPHIN); AddMode("Wireless adapter - Single Computer", LINK_RFU_IPC); + AddMode("Wireless adapter - Network", LINK_RFU_SOCKET); AddMode("Game Link (Game Boy) - Single Computer", LINK_GAMEBOY); sprintf(timeout, "%d", theApp.linkTimeout); @@ -162,7 +163,7 @@ void LinkOptions::OnOk() return; } - bool needsServerHost = newMode == LINK_GAMECUBE_DOLPHIN || (newMode == LINK_CABLE_SOCKET && !m_server); + bool needsServerHost = newMode == LINK_GAMECUBE_DOLPHIN || (newMode == LINK_CABLE_SOCKET && !m_server) || (newMode == LINK_RFU_SOCKET && !m_server); if (needsServerHost) { bool valid = SetLinkServerHost(host); @@ -307,8 +308,8 @@ void LinkOptions::OnCbnSelchangeLinkMode() void LinkOptions::UpdateAvailability() { 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 isNetwork = (m_type == LINK_CABLE_SOCKET) || (m_type == LINK_RFU_SOCKET); + bool canHaveServer = (m_type == LINK_CABLE_SOCKET && !m_server) || (m_type == LINK_RFU_SOCKET && !m_server) || m_type == LINK_GAMECUBE_DOLPHIN; bool hasHacks = m_type == LINK_CABLE_SOCKET; GetDlgItem(IDC_LINK_CLIENT)->EnableWindow(isNetwork);