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);