From a91f99d1116828f66b58438166a81d99991b6714 Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 3 Apr 2015 22:35:13 +0000 Subject: [PATCH] Initial refactor of the GBA link code. The code has been rearranged with the link framework at the top, followed by cable socket, joybus socket, cable IPC, RFU IPC and lastly, GB link IPC. --- src/gb/GB.cpp | 2 +- src/gba/GBALink.cpp | 2727 ++++++++++++++++++++++--------------------- src/gba/GBALink.h | 2 +- 3 files changed, 1368 insertions(+), 1363 deletions(-) diff --git a/src/gb/GB.cpp b/src/gb/GB.cpp index 914c3e03..b302edc7 100644 --- a/src/gb/GB.cpp +++ b/src/gb/GB.cpp @@ -2495,7 +2495,7 @@ void gbReset() #ifndef NO_LINK if (GetLinkMode() == LINK_GAMEBOY) { EmuReseted = true; - gbLinkReset(); + gbInitLink(); } #endif diff --git a/src/gba/GBALink.cpp b/src/gba/GBALink.cpp index 7ff67e2f..f1037688 100644 --- a/src/gba/GBALink.cpp +++ b/src/gba/GBALink.cpp @@ -14,6 +14,8 @@ #define snprintf _snprintf #endif +#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value) + static int vbaid = 0; const char *MakeInstanceFilename(const char *Input) { @@ -80,8 +82,6 @@ bool speedhack = true; #endif #define N_(x) x - - #if (defined __WIN32__ || defined _WIN32) #include #else @@ -201,65 +201,34 @@ int WaitForSingleObject(sem_t *s, int t) #define JOYBUS 4 #define GP 5 +static int GetSIOMode(u16, u16); +static ConnectionState InitSocket(); +static void StartCableSocket(u16 siocnt); +static ConnectionState ConnectUpdateSocket(char * const message, size_t size); +static void UpdateSocket(int ticks); +static void CloseSocket(); + +const u64 TICKS_PER_FRAME = 16777216 / 60; +const u64 TICKS_PER_SECOND = 16777216; +const u64 BITS_PER_SECOND = 115200; +const u64 BYTES_PER_SECOND = BITS_PER_SECOND / 8; + +static u32 lastjoybusupdate = 0; +static u32 nextjoybusupdate = 0; +static u32 lastcommand = 0; +static bool booted = false; + +static ConnectionState JoyBusConnect(); +static void JoyBusUpdate(int ticks); +static void JoyBusShutdown(); + +#if (defined __WIN32__ || defined _WIN32) + #define RFU_INIT 0 #define RFU_COMM 1 #define RFU_SEND 2 #define RFU_RECV 3 -static ConnectionState InitIPC(); -static ConnectionState InitSocket(); -static ConnectionState JoyBusConnect(); - -static void JoyBusShutdown(); -static void CloseIPC(); -static void CloseSocket(); - -static void StartCableSocket(u16 siocnt); -static void StartRFU(u16 siocnt); -static void StartCableIPC(u16 siocnt); - -static void JoyBusUpdate(int ticks); -static void UpdateCableIPC(int ticks); -static void UpdateRFUIPC(int ticks); -static void UpdateSocket(int ticks); - -static ConnectionState ConnectUpdateSocket(char * const message, size_t size); - -struct LinkDriver { - typedef ConnectionState (ConnectFunc)(); - typedef ConnectionState (ConnectUpdateFunc)(char * const message, size_t size); - typedef void (StartFunc)(u16 siocnt); - typedef void (UpdateFunc)(int ticks); - typedef void (CloseFunc)(); - - LinkMode mode; - ConnectFunc *connect; - ConnectUpdateFunc *connectUpdate; - StartFunc *start; - UpdateFunc *update; - CloseFunc *close; - bool uses_socket; -}; -static const LinkDriver linkDrivers[] = -{ - { LINK_CABLE_IPC, InitIPC, NULL, StartCableIPC, UpdateCableIPC, CloseIPC, false }, - { LINK_CABLE_SOCKET, InitSocket, ConnectUpdateSocket, StartCableSocket, UpdateSocket, CloseSocket, true }, - { LINK_RFU_IPC, InitIPC, NULL, StartRFU, UpdateRFUIPC, CloseIPC, false }, - { LINK_GAMECUBE_DOLPHIN, JoyBusConnect, NULL, NULL, JoyBusUpdate, JoyBusShutdown, false }, - { LINK_GAMEBOY, InitIPC, NULL, NULL, NULL, CloseIPC, false } -}; - - -enum -{ - JOY_CMD_RESET = 0xff, - JOY_CMD_STATUS = 0x00, - JOY_CMD_READ = 0x14, - JOY_CMD_WRITE = 0x15 -}; - -#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value) - typedef struct { u16 linkdata[5]; u16 linkcmd[4]; @@ -294,6 +263,87 @@ typedef struct { u32 rfu_timelist[5][256];*/ } LINKDATA; +// RFU crap (except for numtransfers note...should probably check that out) +static LINKDATA *linkmem = NULL; +static u8 rfu_cmd, rfu_qsend, rfu_qrecv; +static int rfu_state, rfu_polarity, rfu_counter, rfu_masterq; +// numtransfers seems to be used interchangeably with linkmem->numtransfers +// in rfu code; probably a bug? +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]; + +bool rfu_enabled = false; +bool rfu_initialized = false; +bool rfu_waiting = false; +u8 rfu_qsend2, rfu_cmd2, rfu_lastcmd, rfu_lastcmd2, rfu_lastcmd3; +u16 rfu_id, rfu_idx; +static int gbaid = 0; +static int gbaidx = 0; +bool rfu_ishost, rfu_isfirst, rfu_cansend; +DWORD rfu_lasttime; +u32 rfu_buf; +u16 PrevVAL = 0; +u32 PrevCOM = 0, PrevDAT = 0; +u8 rfu_numclients = 0; +u8 rfu_curclient = 0; +u32 rfu_clientlist[5]; + +static ConnectionState InitIPC(); +static void StartCableIPC(u16 siocnt); +static void ReconnectCableIPC(); +static void UpdateCableIPC(int ticks); +static void StartRFU(u16 siocnt); +static void UpdateRFUIPC(int ticks); +static void CloseIPC(); + +#endif + +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; + bool uses_socket; +}; + +static const LinkDriver *linkDriver = NULL; +static ConnectionState gba_connection_state = LINK_OK; + +static int linktime = 0; + +static GBASockClient* dol = NULL; +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 }, +#endif + { LINK_CABLE_SOCKET, InitSocket, ConnectUpdateSocket, StartCableSocket, UpdateSocket, CloseSocket, true }, + { LINK_GAMECUBE_DOLPHIN, JoyBusConnect, NULL, NULL, JoyBusUpdate, JoyBusShutdown, false } +}; + +enum +{ + JOY_CMD_RESET = 0xff, + JOY_CMD_STATUS = 0x00, + JOY_CMD_READ = 0x14, + JOY_CMD_WRITE = 0x15 +}; + typedef struct { sf::SocketTCP tcpsocket; int numslaves; @@ -341,25 +391,39 @@ public: void CheckConn(void); }; -static const LinkDriver *linkDriver = NULL; -static ConnectionState gba_connection_state = LINK_OK; +static int i, j; +static int linktimeout = 1000; +static LANLINKDATA lanlink; +static u16 linkdata[4]; +static lserver ls; +static lclient lc; +static bool oncewait = false, after = false; -LinkMode GetLinkMode() { - if (linkDriver && gba_connection_state == LINK_OK) - return linkDriver->mode; - else - return LINK_DISCONNECTED; -} +// time to end of single GBA's transfer, in 16.78 MHz clock ticks +// first index is GBA # +static const int trtimedata[4][4] = { + // 9600 38400 57600 115200 + { 34080, 8520, 5680, 2840 }, + { 65536, 16384, 10923, 5461 }, + { 99609, 24903, 16602, 8301 }, + { 133692, 33423, 22282, 11141 } +}; -static int linktime = 0; - -static GBASockClient* dol = NULL; -static sf::IPAddress joybusHostAddr = sf::IPAddress::LocalHost; +// time to end of transfer +// for 3 slaves, this is time to transfer machine 4 +// for < 3 slaves, this is time to transfer last machine + time to detect lack +// of start bit from next slave +// first index is (# of slaves) - 1 +static const int trtimeend[3][4] = { + // 9600 38400 57600 115200 + { 72527, 18132, 12088, 6044 }, + { 106608, 26652, 17768, 8884 }, + { 133692, 33423, 22282, 11141 } +}; // Hodgepodge static u8 tspeed = 3; static u8 transfer = 0; -static LINKDATA *linkmem = NULL; static int linkid = 0; #if (defined __WIN32__ || defined _WIN32) static HANDLE linksync[4]; @@ -377,69 +441,1204 @@ static char linkevent[] = "/" #endif "VBA link event "; -static int i, j; -static int linktimeout = 1000; -static LANLINKDATA lanlink; -static u16 linkdata[4]; -static lserver ls; -static lclient lc; -static bool oncewait = false, after = false; -// RFU crap (except for numtransfers note...should probably check that out) -static u8 rfu_cmd, rfu_qsend, rfu_qrecv; -static int rfu_state, rfu_polarity, rfu_counter, rfu_masterq; -// numtransfers seems to be used interchangeably with linkmem->numtransfers -// in rfu code; probably a bug? -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]; +inline static int GetSIOMode(u16 siocnt, u16 rcnt) +{ + if (!(rcnt & 0x8000)) + { + switch (siocnt & 0x3000) { + case 0x0000: return NORMAL8; + case 0x1000: return NORMAL32; + case 0x2000: return MULTIPLAYER; + case 0x3000: return UART; + } + } -bool rfu_enabled = false; -bool rfu_initialized = false; -bool rfu_waiting = false; -u8 rfu_qsend2, rfu_cmd2, rfu_lastcmd, rfu_lastcmd2, rfu_lastcmd3; -u16 rfu_id, rfu_idx; -static int gbaid = 0; -static int gbaidx = 0; -bool rfu_ishost, rfu_isfirst, rfu_cansend; -DWORD rfu_lasttime; -u32 rfu_buf; -u16 PrevVAL = 0; -u32 PrevCOM = 0, PrevDAT = 0; -u8 rfu_numclients = 0; -u8 rfu_curclient = 0; -u32 rfu_clientlist[5]; + if (rcnt & 0x4000) + return JOYBUS; -// time to end of single GBA's transfer, in 16.78 MHz clock ticks -// first index is GBA # -static const int trtimedata[4][4] = { - // 9600 38400 57600 115200 - {34080, 8520, 5680, 2840}, - {65536, 16384, 10923, 5461}, - {99609, 24903, 16602, 8301}, - {133692, 33423, 22282, 11141} -}; + return GP; +} -// time to end of transfer -// for 3 slaves, this is time to transfer machine 4 -// for < 3 slaves, this is time to transfer last machine + time to detect lack -// of start bit from next slave -// first index is (# of slaves) - 1 -static const int trtimeend[3][4] = { - // 9600 38400 57600 115200 - {72527, 18132, 12088, 6044}, - {106608, 26652, 17768, 8884}, - {133692, 33423, 22282, 11141} -}; +LinkMode GetLinkMode() { + if (linkDriver && gba_connection_state == LINK_OK) + return linkDriver->mode; + else + return LINK_DISCONNECTED; +} -static int GetSIOMode(u16, u16); +void GetLinkServerHost(char * const host, size_t size) { + if (host == NULL || size == 0) + return; -u8 gbSIO_SC = 0; -bool LinkIsWaiting = false; -bool LinkFirstTime = true; -bool EmuReseted = true; + host[0] = '\0'; + + if (linkDriver && linkDriver->mode == LINK_GAMECUBE_DOLPHIN) + strncpy(host, joybusHostAddr.ToString().c_str(), size); + else if (lanlink.server) + strncpy(host, sf::IPAddress::GetLocalAddress().ToString().c_str(), size); + else + strncpy(host, lc.serveraddr.ToString().c_str(), size); +} + +bool SetLinkServerHost(const char *host) { + sf::IPAddress addr = sf::IPAddress(host); + + lc.serveraddr = addr; + joybusHostAddr = addr; + + return addr.IsValid(); +} + +int GetLinkPlayerId() { + if (GetLinkMode() == LINK_DISCONNECTED) { + return -1; + } else if (linkid > 0) { + return linkid; + } else { + return vbaid; + } +} + +void SetLinkTimeout(int value) { + linktimeout = value; +} + +void EnableLinkServer(bool enable, int numSlaves) { + lanlink.server = enable; + lanlink.numslaves = numSlaves; +} + +void EnableSpeedHacks(bool enable) { + lanlink.speed = enable; +} + +void BootLink(int m_type, const char *hostAddr, int timeout, bool m_hacks, int m_numplayers) +{ + if (linkDriver) { + // Connection has already been established + return; + } + + LinkMode mode = (LinkMode)m_type; + + if (mode == LINK_DISCONNECTED || mode == LINK_CABLE_SOCKET) { + return; + } + + // Close any previous link + CloseLink(); + + bool needsServerHost = (mode == LINK_GAMECUBE_DOLPHIN); + + if (needsServerHost) { + bool valid = SetLinkServerHost(hostAddr); + if (!valid) { + return; + } + } + + SetLinkTimeout(timeout); + EnableSpeedHacks(m_hacks); + + // Init link + ConnectionState state = InitLink(mode); + + if (!linkDriver->uses_socket) + { + // The user canceled the connection attempt + if (state == LINK_ABORT) { + CloseLink(); + return; + } + + // Something failed during init + if (state == LINK_ERROR) { + return; + } + } + else + { + CloseLink(); + return; + } +} + + +////////////////////////////////////////////////////////////////////////// +// Probably from here down needs to be replaced with SFML goodness :) +// tjm: what SFML goodness? SFML for network, yes, but not for IPC + +ConnectionState InitLink(LinkMode mode) +{ + if (mode == LINK_DISCONNECTED) + return LINK_ABORT; + + // Do nothing if we are already connected + if (GetLinkMode() != LINK_DISCONNECTED) { + systemMessage(0, N_("Error, link already connected")); + return LINK_ERROR; + } + + // Find the link driver + linkDriver = NULL; + for (u8 i = 0; i < sizeof(linkDrivers) / sizeof(linkDrivers[0]); i++) { + if (linkDrivers[i].mode == mode) { + linkDriver = &linkDrivers[i]; + break; + } + } + + if (!linkDriver || !linkDriver->connect) { + systemMessage(0, N_("Unable to find link driver")); + return LINK_ERROR; + } + + // Connect the link + gba_connection_state = linkDriver->connect(); + + if (gba_connection_state == LINK_ERROR) { + CloseLink(); + } + + return gba_connection_state; +} + +void StartLink(u16 siocnt) +{ + if (!linkDriver || !linkDriver->start) { + return; + } + + linkDriver->start(siocnt); +} + +ConnectionState ConnectLinkUpdate(char * const message, size_t size) +{ + message[0] = '\0'; + + if (!linkDriver || !linkDriver->connectUpdate || gba_connection_state != LINK_NEEDS_UPDATE) { + gba_connection_state = LINK_ERROR; + snprintf(message, size, N_("Link connection does not need updates.")); + + return LINK_ERROR; + } + + gba_connection_state = linkDriver->connectUpdate(message, size); + + return gba_connection_state; +} + +void StartGPLink(u16 value) +{ + UPDATE_REG(COMM_RCNT, value); + + if (!value) + return; + + switch (GetSIOMode(READ16LE(&ioMem[COMM_SIOCNT]), value)) { + case MULTIPLAYER: + value &= 0xc0f0; + value |= 3; + if (linkid) + value |= 4; + UPDATE_REG(COMM_SIOCNT, ((READ16LE(&ioMem[COMM_SIOCNT])&0xff8b)|(linkid ? 0xc : 8)|(linkid<<4))); + break; + + case GP: + if (GetLinkMode() == LINK_RFU_IPC) + rfu_state = RFU_INIT; + break; + } +} + +void LinkUpdate(int ticks) +{ + if (!linkDriver || !linkDriver->update) { + return; + } + + // this actually gets called every single instruction, so keep default + // path as short as possible + + linktime += ticks; + + linkDriver->update(ticks); +} + +void CheckLinkConnection() { + if (GetLinkMode() == LINK_CABLE_SOCKET) { + if (linkid && lc.numtransfers == 0) { + lc.CheckConn(); + } + } +} + +void CloseLink(void) { + if (!linkDriver || !linkDriver->close) { + return; // Nothing to do + } + + linkDriver->close(); + linkDriver = NULL; + + return; +} + +// Server +lserver::lserver(void) { + intinbuffer = (s32*)inbuffer; + u16inbuffer = (u16*)inbuffer; + intoutbuffer = (s32*)outbuffer; + u16outbuffer = (u16*)outbuffer; + oncewait = false; +} + +void lserver::Send(void) { + if(lanlink.type==0) { // TCP + if(savedlinktime==-1) { + outbuffer[0] = 4; + outbuffer[1] = -32; //0xe0 + for(i=1;i<=lanlink.numslaves;i++) { + tcpsocket[i].Send(outbuffer, 4); + size_t nr; + tcpsocket[i].Receive(inbuffer, 4, nr); + } + } + outbuffer[1] = tspeed; + WRITE16LE(&u16outbuffer[1], linkdata[0]); + WRITE32LE(&intoutbuffer[1], savedlinktime); + if(lanlink.numslaves==1) { + if(lanlink.type==0) { + outbuffer[0] = 8; + tcpsocket[1].Send(outbuffer, 8); + } + } + else if(lanlink.numslaves==2) { + WRITE16LE(&u16outbuffer[4], linkdata[2]); + if(lanlink.type==0) { + outbuffer[0] = 10; + tcpsocket[1].Send(outbuffer, 10); + WRITE16LE(&u16outbuffer[4], linkdata[1]); + tcpsocket[2].Send(outbuffer, 10); + } + } else { + if(lanlink.type==0) { + outbuffer[0] = 12; + WRITE16LE(&u16outbuffer[4], linkdata[2]); + WRITE16LE(&u16outbuffer[5], linkdata[3]); + tcpsocket[1].Send(outbuffer, 12); + WRITE16LE(&u16outbuffer[4], linkdata[1]); + tcpsocket[2].Send(outbuffer, 12); + WRITE16LE(&u16outbuffer[5], linkdata[2]); + tcpsocket[3].Send(outbuffer, 12); + } + } + } + return; +} + +void lserver::Recv(void) { + int numbytes; + if(lanlink.type==0) { // TCP + fdset.Clear(); + for(i=0;i1) memmove(inbuffer, inbuffer+inbuffer[0]*(howmanytimes-1), inbuffer[0]); + if(inbuffer[1]==-32) { + char message[30]; + sprintf(message, _("Player %d disconnected."), i+2); + systemScreenMessage(message); + outbuffer[0] = 4; + outbuffer[1] = -32; + for(i=1;i0) { + while(numbytesShowServerIP(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; + } + } +} + +static ConnectionState ConnectUpdateSocket(char * const message, size_t size) { + ConnectionState newState = LINK_NEEDS_UPDATE; + + if (lanlink.server) { + sf::Selector fdset; + fdset.Add(lanlink.tcpsocket); + + if (fdset.Wait(0.1) == 1) { + int nextSlave = lanlink.connectedSlaves + 1; + + sf::Socket::Status st = lanlink.tcpsocket.Accept(ls.tcpsocket[nextSlave]); + + if (st == sf::Socket::Error) { + for (int j = 1; j < nextSlave; j++) + ls.tcpsocket[j].Close(); + + snprintf(message, size, N_("Network error.")); + newState = LINK_ERROR; + } else { + sf::Packet packet; + packet << static_cast(nextSlave) + << static_cast(lanlink.numslaves); + + ls.tcpsocket[nextSlave].Send(packet); + + snprintf(message, size, N_("Player %d connected"), nextSlave); + + lanlink.connectedSlaves++; + } + } + + if (lanlink.numslaves == lanlink.connectedSlaves) { + for (int i = 1; i <= lanlink.numslaves; i++) { + sf::Packet packet; + packet << true; + + ls.tcpsocket[i].Send(packet); + } + + snprintf(message, size, N_("All players connected")); + newState = LINK_OK; + } + } else { + + sf::Packet packet; + sf::Socket::Status status = lanlink.tcpsocket.Receive(packet); + + if (status == sf::Socket::Error || status == sf::Socket::Disconnected) { + snprintf(message, size, N_("Network error.")); + newState = LINK_ERROR; + } else if (status == sf::Socket::Done) { + + if (linkid == 0) { + sf::Uint16 receivedId, receivedSlaves; + packet >> receivedId >> receivedSlaves; + + if (packet) { + linkid = receivedId; + lanlink.numslaves = receivedSlaves; + + snprintf(message, size, N_("Connected as #%d, Waiting for %d players to join"), + linkid + 1, lanlink.numslaves - linkid); + } + } else { + bool gameReady; + packet >> gameReady; + + if (packet && gameReady) { + newState = LINK_OK; + snprintf(message, size, N_("All players joined.")); + } + } + + sf::Selector fdset; + fdset.Add(lanlink.tcpsocket); + fdset.Wait(0.1); + } + } + + return newState; +} + +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 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; + } +} + +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(); +} + +// call this to clean up crashed program's shared state +// or to use TCP on same machine (for testing) +// this may be necessary under MSW as well, but I wouldn't know how +void CleanLocalLink() +{ +#if !(defined __WIN32__ || defined _WIN32) + shm_unlink("/" LOCAL_LINK_NAME); + for(int i = 0; i < 4; i++) { + linkevent[sizeof(linkevent) - 2] = '1' + i; + sem_unlink(linkevent); + } +#endif +} + +static ConnectionState JoyBusConnect() +{ + delete dol; + dol = NULL; + + dol = new GBASockClient(joybusHostAddr); + if (dol) { + return LINK_OK; + } + else { + return LINK_ERROR; + } +} + +static void JoyBusUpdate(int ticks) +{ + lastjoybusupdate += ticks; + lastcommand += ticks; + + bool joybus_activated = ((READ16LE(&ioMem[COMM_RCNT])) >> 14) == 3; + gba_joybus_active = dol && gba_joybus_enabled && joybus_activated; + + if ((lastjoybusupdate > nextjoybusupdate)) + { + if (!joybus_activated) + { + if (dol && booted) + { + JoyBusShutdown(); + } + + lastjoybusupdate = 0; + nextjoybusupdate = 0; + lastcommand = 0; + return; + } + + if (!dol) + { + booted = false; + JoyBusConnect(); + } + + dol->ReceiveClock(false); + + if (dol->IsDisconnected()) + { + JoyBusShutdown(); + nextjoybusupdate = TICKS_PER_SECOND * 2; // try to connect after 2 seconds + lastjoybusupdate = 0; + lastcommand = 0; + return; + } + + dol->ClockSync(lastjoybusupdate); + + char data[5] = { 0x10, 0, 0, 0, 0 }; // init with invalid cmd + std::vector resp; + u8 cmd = 0x10; + + if (lastcommand > (TICKS_PER_FRAME * 4)) + { + cmd = dol->ReceiveCmd(data, true); + } + else + { + cmd = dol->ReceiveCmd(data, false); + } + + switch (cmd) { + case JOY_CMD_RESET: + UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RESET); + resp.push_back(0x00); // GBA device ID + resp.push_back(0x04); + nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND; + break; + + case JOY_CMD_STATUS: + resp.push_back(0x00); // GBA device ID + resp.push_back(0x04); + + nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND; + break; + + case JOY_CMD_READ: + resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_L]) & 0xff)); + resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_L]) >> 8)); + resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) & 0xff)); + resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) >> 8)); + + UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_SEND_COMPLETE); + nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND; + booted = true; + break; + + case JOY_CMD_WRITE: + UPDATE_REG(COMM_JOY_RECV_L, (u16)((u16)data[2] << 8) | (u8)data[1]); + UPDATE_REG(COMM_JOY_RECV_H, (u16)((u16)data[4] << 8) | (u8)data[3]); + UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_RECV); + UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RECV_COMPLETE); + nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND; + booted = true; + break; + + default: + nextjoybusupdate = TICKS_PER_SECOND / 40000; + lastjoybusupdate = 0; + return; // ignore + } + + lastjoybusupdate = 0; + resp.push_back((u8)READ16LE(&ioMem[COMM_JOYSTAT])); + + if (cmd == JOY_CMD_READ) + { + UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) & ~JOYSTAT_SEND); + } + + dol->Send(resp); + + // Generate SIO interrupt if we can + if (((cmd == JOY_CMD_RESET) || (cmd == JOY_CMD_READ) || (cmd == JOY_CMD_WRITE)) + && (READ16LE(&ioMem[COMM_JOYCNT]) & JOYCNT_INT_ENABLE) ) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + + lastcommand = 0; + } +} + +static void JoyBusShutdown() +{ + delete dol; + dol = NULL; +} + +#if (defined __WIN32__ || defined _WIN32) + +static ConnectionState InitIPC() { + linkid = 0; + +#if (defined __WIN32__ || defined _WIN32) + if((mmf=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LINKDATA), LOCAL_LINK_NAME))==NULL) { + systemMessage(0, N_("Error creating file mapping")); + return LINK_ERROR; + } + + if(GetLastError() == ERROR_ALREADY_EXISTS) + vbaid = 1; + else + vbaid = 0; + + + if((linkmem=(LINKDATA *)MapViewOfFile(mmf, FILE_MAP_WRITE, 0, 0, sizeof(LINKDATA)))==NULL) { + CloseHandle(mmf); + systemMessage(0, N_("Error mapping file")); + return LINK_ERROR; + } +#else + if((mmf = shm_open("/" LOCAL_LINK_NAME, O_RDWR|O_CREAT|O_EXCL, 0777)) < 0) { + vbaid = 1; + mmf = shm_open("/" LOCAL_LINK_NAME, O_RDWR, 0); + } else + vbaid = 0; + if(mmf < 0 || ftruncate(mmf, sizeof(LINKDATA)) < 0 || + !(linkmem = (LINKDATA *)mmap(NULL, sizeof(LINKDATA), + PROT_READ|PROT_WRITE, MAP_SHARED, + mmf, 0))) { + systemMessage(0, N_("Error creating file mapping")); + if(mmf) { + if(!vbaid) + shm_unlink("/" LOCAL_LINK_NAME); + close(mmf); + } + } +#endif + + // get lowest-numbered available machine slot + bool firstone = !vbaid; + if(firstone) { + linkmem->linkflags = 1; + linkmem->numgbas = 1; + linkmem->numtransfers=0; + for(i=0;i<4;i++) + linkmem->linkdata[i] = 0xffff; + } else { + // FIXME: this should be done while linkmem is locked + // (no xfer in progress, no other vba trying to connect) + int n = linkmem->numgbas; + int f = linkmem->linkflags; + for(int i = 0; i <= n; i++) + if(!(f & (1 << i))) { + vbaid = i; + break; + } + if(vbaid == 4) { +#if (defined __WIN32__ || defined _WIN32) + UnmapViewOfFile(linkmem); + CloseHandle(mmf); +#else + munmap(linkmem, sizeof(LINKDATA)); + if(!vbaid) + shm_unlink("/" LOCAL_LINK_NAME); + close(mmf); +#endif + systemMessage(0, N_("5 or more GBAs not supported.")); + return LINK_ERROR; + } + if(vbaid == n) + linkmem->numgbas = n + 1; + linkmem->linkflags = f | (1 << vbaid); + } + linkid = vbaid; + + for(i=0;i<4;i++) { + linkevent[sizeof(linkevent)-2]=(char)i+'1'; +#if (defined __WIN32__ || defined _WIN32) + linksync[i] = firstone ? + CreateSemaphore(NULL, 0, 4, linkevent) : + OpenSemaphore(SEMAPHORE_ALL_ACCESS, false, linkevent); + if(linksync[i] == NULL) { + UnmapViewOfFile(linkmem); + CloseHandle(mmf); + for(j=0;jnumgbas > 1) + { + // find first active attached GBA + // doing this first reduces the potential + // race window size for new connections + int n = linkmem->numgbas + 1; + int f = linkmem->linkflags; + int m; + do { + n--; + m = (1 << n) - 1; + } while((f & m) != m); + linkmem->trgbas = n; + + // before starting xfer, make pathetic attempt + // at clearing out any previous stuck xfer + // this will fail if a slave was stuck for + // too long + for(int i = 0; i < 4; i++) + while(WaitForSingleObject(linksync[i], 0) != WAIT_TIMEOUT); + + // transmit first value + linkmem->linkcmd[0] = ('M' << 8) + (value & 3); + linkmem->linkdata[0] = READ16LE(&ioMem[COMM_SIODATA8]); + + // start up slaves & sync clocks + numtransfers = linkmem->numtransfers; + if (numtransfers != 0) + linkmem->lastlinktime = linktime; + else + linkmem->lastlinktime = 0; + + if ((++numtransfers) == 0) + linkmem->numtransfers = 2; + else + linkmem->numtransfers = numtransfers; + + transfer = 1; + linktime = 0; + tspeed = value & 3; + WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); + WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); + value &= ~0x40; + } else { + value |= 0x40; // comm error + } + } + 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; + } +} + +static void ReconnectCableIPC() +{ + int f = linkmem->linkflags; + int n = linkmem->numgbas; + if(f & (1 << linkid)) { + systemMessage(0, N_("Lost link; reinitialize to reconnect")); + return; + } + linkmem->linkflags |= 1 << linkid; + if(n < linkid + 1) + linkmem->numgbas = linkid + 1; + numtransfers = linkmem->numtransfers; + systemScreenMessage(_("Lost link; reconnected")); +} + +static void UpdateCableIPC(int ticks) +{ + if (((READ16LE(&ioMem[COMM_RCNT])) >> 14) == 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 + // to integer overflow + if(!transfer && numtransfers && linktime < 0) { + linktime = 0; + // there is a very, very, small chance that this will abort + // a transfer that was just started + linkmem->numtransfers = numtransfers = 0; + } + if (linkid && !transfer && linktime >= linkmem->lastlinktime && + linkmem->numtransfers != numtransfers) + { + numtransfers = linkmem->numtransfers; + if(!numtransfers) + return; + + // if this or any previous machine was dropped, no transfer + // can take place + if(linkmem->trgbas <= linkid) { + transfer = 0; + numtransfers = 0; + // if this is the one that was dropped, reconnect + if(!(linkmem->linkflags & (1 << linkid))) + ReconnectCableIPC(); + return; + } + + // sync clock + if (numtransfers == 1) + linktime = 0; + else + linktime -= linkmem->lastlinktime; + + // there's really no point to this switch; 'M' is the only + // possible command. +#if 0 + switch ((linkmem->linkcmd) >> 8) + { + case 'M': +#endif + tspeed = linkmem->linkcmd[0] & 3; + transfer = 1; + WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); + WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & ~0x40 | 0x80); +#if 0 + break; + } +#endif + } + + if (!transfer) + return; + + if (transfer <= linkmem->trgbas && linktime >= trtimedata[transfer-1][tspeed]) + { + // transfer #n -> wait for value n - 1 + if(transfer > 1 && linkid != transfer - 1) { + if(WaitForSingleObject(linksync[transfer - 1], linktimeout) == WAIT_TIMEOUT) { + // assume slave has dropped off if timed out + if(!linkid) { + linkmem->trgbas = transfer - 1; + int f = linkmem->linkflags; + f &= ~(1 << (transfer - 1)); + linkmem->linkflags = f; + if(f < (1 << transfer) - 1) + linkmem->numgbas = transfer - 1; + char message[30]; + sprintf(message, _("Player %d disconnected."), transfer - 1); + systemScreenMessage(message); + } + transfer = linkmem->trgbas + 1; + // next cycle, transfer will finish up + return; + } + } + // now that value is available, store it + UPDATE_REG((COMM_SIOMULTI0 - 2) + (transfer<<1), linkmem->linkdata[transfer-1]); + + // transfer machine's value at start of its transfer cycle + if(linkid == transfer) { + // skip if dropped + if(linkmem->trgbas <= linkid) { + transfer = 0; + numtransfers = 0; + // if this is the one that was dropped, reconnect + if(!(linkmem->linkflags & (1 << linkid))) + ReconnectCableIPC(); + return; + } + // SI becomes low + UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & ~4); + UPDATE_REG(COMM_RCNT, 10); + linkmem->linkdata[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); + ReleaseSemaphore(linksync[linkid], linkmem->numgbas-1, NULL); + } + if(linkid == transfer - 1) { + // SO becomes low to begin next trasnfer + // may need to set DDR as well + UPDATE_REG(COMM_RCNT, 0x22); + } + + // next cycle + transfer++; + } + + if (transfer > linkmem->trgbas && linktime >= trtimeend[transfer-3][tspeed]) + { + // wait for slaves to finish + // this keeps unfinished slaves from screwing up last xfer + // not strictly necessary; may just slow things down + if(!linkid) { + for(int i = 2; i < transfer; i++) + if(WaitForSingleObject(linksync[0], linktimeout) == WAIT_TIMEOUT) { + // impossible to determine which slave died + // so leave them alone for now + systemScreenMessage(_("Unknown slave timed out; resetting comm")); + linkmem->numtransfers = numtransfers = 0; + break; + } + } else if(linkmem->trgbas > linkid) + // signal master that this slave is finished + ReleaseSemaphore(linksync[0], 1, NULL); + linktime -= trtimeend[transfer - 3][tspeed]; + transfer = 0; + u16 value = READ16LE(&ioMem[COMM_SIOCNT]); + if(!linkid) + value |= 4; // SI becomes high on slaves after xfer + UPDATE_REG(COMM_SIOCNT, (value & 0xff0f) | (linkid << 4)); + // SC/SI high after transfer + UPDATE_REG(COMM_RCNT, linkid ? 15 : 11); + if (value & 0x4000) + { + IF |= 0x80; + UPDATE_REG(0x202, IF); + } + } +} // The GBA wireless RFU (see adapter3.txt) static void StartRFU(u16 value) @@ -1412,472 +2611,6 @@ static void StartRFU(u16 value) UPDATE_REG(COMM_SIOCNT, value); } -static void StartCableIPC(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) { - if (linkmem->numgbas > 1) - { - // find first active attached GBA - // doing this first reduces the potential - // race window size for new connections - int n = linkmem->numgbas + 1; - int f = linkmem->linkflags; - int m; - do { - n--; - m = (1 << n) - 1; - } while((f & m) != m); - linkmem->trgbas = n; - - // before starting xfer, make pathetic attempt - // at clearing out any previous stuck xfer - // this will fail if a slave was stuck for - // too long - for(int i = 0; i < 4; i++) - while(WaitForSingleObject(linksync[i], 0) != WAIT_TIMEOUT); - - // transmit first value - linkmem->linkcmd[0] = ('M' << 8) + (value & 3); - linkmem->linkdata[0] = READ16LE(&ioMem[COMM_SIODATA8]); - - // start up slaves & sync clocks - numtransfers = linkmem->numtransfers; - if (numtransfers != 0) - linkmem->lastlinktime = linktime; - else - linkmem->lastlinktime = 0; - - if ((++numtransfers) == 0) - linkmem->numtransfers = 2; - else - linkmem->numtransfers = numtransfers; - - transfer = 1; - linktime = 0; - tspeed = value & 3; - WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); - WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); - value &= ~0x40; - } else { - value |= 0x40; // comm error - } - } - 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 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); - - if (!value) - return; - - switch (GetSIOMode(READ16LE(&ioMem[COMM_SIOCNT]), value)) { - case MULTIPLAYER: - value &= 0xc0f0; - value |= 3; - if (linkid) - value |= 4; - UPDATE_REG(COMM_SIOCNT, ((READ16LE(&ioMem[COMM_SIOCNT])&0xff8b)|(linkid ? 0xc : 8)|(linkid<<4))); - break; - - case GP: - if (GetLinkMode() == LINK_RFU_IPC) - rfu_state = RFU_INIT; - break; - } -} - -static ConnectionState JoyBusConnect() -{ - delete dol; - dol = NULL; - - dol = new GBASockClient(joybusHostAddr); - if (dol) { - return LINK_OK; - } - else { - return LINK_ERROR; - } -} - -static void JoyBusShutdown() -{ - delete dol; - dol = NULL; -} - -const u64 TICKS_PER_FRAME = 16777216 / 60; -const u64 TICKS_PER_SECOND = 16777216; -const u64 BITS_PER_SECOND = 115200; -const u64 BYTES_PER_SECOND = BITS_PER_SECOND / 8; - -static u32 lastjoybusupdate = 0; -static u32 nextjoybusupdate = 0; -static u32 lastcommand = 0; -static bool booted = false; - -static void JoyBusUpdate(int ticks) -{ - lastjoybusupdate += ticks; - lastcommand += ticks; - - bool joybus_activated = ((READ16LE(&ioMem[COMM_RCNT])) >> 14) == 3; - gba_joybus_active = dol && gba_joybus_enabled && joybus_activated; - - if ((lastjoybusupdate > nextjoybusupdate)) - { - if (!joybus_activated) - { - if (dol && booted) - { - JoyBusShutdown(); - } - - lastjoybusupdate = 0; - nextjoybusupdate = 0; - lastcommand = 0; - return; - } - - if (!dol) - { - booted = false; - JoyBusConnect(); - } - - dol->ReceiveClock(false); - - if (dol->IsDisconnected()) - { - JoyBusShutdown(); - nextjoybusupdate = TICKS_PER_SECOND * 2; // try to connect after 2 seconds - lastjoybusupdate = 0; - lastcommand = 0; - return; - } - - dol->ClockSync(lastjoybusupdate); - - char data[5] = { 0x10, 0, 0, 0, 0 }; // init with invalid cmd - std::vector resp; - u8 cmd = 0x10; - - if (lastcommand > (TICKS_PER_FRAME * 4)) - { - cmd = dol->ReceiveCmd(data, true); - } - else - { - cmd = dol->ReceiveCmd(data, false); - } - - switch (cmd) { - case JOY_CMD_RESET: - UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RESET); - resp.push_back(0x00); // GBA device ID - resp.push_back(0x04); - nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND; - break; - - case JOY_CMD_STATUS: - resp.push_back(0x00); // GBA device ID - resp.push_back(0x04); - - nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND; - break; - - case JOY_CMD_READ: - resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_L]) & 0xff)); - resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_L]) >> 8)); - resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) & 0xff)); - resp.push_back((u8)(READ16LE(&ioMem[COMM_JOY_TRANS_H]) >> 8)); - - UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_SEND_COMPLETE); - nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND; - booted = true; - break; - - case JOY_CMD_WRITE: - UPDATE_REG(COMM_JOY_RECV_L, (u16)((u16)data[2] << 8) | (u8)data[1]); - UPDATE_REG(COMM_JOY_RECV_H, (u16)((u16)data[4] << 8) | (u8)data[3]); - UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) | JOYSTAT_RECV); - UPDATE_REG(COMM_JOYCNT, READ16LE(&ioMem[COMM_JOYCNT]) | JOYCNT_RECV_COMPLETE); - nextjoybusupdate = TICKS_PER_SECOND / BYTES_PER_SECOND; - booted = true; - break; - - default: - nextjoybusupdate = TICKS_PER_SECOND / 40000; - lastjoybusupdate = 0; - return; // ignore - } - - lastjoybusupdate = 0; - resp.push_back((u8)READ16LE(&ioMem[COMM_JOYSTAT])); - - if (cmd == JOY_CMD_READ) - { - UPDATE_REG(COMM_JOYSTAT, READ16LE(&ioMem[COMM_JOYSTAT]) & ~JOYSTAT_SEND); - } - - dol->Send(resp); - - // Generate SIO interrupt if we can - if (((cmd == JOY_CMD_RESET) || (cmd == JOY_CMD_READ) || (cmd == JOY_CMD_WRITE)) - && (READ16LE(&ioMem[COMM_JOYCNT]) & JOYCNT_INT_ENABLE) ) - { - IF |= 0x80; - UPDATE_REG(0x202, IF); - } - - lastcommand = 0; - } -} - -static void ReInitLink(); - -static void UpdateCableIPC(int ticks) -{ - if (((READ16LE(&ioMem[COMM_RCNT])) >> 14) == 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 - // to integer overflow - if(!transfer && numtransfers && linktime < 0) { - linktime = 0; - // there is a very, very, small chance that this will abort - // a transfer that was just started - linkmem->numtransfers = numtransfers = 0; - } - if (linkid && !transfer && linktime >= linkmem->lastlinktime && - linkmem->numtransfers != numtransfers) - { - numtransfers = linkmem->numtransfers; - if(!numtransfers) - return; - - // if this or any previous machine was dropped, no transfer - // can take place - if(linkmem->trgbas <= linkid) { - transfer = 0; - numtransfers = 0; - // if this is the one that was dropped, reconnect - if(!(linkmem->linkflags & (1 << linkid))) - ReInitLink(); - return; - } - - // sync clock - if (numtransfers == 1) - linktime = 0; - else - linktime -= linkmem->lastlinktime; - - // there's really no point to this switch; 'M' is the only - // possible command. -#if 0 - switch ((linkmem->linkcmd) >> 8) - { - case 'M': -#endif - tspeed = linkmem->linkcmd[0] & 3; - transfer = 1; - WRITE32LE(&ioMem[COMM_SIOMULTI0], 0xffffffff); - WRITE32LE(&ioMem[COMM_SIOMULTI2], 0xffffffff); - UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & ~0x40 | 0x80); -#if 0 - break; - } -#endif - } - - if (!transfer) - return; - - if (transfer <= linkmem->trgbas && linktime >= trtimedata[transfer-1][tspeed]) - { - // transfer #n -> wait for value n - 1 - if(transfer > 1 && linkid != transfer - 1) { - if(WaitForSingleObject(linksync[transfer - 1], linktimeout) == WAIT_TIMEOUT) { - // assume slave has dropped off if timed out - if(!linkid) { - linkmem->trgbas = transfer - 1; - int f = linkmem->linkflags; - f &= ~(1 << (transfer - 1)); - linkmem->linkflags = f; - if(f < (1 << transfer) - 1) - linkmem->numgbas = transfer - 1; - char message[30]; - sprintf(message, _("Player %d disconnected."), transfer - 1); - systemScreenMessage(message); - } - transfer = linkmem->trgbas + 1; - // next cycle, transfer will finish up - return; - } - } - // now that value is available, store it - UPDATE_REG((COMM_SIOMULTI0 - 2) + (transfer<<1), linkmem->linkdata[transfer-1]); - - // transfer machine's value at start of its transfer cycle - if(linkid == transfer) { - // skip if dropped - if(linkmem->trgbas <= linkid) { - transfer = 0; - numtransfers = 0; - // if this is the one that was dropped, reconnect - if(!(linkmem->linkflags & (1 << linkid))) - ReInitLink(); - return; - } - // SI becomes low - UPDATE_REG(COMM_SIOCNT, READ16LE(&ioMem[COMM_SIOCNT]) & ~4); - UPDATE_REG(COMM_RCNT, 10); - linkmem->linkdata[linkid] = READ16LE(&ioMem[COMM_SIODATA8]); - ReleaseSemaphore(linksync[linkid], linkmem->numgbas-1, NULL); - } - if(linkid == transfer - 1) { - // SO becomes low to begin next trasnfer - // may need to set DDR as well - UPDATE_REG(COMM_RCNT, 0x22); - } - - // next cycle - transfer++; - } - - if (transfer > linkmem->trgbas && linktime >= trtimeend[transfer-3][tspeed]) - { - // wait for slaves to finish - // this keeps unfinished slaves from screwing up last xfer - // not strictly necessary; may just slow things down - if(!linkid) { - for(int i = 2; i < transfer; i++) - if(WaitForSingleObject(linksync[0], linktimeout) == WAIT_TIMEOUT) { - // impossible to determine which slave died - // so leave them alone for now - systemScreenMessage(_("Unknown slave timed out; resetting comm")); - linkmem->numtransfers = numtransfers = 0; - break; - } - } else if(linkmem->trgbas > linkid) - // signal master that this slave is finished - ReleaseSemaphore(linksync[0], 1, NULL); - linktime -= trtimeend[transfer - 3][tspeed]; - transfer = 0; - u16 value = READ16LE(&ioMem[COMM_SIOCNT]); - if(!linkid) - value |= 4; // SI becomes high on slaves after xfer - UPDATE_REG(COMM_SIOCNT, (value & 0xff0f) | (linkid << 4)); - // SC/SI high after transfer - UPDATE_REG(COMM_RCNT, linkid ? 15 : 11); - if (value & 0x4000) - { - IF |= 0x80; - UPDATE_REG(0x202, IF); - } - } -} - bool LinkRFUUpdate() { //if (IsLinkConnected()) { @@ -1977,732 +2710,12 @@ static void UpdateRFUIPC(int ticks) } } -static void UpdateSocket(int ticks) -{ - if (after) - { - if (linkid && linktime > 6044) { - lc.Recv(); - oncewait = true; - } - else - return; - } +u8 gbSIO_SC = 0; +bool LinkIsWaiting = false; +bool LinkFirstTime = true; +bool EmuReseted = true; - 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 || !linkDriver->update) { - return; - } - - // this actually gets called every single instruction, so keep default - // path as short as possible - - linktime += ticks; - - linkDriver->update(ticks); -} - -inline static int GetSIOMode(u16 siocnt, u16 rcnt) -{ - if (!(rcnt & 0x8000)) - { - switch (siocnt & 0x3000) { - case 0x0000: return NORMAL8; - case 0x1000: return NORMAL32; - case 0x2000: return MULTIPLAYER; - case 0x3000: return UART; - } - } - - if (rcnt & 0x4000) - return JOYBUS; - - return GP; -} - -static ConnectionState InitIPC() { - linkid = 0; - -#if (defined __WIN32__ || defined _WIN32) - if((mmf=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(LINKDATA), LOCAL_LINK_NAME))==NULL) { - systemMessage(0, N_("Error creating file mapping")); - return LINK_ERROR; - } - - if(GetLastError() == ERROR_ALREADY_EXISTS) - vbaid = 1; - else - vbaid = 0; - - - if((linkmem=(LINKDATA *)MapViewOfFile(mmf, FILE_MAP_WRITE, 0, 0, sizeof(LINKDATA)))==NULL) { - CloseHandle(mmf); - systemMessage(0, N_("Error mapping file")); - return LINK_ERROR; - } -#else - if((mmf = shm_open("/" LOCAL_LINK_NAME, O_RDWR|O_CREAT|O_EXCL, 0777)) < 0) { - vbaid = 1; - mmf = shm_open("/" LOCAL_LINK_NAME, O_RDWR, 0); - } else - vbaid = 0; - if(mmf < 0 || ftruncate(mmf, sizeof(LINKDATA)) < 0 || - !(linkmem = (LINKDATA *)mmap(NULL, sizeof(LINKDATA), - PROT_READ|PROT_WRITE, MAP_SHARED, - mmf, 0))) { - systemMessage(0, N_("Error creating file mapping")); - if(mmf) { - if(!vbaid) - shm_unlink("/" LOCAL_LINK_NAME); - close(mmf); - } - } -#endif - - // get lowest-numbered available machine slot - bool firstone = !vbaid; - if(firstone) { - linkmem->linkflags = 1; - linkmem->numgbas = 1; - linkmem->numtransfers=0; - for(i=0;i<4;i++) - linkmem->linkdata[i] = 0xffff; - } else { - // FIXME: this should be done while linkmem is locked - // (no xfer in progress, no other vba trying to connect) - int n = linkmem->numgbas; - int f = linkmem->linkflags; - for(int i = 0; i <= n; i++) - if(!(f & (1 << i))) { - vbaid = i; - break; - } - if(vbaid == 4) { -#if (defined __WIN32__ || defined _WIN32) - UnmapViewOfFile(linkmem); - CloseHandle(mmf); -#else - munmap(linkmem, sizeof(LINKDATA)); - if(!vbaid) - shm_unlink("/" LOCAL_LINK_NAME); - close(mmf); -#endif - systemMessage(0, N_("5 or more GBAs not supported.")); - return LINK_ERROR; - } - if(vbaid == n) - linkmem->numgbas = n + 1; - linkmem->linkflags = f | (1 << vbaid); - } - linkid = vbaid; - - for(i=0;i<4;i++) { - linkevent[sizeof(linkevent)-2]=(char)i+'1'; -#if (defined __WIN32__ || defined _WIN32) - linksync[i] = firstone ? - CreateSemaphore(NULL, 0, 4, linkevent) : - OpenSemaphore(SEMAPHORE_ALL_ACCESS, false, linkevent); - if(linksync[i] == NULL) { - UnmapViewOfFile(linkmem); - CloseHandle(mmf); - for(j=0;jShowServerIP(sf::IPAddress::GetLocalAddress()); - - // too bad Listen() doesn't take an address as well - // then again, old code used INADDR_ANY anyway - if (!lanlink.tcpsocket.Listen(IP_LINK_PORT)) - // Note: old code closed socket & retried once on bind failure - return LINK_ERROR; // FIXME: error code? - else - return LINK_NEEDS_UPDATE; - } else { - lc.serverport = IP_LINK_PORT; - - if (!lc.serveraddr.IsValid()) { - return LINK_ERROR; - } else { - lanlink.tcpsocket.SetBlocking(false); - sf::Socket::Status status = lanlink.tcpsocket.Connect(lc.serverport, lc.serveraddr); - - if (status == sf::Socket::Error || status == sf::Socket::Disconnected) - return LINK_ERROR; - else - return LINK_NEEDS_UPDATE; - } - } -} - -////////////////////////////////////////////////////////////////////////// -// Probably from here down needs to be replaced with SFML goodness :) -// tjm: what SFML goodness? SFML for network, yes, but not for IPC - -ConnectionState InitLink(LinkMode mode) -{ - if (mode == LINK_DISCONNECTED) - return LINK_ABORT; - - // Do nothing if we are already connected - if (GetLinkMode() != LINK_DISCONNECTED) { - systemMessage(0, N_("Error, link already connected")); - return LINK_ERROR; - } - - // Find the link driver - linkDriver = NULL; - for (u8 i = 0; i < sizeof(linkDrivers) / sizeof(linkDrivers[0]); i++) { - if (linkDrivers[i].mode == mode) { - linkDriver = &linkDrivers[i]; - break; - } - } - - if (!linkDriver || !linkDriver->connect) { - systemMessage(0, N_("Unable to find link driver")); - return LINK_ERROR; - } - - // Connect the link - gba_connection_state = linkDriver->connect(); - - if (gba_connection_state == LINK_ERROR) { - CloseLink(); - } - - return gba_connection_state; -} - -static ConnectionState ConnectUpdateSocket(char * const message, size_t size) { - ConnectionState newState = LINK_NEEDS_UPDATE; - - if (lanlink.server) { - sf::Selector fdset; - fdset.Add(lanlink.tcpsocket); - - if (fdset.Wait(0.1) == 1) { - int nextSlave = lanlink.connectedSlaves + 1; - - sf::Socket::Status st = lanlink.tcpsocket.Accept(ls.tcpsocket[nextSlave]); - - if (st == sf::Socket::Error) { - for (int j = 1; j < nextSlave; j++) - ls.tcpsocket[j].Close(); - - snprintf(message, size, N_("Network error.")); - newState = LINK_ERROR; - } else { - sf::Packet packet; - packet << static_cast(nextSlave) - << static_cast(lanlink.numslaves); - - ls.tcpsocket[nextSlave].Send(packet); - - snprintf(message, size, N_("Player %d connected"), nextSlave); - - lanlink.connectedSlaves++; - } - } - - if (lanlink.numslaves == lanlink.connectedSlaves) { - for (int i = 1; i <= lanlink.numslaves; i++) { - sf::Packet packet; - packet << true; - - ls.tcpsocket[i].Send(packet); - } - - snprintf(message, size, N_("All players connected")); - newState = LINK_OK; - } - } else { - - sf::Packet packet; - sf::Socket::Status status = lanlink.tcpsocket.Receive(packet); - - if (status == sf::Socket::Error || status == sf::Socket::Disconnected) { - snprintf(message, size, N_("Network error.")); - newState = LINK_ERROR; - } else if (status == sf::Socket::Done) { - - if (linkid == 0) { - sf::Uint16 receivedId, receivedSlaves; - packet >> receivedId >> receivedSlaves; - - if (packet) { - linkid = receivedId; - lanlink.numslaves = receivedSlaves; - - snprintf(message, size, N_("Connected as #%d, Waiting for %d players to join"), - linkid + 1, lanlink.numslaves - linkid); - } - } else { - bool gameReady; - packet >> gameReady; - - if (packet && gameReady) { - newState = LINK_OK; - snprintf(message, size, N_("All players joined.")); - } - } - - sf::Selector fdset; - fdset.Add(lanlink.tcpsocket); - fdset.Wait(0.1); - } - } - - return newState; -} - -ConnectionState ConnectLinkUpdate(char * const message, size_t size) -{ - message[0] = '\0'; - - if (!linkDriver || !linkDriver->connectUpdate || gba_connection_state != LINK_NEEDS_UPDATE) { - gba_connection_state = LINK_ERROR; - snprintf(message, size, N_("Link connection does not need updates.")); - - return LINK_ERROR; - } - - gba_connection_state = linkDriver->connectUpdate(message, size); - - return gba_connection_state; -} - -void EnableLinkServer(bool enable, int numSlaves) { - lanlink.server = enable; - lanlink.numslaves = numSlaves; -} - -void EnableSpeedHacks(bool enable) { - lanlink.speed = enable; -} - -bool SetLinkServerHost(const char *host) { - sf::IPAddress addr = sf::IPAddress(host); - - lc.serveraddr = addr; - joybusHostAddr = addr; - - return addr.IsValid(); -} - -void GetLinkServerHost(char * const host, size_t size) { - if (host == NULL || size == 0) - return; - - host[0] = '\0'; - - if (linkDriver && linkDriver->mode == LINK_GAMECUBE_DOLPHIN) - strncpy(host, joybusHostAddr.ToString().c_str(), size); - else if (lanlink.server) - strncpy(host, sf::IPAddress::GetLocalAddress().ToString().c_str(), size); - else - strncpy(host, lc.serveraddr.ToString().c_str(), size); -} - -void SetLinkTimeout(int value) { - linktimeout = value; -} - -int GetLinkPlayerId() { - if (GetLinkMode() == LINK_DISCONNECTED) { - return -1; - } else if (linkid > 0) { - return linkid; - } else { - return vbaid; - } -} - -static void ReInitLink() -{ - int f = linkmem->linkflags; - int n = linkmem->numgbas; - if(f & (1 << linkid)) { - systemMessage(0, N_("Lost link; reinitialize to reconnect")); - return; - } - linkmem->linkflags |= 1 << linkid; - if(n < linkid + 1) - linkmem->numgbas = linkid + 1; - numtransfers = linkmem->numtransfers; - 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 (!linkDriver || !linkDriver->close) { - return; // Nothing to do - } - - linkDriver->close(); - linkDriver = NULL; - - return; -} - -// call this to clean up crashed program's shared state -// or to use TCP on same machine (for testing) -// this may be necessary under MSW as well, but I wouldn't know how -void CleanLocalLink() -{ -#if !(defined __WIN32__ || defined _WIN32) - shm_unlink("/" LOCAL_LINK_NAME); - for(int i = 0; i < 4; i++) { - linkevent[sizeof(linkevent) - 2] = '1' + i; - sem_unlink(linkevent); - } -#endif -} - -// Server -lserver::lserver(void) { - intinbuffer = (s32*)inbuffer; - u16inbuffer = (u16*)inbuffer; - intoutbuffer = (s32*)outbuffer; - u16outbuffer = (u16*)outbuffer; - oncewait = false; -} - -void lserver::Send(void) { - if(lanlink.type==0) { // TCP - if(savedlinktime==-1) { - outbuffer[0] = 4; - outbuffer[1] = -32; //0xe0 - for(i=1;i<=lanlink.numslaves;i++) { - tcpsocket[i].Send(outbuffer, 4); - size_t nr; - tcpsocket[i].Receive(inbuffer, 4, nr); - } - } - outbuffer[1] = tspeed; - WRITE16LE(&u16outbuffer[1], linkdata[0]); - WRITE32LE(&intoutbuffer[1], savedlinktime); - if(lanlink.numslaves==1) { - if(lanlink.type==0) { - outbuffer[0] = 8; - tcpsocket[1].Send(outbuffer, 8); - } - } - else if(lanlink.numslaves==2) { - WRITE16LE(&u16outbuffer[4], linkdata[2]); - if(lanlink.type==0) { - outbuffer[0] = 10; - tcpsocket[1].Send(outbuffer, 10); - WRITE16LE(&u16outbuffer[4], linkdata[1]); - tcpsocket[2].Send(outbuffer, 10); - } - } else { - if(lanlink.type==0) { - outbuffer[0] = 12; - WRITE16LE(&u16outbuffer[4], linkdata[2]); - WRITE16LE(&u16outbuffer[5], linkdata[3]); - tcpsocket[1].Send(outbuffer, 12); - WRITE16LE(&u16outbuffer[4], linkdata[1]); - tcpsocket[2].Send(outbuffer, 12); - WRITE16LE(&u16outbuffer[5], linkdata[2]); - tcpsocket[3].Send(outbuffer, 12); - } - } - } - return; -} - -void lserver::Recv(void) { - int numbytes; - if(lanlink.type==0) { // TCP - fdset.Clear(); - for(i=0;i1) memmove(inbuffer, inbuffer+inbuffer[0]*(howmanytimes-1), inbuffer[0]); - if(inbuffer[1]==-32) { - char message[30]; - sprintf(message, _("Player %d disconnected."), i+2); - systemScreenMessage(message); - outbuffer[0] = 4; - outbuffer[1] = -32; - for(i=1;i0) { - while(numbyteslinkflags; + 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; + } } - LinkMode mode = (LinkMode)m_type; - - if (mode == LINK_DISCONNECTED || mode == LINK_CABLE_SOCKET) { - return; - } - - // Close any previous link - CloseLink(); - - bool needsServerHost = (mode == LINK_GAMECUBE_DOLPHIN); - - if (needsServerHost) { - bool valid = SetLinkServerHost(hostAddr); - if (!valid) { - return; + 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); - SetLinkTimeout(timeout); - EnableSpeedHacks(m_hacks); - - // Init link - ConnectionState state = InitLink(mode); - - if (!linkDriver->uses_socket) - { - // The user canceled the connection attempt - if (state == LINK_ABORT) { - CloseLink(); - return; - } - - // Something failed during init - if (state == LINK_ERROR) { - return; - } - } - else - { - CloseLink(); - return; - } + // 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 } +#endif #else bool gba_joybus_active = false; diff --git a/src/gba/GBALink.h b/src/gba/GBALink.h index 516019fd..048dae37 100644 --- a/src/gba/GBALink.h +++ b/src/gba/GBALink.h @@ -198,7 +198,7 @@ extern u8 gbSIO_SC; extern bool LinkIsWaiting; extern bool LinkFirstTime; extern bool EmuReseted; -extern void gbLinkReset(); +extern void gbInitLink(); extern u8 gbStartLink(u8 b); extern u16 gbLinkUpdate(u8 b, int gbSerialOn);