From 303a03c9a8c5fe1ed88936fc9c87f550d4a7d4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jan 2025 21:10:16 +0100 Subject: [PATCH 01/19] Cleanup, define socket struct --- Core/HLE/sceNetInet.cpp | 9 +++++++++ Core/HLE/sceNetInet.h | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index d567efe253..2d532065f8 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -27,12 +27,21 @@ int inetLastSocket = -1; // A workaround to keep the most recent socket id for s bool netInetInited = false; +extern InetSocket g_inetSockets[256]; + void __NetInetShutdown() { if (!netInetInited) { return; } netInetInited = false; + + for (auto &sock : g_inetSockets) { + if (sock.state != SocketState::Unused) { + closesocket(sock.sock); + } + sock.state = SocketState::Unused; + } // TODO: Shut down any open sockets here. } diff --git a/Core/HLE/sceNetInet.h b/Core/HLE/sceNetInet.h index fb84e14350..f38282a32b 100644 --- a/Core/HLE/sceNetInet.h +++ b/Core/HLE/sceNetInet.h @@ -186,3 +186,17 @@ int sceNetApctlConnect(int connIndex); int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout); int sceNetApctlTerm(); int sceNetApctlDisconnect(); + +enum class SocketState { + Unused, + TCP, + UDP, +}; + +// Internal socket state tracking +struct InetSocket { + SOCKET sock; // native socket + SocketState state; +}; + +extern InetSocket g_inetSockets[256]; From 08f2bee1bdc15594ab92e362efd55e05f0820544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jan 2025 21:22:39 +0100 Subject: [PATCH 02/19] Add socket lookups --- Core/HLE/sceNetInet.cpp | 144 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 136 insertions(+), 8 deletions(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 2d532065f8..2a825a50d8 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -27,8 +27,28 @@ int inetLastSocket = -1; // A workaround to keep the most recent socket id for s bool netInetInited = false; +// We use this array from 1 and forward. It's probably not a good idea to return 0 as a socket. extern InetSocket g_inetSockets[256]; +static int AllocFreeSocket() { + for (int i = 1; i < ARRAY_SIZE(g_inetSockets); i++) { + if (g_inetSockets[i].state == SocketState::Unused) { + return i; + } + } + _dbg_assert_(false); + ERROR_LOG(Log::sceNet, "Ran out of socket handles! This is BAD."); + return 0; +} + +static bool GetInetSocket(int sock, InetSocket **inetSocket) { + if (sock < 1 || sock >= 256 || g_inetSockets[sock].state == SocketState::Unused) { + return false; + } + *inetSocket = &g_inetSockets[sock]; + return true; +} + void __NetInetShutdown() { if (!netInetInited) { return; @@ -42,6 +62,7 @@ void __NetInetShutdown() { } sock.state = SocketState::Unused; } + // TODO: Shut down any open sockets here. } @@ -113,7 +134,7 @@ static u32 sceNetInetInetNtop(int af, u32 srcInAddrPtr, u32 dstBufPtr, u32 bufsi return hleLogSuccessX(Log::sceNet, dstBufPtr, "%s", safe_string(Memory::GetCharPointer(dstBufPtr))); } -static u32_le sceNetInetInetAddr(const char* hostname) { +static u32_le sceNetInetInetAddr(const char *hostname) { WARN_LOG(Log::sceNet, "UNTESTED sceNetInetInetAddr(%s)", safe_string(hostname)); if (hostname == nullptr || hostname[0] == '\0') return hleLogError(Log::sceNet, INADDR_NONE, "invalid arg"); @@ -124,13 +145,18 @@ static u32_le sceNetInetInetAddr(const char* hostname) { return hleLogSuccessX(Log::sceNet, retval); } -static int sceNetInetGetpeername(int sock, u32 namePtr, u32 namelenPtr) { - WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x)", __FUNCTION__, sock, namePtr, namelenPtr); +static int sceNetInetGetpeername(int socket, u32 namePtr, u32 namelenPtr) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x)", __FUNCTION__, socket, namePtr, namelenPtr); if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidAddress(namelenPtr)) { inetLastErrno = EFAULT; return hleLogError(Log::sceNet, -1, "invalid arg"); } + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); int* namelen = (int*)Memory::GetPointer(namelenPtr); SockAddrIN4 saddr{}; @@ -138,7 +164,8 @@ static int sceNetInetGetpeername(int sock, u32 namePtr, u32 namelenPtr) { saddr.addr.sa_family = name->sa_family; int len = std::min(*namelen > 0 ? *namelen : 0, static_cast(sizeof(saddr))); memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); - int retval = getpeername(sock, (sockaddr*)&saddr, (socklen_t*)&len); + + int retval = getpeername(socket, (sockaddr*)&saddr, (socklen_t*)&len); DEBUG_LOG(Log::sceNet, "Getpeername: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); *namelen = len; if (retval < 0) { @@ -152,20 +179,25 @@ static int sceNetInetGetpeername(int sock, u32 namePtr, u32 namelenPtr) { return 0; } -static int sceNetInetGetsockname(int sock, u32 namePtr, u32 namelenPtr) { - WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x)", __FUNCTION__, sock, namePtr, namelenPtr); +static int sceNetInetGetsockname(int socket, u32 namePtr, u32 namelenPtr) { + WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x)", __FUNCTION__, socket, namePtr, namelenPtr); if (!Memory::IsValidAddress(namePtr) || !Memory::IsValidAddress(namelenPtr)) { inetLastErrno = EFAULT; return hleLogError(Log::sceNet, -1, "invalid arg"); } + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); int* namelen = (int*)Memory::GetPointer(namelenPtr); SockAddrIN4 saddr{}; saddr.addr.sa_family = name->sa_family; int len = std::min(*namelen > 0 ? *namelen : 0, static_cast(sizeof(saddr))); memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); - int retval = getsockname(sock, (sockaddr*)&saddr, (socklen_t*)&len); + int retval = getsockname(socket, (sockaddr*)&saddr, (socklen_t*)&len); DEBUG_LOG(Log::sceNet, "Getsockname: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); *namelen = len; if (retval < 0) { @@ -328,6 +360,12 @@ int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout) { // timeout in milisecond static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetRecv(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode flgs = convertMSGFlagsPSP2Host(flgs); int retval = recv(socket, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); @@ -350,6 +388,11 @@ static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetSend(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + std::string datahex; DataToHexString(10, 0, Memory::GetPointer(bufPtr), bufLen, &datahex); VERBOSE_LOG(Log::sceNet, "Data Dump (%d bytes):\n%s", bufLen, datahex.c_str()); @@ -397,6 +440,12 @@ static int sceNetInetSocket(int domain, int type, int protocol) { static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPtr, int optlen) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i, %i, %08x, %i) at %08x", __FUNCTION__, socket, level, optname, optvalPtr, optlen, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + u32_le* optval = (u32_le*)Memory::GetPointer(optvalPtr); DEBUG_LOG(Log::sceNet, "SockOpt: Level = %s, OptName = %s, OptValue = %d", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str(), *optval); timeval tval{}; @@ -453,6 +502,12 @@ static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPt static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPtr, u32 optlenPtr) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i, %i, %08x, %08x) at %08x", __FUNCTION__, socket, level, optname, optvalPtr, optlenPtr, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + u32_le* optval = (u32_le*)Memory::GetPointer(optvalPtr); socklen_t* optlen = (socklen_t*)Memory::GetPointer(optlenPtr); DEBUG_LOG(Log::sceNet, "SockOpt: Level = %s, OptName = %s", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str()); @@ -513,6 +568,12 @@ static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPt static int sceNetInetBind(int socket, u32 namePtr, int namelen) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i) at %08x", __FUNCTION__, socket, namePtr, namelen, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + SceNetInetSockaddr* name = (SceNetInetSockaddr*)Memory::GetPointer(namePtr); SockAddrIN4 saddr{}; // TODO: Should've created convertSockaddrPSP2Host (and Host2PSP too) function as it's being used pretty often, thus fixing a bug on it will be tedious when scattered all over the places @@ -564,6 +625,12 @@ static int sceNetInetBind(int socket, u32 namePtr, int namelen) { static int sceNetInetConnect(int socket, u32 sockAddrPtr, int sockAddrLen) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i) at %08x", __FUNCTION__, socket, sockAddrPtr, sockAddrLen, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + SceNetInetSockaddr* dst = (SceNetInetSockaddr*)Memory::GetPointer(sockAddrPtr); SockAddrIN4 saddr{}; int dstlen = std::min(sockAddrLen > 0 ? sockAddrLen : 0, static_cast(sizeof(saddr))); @@ -597,6 +664,11 @@ static int sceNetInetConnect(int socket, u32 sockAddrPtr, int sockAddrLen) { static int sceNetInetListen(int socket, int backlog) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i) at %08x", __FUNCTION__, socket, backlog, currentMIPS->pc); + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + int retval = listen(socket, (backlog == PSP_NET_INET_SOMAXCONN ? SOMAXCONN : backlog)); if (retval < 0) { inetLastErrno = socket_errno; @@ -608,6 +680,12 @@ static int sceNetInetListen(int socket, int backlog) { static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, addrPtr, addrLenPtr, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + SceNetInetSockaddr* src = (SceNetInetSockaddr*)Memory::GetCharPointer(addrPtr); socklen_t* srclen = (socklen_t*)Memory::GetCharPointer(addrLenPtr); SockAddrIN4 saddr{}; @@ -635,6 +713,12 @@ static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) { static int sceNetInetShutdown(int socket, int how) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i) at %08x", __FUNCTION__, socket, how, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + // Convert HOW from PSP to Host int hostHow = how; switch (how) { @@ -642,22 +726,42 @@ static int sceNetInetShutdown(int socket, int how) { case PSP_NET_INET_SHUT_WR: hostHow = SHUT_WR; break; case PSP_NET_INET_SHUT_RDWR: hostHow = SHUT_RDWR; break; } - return hleLogSuccessI(Log::sceNet, shutdown(socket, hostHow)); + + int retVal = shutdown(socket, hostHow); // no translation + return hleLogSuccessI(Log::sceNet, retVal); } static int sceNetInetSocketAbort(int socket) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i)", __FUNCTION__, socket); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + // FIXME: either using shutdown/close or select? probably using select if blocking mode is being simulated with non-blocking return hleLogSuccessI(Log::sceNet, shutdown(socket, SHUT_RDWR)); } static int sceNetInetClose(int socket) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + return hleLogSuccessI(Log::sceNet, closesocket(socket)); } static int sceNetInetCloseWithRST(int socket) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + // Based on http://deepix.github.io/2016/10/21/tcprst.html struct linger sl {}; sl.l_onoff = 1; // non-zero value enables linger option in kernel @@ -668,6 +772,12 @@ static int sceNetInetCloseWithRST(int socket) { static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 fromPtr, u32 fromlenPtr) { DEBUG_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i, %08x, %08x, %08x) at %08x", __FUNCTION__, socket, bufferPtr, len, flags, fromPtr, fromlenPtr, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + SceNetInetSockaddr* src = (SceNetInetSockaddr*)Memory::GetCharPointer(fromPtr); socklen_t* srclen = (socklen_t*)Memory::GetCharPointer(fromlenPtr); SockAddrIN4 saddr{}; @@ -710,6 +820,12 @@ static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 static int sceNetInetSendto(int socket, u32 bufferPtr, int len, int flags, u32 toPtr, int tolen) { DEBUG_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i, %08x, %08x, %d) at %08x", __FUNCTION__, socket, bufferPtr, len, flags, toPtr, tolen, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + SceNetInetSockaddr* dst = (SceNetInetSockaddr*)Memory::GetCharPointer(toPtr); int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode flgs = convertMSGFlagsPSP2Host(flgs); @@ -788,6 +904,12 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) { inetLastErrno = EFAULT; return hleLogError(Log::sceNet, retval); } + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + InetMsghdr* pspMsghdr = (InetMsghdr*)Memory::GetPointer(msghdrPtr); int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode flgs = convertMSGFlagsPSP2Host(flgs); @@ -976,6 +1098,12 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) { // Games using this: World of Poker static int sceNetInetRecvmsg(int socket, u32 msghdrPtr, int flags) { ERROR_LOG(Log::sceNet, "UNIMPL %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); + + InetSocket *inetSock; + if (!GetInetSocket(socket, &inetSock)) { + return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); + } + // Reference: http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch14lev1sec5.html int retval = -1; if (!Memory::IsValidAddress(msghdrPtr)) { From 0f2bd65845afb1f214f461e539e49507307e32d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jan 2025 21:34:54 +0100 Subject: [PATCH 03/19] Use the wrapper struct, except in case of poll/select --- Core/HLE/sceNetInet.cpp | 125 +++++++++++++++++++++++++--------------- Core/HLE/sceNetInet.h | 7 ++- 2 files changed, 83 insertions(+), 49 deletions(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 2a825a50d8..e0e3b320c2 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -30,7 +30,7 @@ bool netInetInited = false; // We use this array from 1 and forward. It's probably not a good idea to return 0 as a socket. extern InetSocket g_inetSockets[256]; -static int AllocFreeSocket() { +static int AllocInetSocket() { for (int i = 1; i < ARRAY_SIZE(g_inetSockets); i++) { if (g_inetSockets[i].state == SocketState::Unused) { return i; @@ -42,13 +42,25 @@ static int AllocFreeSocket() { } static bool GetInetSocket(int sock, InetSocket **inetSocket) { - if (sock < 1 || sock >= 256 || g_inetSockets[sock].state == SocketState::Unused) { + if (sock < 1 || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { + *inetSocket = nullptr; return false; } *inetSocket = &g_inetSockets[sock]; return true; } +static bool GetInetSocketFromHostSocket(int hostSock, InetSocket **inetSocket) { + for (int i = 1; i < ARRAY_SIZE(g_inetSockets); i++) { + if (g_inetSockets[i].state == SocketState::Used && g_inetSockets[i].sock == hostSock) { + *inetSocket = &g_inetSockets[i]; + return true; + } + } + *inetSocket = nullptr; + return false; +} + void __NetInetShutdown() { if (!netInetInited) { return; @@ -165,7 +177,7 @@ static int sceNetInetGetpeername(int socket, u32 namePtr, u32 namelenPtr) { int len = std::min(*namelen > 0 ? *namelen : 0, static_cast(sizeof(saddr))); memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); - int retval = getpeername(socket, (sockaddr*)&saddr, (socklen_t*)&len); + int retval = getpeername(inetSock->sock, (sockaddr*)&saddr, (socklen_t*)&len); DEBUG_LOG(Log::sceNet, "Getpeername: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); *namelen = len; if (retval < 0) { @@ -197,7 +209,7 @@ static int sceNetInetGetsockname(int socket, u32 namePtr, u32 namelenPtr) { saddr.addr.sa_family = name->sa_family; int len = std::min(*namelen > 0 ? *namelen : 0, static_cast(sizeof(saddr))); memcpy(saddr.addr.sa_data, name->sa_data, sizeof(name->sa_data)); - int retval = getsockname(socket, (sockaddr*)&saddr, (socklen_t*)&len); + int retval = getsockname(inetSock->sock, (sockaddr*)&saddr, (socklen_t*)&len); DEBUG_LOG(Log::sceNet, "Getsockname: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); *namelen = len; if (retval < 0) { @@ -368,7 +380,7 @@ static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode flgs = convertMSGFlagsPSP2Host(flgs); - int retval = recv(socket, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); + int retval = recv(inetSock->sock, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); if (retval < 0) { inetLastErrno = socket_errno; if (inetLastErrno == EAGAIN) @@ -389,7 +401,7 @@ static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetSend(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!GetInetSocket(inetSock->sock, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -399,7 +411,7 @@ static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode flgs = convertMSGFlagsPSP2Host(flgs); - int retval = send(socket, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); + int retval = send(inetSock->sock, (char*)Memory::GetPointer(bufPtr), bufLen, flgs | MSG_NOSIGNAL); if (retval < 0) { inetLastErrno = socket_errno; @@ -416,26 +428,41 @@ static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { static int sceNetInetSocket(int domain, int type, int protocol) { WARN_LOG(Log::sceNet, "UNTESTED sceNetInetSocket(%i, %i, %i) at %08x", domain, type, protocol, currentMIPS->pc); DEBUG_LOG(Log::sceNet, "Socket: Domain = %s, Type = %s, Protocol = %s", inetSocketDomain2str(domain).c_str(), inetSocketType2str(type).c_str(), inetSocketProto2str(protocol).c_str()); - int retval = socket(convertSocketDomainPSP2Host(domain), convertSocketTypePSP2Host(type), convertSocketProtoPSP2Host(protocol)); - if (retval < 0) { + + int hostDomain = convertSocketDomainPSP2Host(domain); + int hostType = convertSocketTypePSP2Host(type); + int hostProtocol = convertSocketProtoPSP2Host(protocol); + + SOCKET sock = ::socket(hostDomain, hostType, hostProtocol); + if (sock < 0) { inetLastErrno = socket_errno; - return hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); + return hleLogError(Log::sceNet, sock, "errno = %d", inetLastErrno); } - //InetSocket* sock = new InetSocket(domain, type, protocol); - //retval = pspSockets.Create(sock); + // Register the socket. + + int socket = AllocInetSocket(); + if (socket < 0) { + // Alloc already logged. Let's bail. + return hleLogError(Log::sceNet, ERROR_NET_INTERNAL); + } + InetSocket *inetSock = &g_inetSockets[socket]; + inetSock->sock = socket; + inetSock->domain = domain; + inetSock->type = type; + inetSock->protocol = protocol; // Ignore SIGPIPE when supported (ie. BSD/MacOS) - setSockNoSIGPIPE(retval, 1); + setSockNoSIGPIPE(sock, 1); // TODO: We should always use non-blocking mode and simulate blocking mode - changeBlockingMode(retval, 1); + changeBlockingMode(sock, 1); // Enable Port Re-use, required for multiple-instance - setSockReuseAddrPort(retval); + setSockReuseAddrPort(sock); // Disable Connection Reset error on UDP to avoid strange behavior - setUDPConnReset(retval, false); + setUDPConnReset(sock, false); - inetLastSocket = retval; - return hleLogSuccessI(Log::sceNet, retval); + inetLastSocket = sock; + return hleLogSuccessI(Log::sceNet, sock); } static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPtr, int optlen) { @@ -489,9 +516,9 @@ static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPt if (level == PSP_NET_INET_SOL_SOCKET && optval && (optname == PSP_NET_INET_SO_RCVTIMEO || optname == PSP_NET_INET_SO_SNDTIMEO)) { tval.tv_sec = *optval / 1000000; // seconds tval.tv_usec = (*optval % 1000000); // microseconds - retval = setsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, sizeof(tval)); + retval = setsockopt(inetSock->sock, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, sizeof(tval)); } else { - retval = setsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)optval, optlen); + retval = setsockopt(inetSock->sock, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)optval, optlen); } if (retval < 0) { inetLastErrno = socket_errno; @@ -550,13 +577,13 @@ static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPt // PSP timeout are a single 32bit value (micro seconds) if (level == PSP_NET_INET_SOL_SOCKET && optval && (optname == PSP_NET_INET_SO_RCVTIMEO || optname == PSP_NET_INET_SO_SNDTIMEO)) { socklen_t tvlen = sizeof(tval); - retval = getsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, &tvlen); + retval = getsockopt(inetSock->sock, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)&tval, &tvlen); if (retval != SOCKET_ERROR) { u64_le val = (tval.tv_sec * 1000000LL) + tval.tv_usec; memcpy(optval, &val, std::min(static_cast(sizeof(val)), std::min(static_cast(sizeof(*optval)), *optlen))); } } else { - retval = getsockopt(socket, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)optval, optlen); + retval = getsockopt(inetSock->sock, convertSockoptLevelPSP2Host(level), convertSockoptNamePSP2Host(optname, level), (char*)optval, optlen); } if (retval < 0) { inetLastErrno = socket_errno; @@ -596,14 +623,14 @@ static int sceNetInetBind(int socket, u32 namePtr, int namelen) { // TODO: Make use Port Offset only for PPSSPP to PPSSPP communications (ie. IP addresses available in the group/friendlist), otherwise should be considered as Online Service thus should use the port as is. //saddr.in.sin_port = htons(ntohs(saddr.in.sin_port) + portOffset); DEBUG_LOG(Log::sceNet, "Bind: Family = %s, Address = %s, Port = %d", inetSocketDomain2str(saddr.addr.sa_family).c_str(), ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); - changeBlockingMode(socket, 0); - int retval = bind(socket, (struct sockaddr*)&saddr, len); + changeBlockingMode(inetSock->sock, 0); + int retval = bind(inetSock->sock, (struct sockaddr*)&saddr, len); if (retval < 0) { inetLastErrno = socket_errno; - changeBlockingMode(socket, 1); + changeBlockingMode(inetSock->sock, 1); return hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); } - changeBlockingMode(socket, 1); + changeBlockingMode(inetSock->sock, 1); // Update binded port number if it was 0 (any port) memcpy(name->sa_data, saddr.addr.sa_data, sizeof(name->sa_data)); // Enable Port-forwarding @@ -639,24 +666,24 @@ static int sceNetInetConnect(int socket, u32 sockAddrPtr, int sockAddrLen) { DEBUG_LOG(Log::sceNet, "Connect: Address = %s, Port = %d", ip2str(saddr.in.sin_addr).c_str(), ntohs(saddr.in.sin_port)); // Workaround to avoid blocking for indefinitely - setSockTimeout(socket, SO_SNDTIMEO, 5000000); - setSockTimeout(socket, SO_RCVTIMEO, 5000000); - changeBlockingMode(socket, 0); // Use blocking mode as temporary fix for UNO, since we don't simulate blocking-mode yet - int retval = connect(socket, (struct sockaddr*)&saddr.addr, dstlen); + setSockTimeout(inetSock->sock, SO_SNDTIMEO, 5000000); + setSockTimeout(inetSock->sock, SO_RCVTIMEO, 5000000); + changeBlockingMode(inetSock->sock, 0); // Use blocking mode as temporary fix for UNO, since we don't simulate blocking-mode yet + int retval = connect(inetSock->sock, (struct sockaddr*)&saddr.addr, dstlen); if (retval < 0) { inetLastErrno = socket_errno; if (connectInProgress(inetLastErrno)) hleLogDebug(Log::sceNet, retval, "errno = %d", inetLastErrno); else hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); - changeBlockingMode(socket, 1); + changeBlockingMode(inetSock->sock, 1); // TODO: Since we're temporarily forcing blocking-mode we'll need to change errno from ETIMEDOUT to EAGAIN /*if (inetLastErrno == ETIMEDOUT) inetLastErrno = EAGAIN; */ return hleLogDebug(Log::sceNet, retval); } - changeBlockingMode(socket, 1); + changeBlockingMode(inetSock->sock, 1); return hleLogSuccessI(Log::sceNet, retval); } @@ -669,7 +696,7 @@ static int sceNetInetListen(int socket, int backlog) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } - int retval = listen(socket, (backlog == PSP_NET_INET_SOMAXCONN ? SOMAXCONN : backlog)); + int retval = listen(inetSock->sock, (backlog == PSP_NET_INET_SOMAXCONN ? SOMAXCONN : backlog)); if (retval < 0) { inetLastErrno = socket_errno; return hleLogError(Log::sceNet, retval, "errno = %d", inetLastErrno); @@ -691,7 +718,7 @@ static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) { SockAddrIN4 saddr{}; if (srclen) *srclen = std::min((*srclen) > 0 ? *srclen : 0, static_cast(sizeof(saddr))); - int retval = accept(socket, (struct sockaddr*)&saddr.addr, srclen); + int retval = accept(inetSock->sock, (struct sockaddr*)&saddr.addr, srclen); if (retval < 0) { inetLastErrno = socket_errno; if (inetLastErrno == EAGAIN) @@ -727,7 +754,7 @@ static int sceNetInetShutdown(int socket, int how) { case PSP_NET_INET_SHUT_RDWR: hostHow = SHUT_RDWR; break; } - int retVal = shutdown(socket, hostHow); // no translation + int retVal = shutdown(inetSock->sock, hostHow); // no translation return hleLogSuccessI(Log::sceNet, retVal); } @@ -740,7 +767,8 @@ static int sceNetInetSocketAbort(int socket) { } // FIXME: either using shutdown/close or select? probably using select if blocking mode is being simulated with non-blocking - return hleLogSuccessI(Log::sceNet, shutdown(socket, SHUT_RDWR)); + int retVal = shutdown(inetSock->sock, SHUT_RDWR); + return hleLogSuccessI(Log::sceNet, retVal); } static int sceNetInetClose(int socket) { @@ -751,7 +779,8 @@ static int sceNetInetClose(int socket) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } - return hleLogSuccessI(Log::sceNet, closesocket(socket)); + int retVal = closesocket(socket); + return hleLogSuccessI(Log::sceNet, retVal); } static int sceNetInetCloseWithRST(int socket) { @@ -766,8 +795,9 @@ static int sceNetInetCloseWithRST(int socket) { struct linger sl {}; sl.l_onoff = 1; // non-zero value enables linger option in kernel sl.l_linger = 0; // timeout interval in seconds - setsockopt(socket, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl)); - return hleLogSuccessI(Log::sceNet, closesocket(socket)); + setsockopt(inetSock->sock, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl)); + int retVal = closesocket(inetSock->sock); + return hleLogSuccessI(Log::sceNet, retVal); } static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 fromPtr, u32 fromlenPtr) { @@ -785,7 +815,7 @@ static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 *srclen = std::min((*srclen) > 0 ? *srclen : 0, static_cast(sizeof(saddr))); int flgs = flags & ~PSP_NET_INET_MSG_DONTWAIT; // removing non-POSIX flag, which is an alternative way to use non-blocking mode flgs = convertMSGFlagsPSP2Host(flgs); - int retval = recvfrom(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, srclen); + int retval = recvfrom(inetSock->sock, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, srclen); if (retval < 0) { inetLastErrno = socket_errno; if (inetLastErrno == EAGAIN) @@ -854,7 +884,7 @@ static int sceNetInetSendto(int socket, u32 bufferPtr, int len, int flags, u32 t continue; saddr.in.sin_addr.s_addr = peer->ip_addr; - retval = sendto(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, dstlen); + retval = sendto(inetSock->sock, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, dstlen); if (retval < 0) { DEBUG_LOG(Log::sceNet, "SendTo(BC): Socket error %d", socket_errno); } else { @@ -880,7 +910,7 @@ static int sceNetInetSendto(int socket, u32 bufferPtr, int len, int flags, u32 t saddr.in.sin_addr.s_addr = sockAddr.sin_addr.s_addr; DEBUG_LOG(Log::sceNet, "SendTo(BC): Address Replacement = %s", ip2str(saddr.in.sin_addr).c_str()); }*/ - retval = sendto(socket, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, dstlen); + retval = sendto(inetSock->sock, (char*)Memory::GetPointer(bufferPtr), len, flgs | MSG_NOSIGNAL, (struct sockaddr*)&saddr.addr, dstlen); } if (retval < 0) { inetLastErrno = socket_errno; @@ -1024,11 +1054,11 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) { saddr.in.sin_addr.s_addr = peer->ip_addr; #if defined(_WIN32) - int result = WSASendMsg(socket, &hdr, flgs | MSG_NOSIGNAL, &sent, NULL, NULL); + int result = WSASendMsg(inetSock->sock, &hdr, flgs | MSG_NOSIGNAL, &sent, NULL, NULL); if (static_cast(sent) > retval) retval = sent; #else - size_t result = sendmsg(socket, &hdr, flgs | MSG_NOSIGNAL); + size_t result = sendmsg(inetSock->sock, &hdr, flgs | MSG_NOSIGNAL); if (static_cast(result) > retval) retval = result; #endif @@ -1059,11 +1089,11 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) { DEBUG_LOG(Log::sceNet, "SendMsg(BC): Address Replacement = %s", ip2str(saddr.in.sin_addr).c_str()); }*/ #if defined(_WIN32) - int result = WSASendMsg(socket, &hdr, flgs | MSG_NOSIGNAL, &sent, NULL, NULL); + int result = WSASendMsg(inetSock->sock, &hdr, flgs | MSG_NOSIGNAL, &sent, NULL, NULL); if (result != SOCKET_ERROR) retval = sent; #else - retval = sendmsg(socket, &hdr, flgs | MSG_NOSIGNAL); + retval = sendmsg(inetSock->sock, &hdr, flgs | MSG_NOSIGNAL); #endif } free(chdr); @@ -1080,7 +1110,7 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) { iov[0].iov_base = buf; iov[0].iov_len = buflen; - retval = sendmsg(socket, &msg, flags); + retval = sendmsg(inetSock->sock, &msg, flags); free(buf); */ if (retval < 0) { @@ -1131,6 +1161,7 @@ static int sceNetInetRecvmsg(int socket, u32 msghdrPtr, int flags) { } memset(iov, 0, pspMsghdr->msg_iovlen * iovecsize); memset(&hdr, 0, sizeof(hdr)); + // TODO: Do similar to the already working sceNetInetSendmsg but in reverse //if (pspMsghdr->msg_name != 0) { ... } diff --git a/Core/HLE/sceNetInet.h b/Core/HLE/sceNetInet.h index f38282a32b..12df443498 100644 --- a/Core/HLE/sceNetInet.h +++ b/Core/HLE/sceNetInet.h @@ -189,14 +189,17 @@ int sceNetApctlDisconnect(); enum class SocketState { Unused, - TCP, - UDP, + Used, }; // Internal socket state tracking struct InetSocket { SOCKET sock; // native socket SocketState state; + // NOTE: These are the PSP types for now + int domain; + int type; + int protocol; }; extern InetSocket g_inetSockets[256]; From 8505e24812d5bfb3597d59de4b36b9447fa29569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jan 2025 22:29:28 +0100 Subject: [PATCH 04/19] Attempt to implement remapping in select/poll --- Core/HLE/proAdhoc.cpp | 2 +- Core/HLE/sceNetInet.cpp | 177 ++++++++++++++++++++++------------------ 2 files changed, 99 insertions(+), 80 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 01df36d390..e040fc6b67 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1318,7 +1318,7 @@ int GetChatMessageCount() { } // TODO: We should probably change this thread into PSPThread (or merging it into the existing AdhocThread PSPThread) as there are too many global vars being used here which also being used within some HLEs -int friendFinder(){ +int friendFinder() { SetCurrentThreadName("FriendFinder"); auto n = GetI18NCategory(I18NCat::NETWORKING); // Receive Buffer diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index e0e3b320c2..cee5d3a89c 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -23,12 +23,11 @@ #include "Core/Instance.h" int inetLastErrno = 0; // TODO: since errno can only be read once, we should keep track the value to be used on sceNetInetGetErrno -int inetLastSocket = -1; // A workaround to keep the most recent socket id for sceNetInetSelect, until we have a socket class wrapper bool netInetInited = false; // We use this array from 1 and forward. It's probably not a good idea to return 0 as a socket. -extern InetSocket g_inetSockets[256]; +InetSocket g_inetSockets[256]; static int AllocInetSocket() { for (int i = 1; i < ARRAY_SIZE(g_inetSockets); i++) { @@ -50,15 +49,31 @@ static bool GetInetSocket(int sock, InetSocket **inetSocket) { return true; } -static bool GetInetSocketFromHostSocket(int hostSock, InetSocket **inetSocket) { +// Simplified mappers, only really useful in select/poll +static int GetInetSocketFromHostSocket(SOCKET hostSock) { for (int i = 1; i < ARRAY_SIZE(g_inetSockets); i++) { if (g_inetSockets[i].state == SocketState::Used && g_inetSockets[i].sock == hostSock) { - *inetSocket = &g_inetSockets[i]; - return true; + return i; } } - *inetSocket = nullptr; - return false; + if (hostSock == 0) { + // Map 0 to 0, special case. + return 0; + } + _dbg_assert_(false); + return -1; +} + +static SOCKET GetHostSocketFromInetSocket(int sock) { + if (sock < 1 || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { + _dbg_assert_(false); + return -1; + } + if (sock == 0) { + // Map 0 to 0, special case. + return 0; + } + return g_inetSockets[sock].sock; } void __NetInetShutdown() { @@ -226,58 +241,61 @@ static int sceNetInetGetsockname(int socket, u32 namePtr, u32 namelenPtr) { // FIXME: nfds is number of fd(s) as in posix poll, or was it maximum fd value as in posix select? Star Wars Battlefront Renegade seems to set the nfds to 64, while Coded Arms Contagion is using 256 int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr, u32 timeoutPtr) { DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetSelect(%i, %08x, %08x, %08x, %08x) at %08x", nfds, readfdsPtr, writefdsPtr, exceptfdsPtr, timeoutPtr, currentMIPS->pc); - int retval = -1; - SceNetInetFdSet* readfds = (SceNetInetFdSet*)Memory::GetCharPointer(readfdsPtr); - SceNetInetFdSet* writefds = (SceNetInetFdSet*)Memory::GetCharPointer(writefdsPtr); - SceNetInetFdSet* exceptfds = (SceNetInetFdSet*)Memory::GetCharPointer(exceptfdsPtr); - SceNetInetTimeval* timeout = (SceNetInetTimeval*)Memory::GetCharPointer(timeoutPtr); - // TODO: Use poll instead of select since Windows' FD_SETSIZE is only 64 while PSP is 256, and select can only work for fd value less than FD_SETSIZE on some system - fd_set rdfds{}, wrfds{}, exfds{}; + + SceNetInetFdSet *readfds = readfdsPtr ? (SceNetInetFdSet*)Memory::GetPointerWrite(readfdsPtr) : nullptr; + SceNetInetFdSet *writefds = writefdsPtr ? (SceNetInetFdSet*)Memory::GetPointerWrite(writefdsPtr) : nullptr; + SceNetInetFdSet *exceptfds = exceptfdsPtr ? (SceNetInetFdSet*)Memory::GetPointerWrite(exceptfdsPtr) : nullptr; + SceNetInetTimeval *timeout = timeoutPtr ? (SceNetInetTimeval*)Memory::GetPointerWrite(timeoutPtr) : nullptr; + + // First, translate the specified fd_sets to host sockets. + + fd_set rdfds, wrfds, exfds; FD_ZERO(&rdfds); FD_ZERO(&wrfds); FD_ZERO(&exfds); - int maxfd = nfds; // (nfds > PSP_NET_INET_FD_SETSIZE) ? nfds : PSP_NET_INET_FD_SETSIZE; + + if (nfds > 256) { + ERROR_LOG(Log::sceNet, "Bad nfds value: %d", nfds); + nfds = 256; + } + int rdcnt = 0, wrcnt = 0, excnt = 0; - for (int i = maxfd - 1; i >= 0 /*&& i >= maxfd - 64*/; i--) { - bool windows_workaround = false; -#if PPSSPP_PLATFORM(WINDOWS) - //windows_workaround = (i == nfds - 1); -#endif - if (readfds != NULL && (NetInetFD_ISSET(i, readfds) || windows_workaround)) { + + // Save the mapping during setup. + SOCKET hostSockets[256]{}; + + for (int i = 0; i < nfds; i++) { + if (readfds && (NetInetFD_ISSET(i, readfds))) { + SOCKET sock = GetHostSocketFromInetSocket(i); + hostSockets[i] = sock; VERBOSE_LOG(Log::sceNet, "Input Read FD #%i", i); if (rdcnt < FD_SETSIZE) { - FD_SET(i, &rdfds); // This might pointed to a non-existing socket or sockets belonged to other programs on Windows, because most of the time Windows socket have an id above 1k instead of 0-255 + FD_SET(sock, &rdfds); // This might pointed to a non-existing socket or sockets belonged to other programs on Windows, because most of the time Windows socket have an id above 1k instead of 0-255 rdcnt++; } } - if (writefds != NULL && (NetInetFD_ISSET(i, writefds) || windows_workaround)) { + if (writefds && (NetInetFD_ISSET(i, writefds))) { + SOCKET sock = GetHostSocketFromInetSocket(i); + hostSockets[i] = sock; VERBOSE_LOG(Log::sceNet, "Input Write FD #%i", i); if (wrcnt < FD_SETSIZE) { - FD_SET(i, &wrfds); + FD_SET(sock, &wrfds); wrcnt++; } } - if (exceptfds != NULL && (NetInetFD_ISSET(i, exceptfds) || windows_workaround)) { + if (exceptfds && (NetInetFD_ISSET(i, exceptfds))) { + SOCKET sock = GetHostSocketFromInetSocket(i); + hostSockets[i] = sock; VERBOSE_LOG(Log::sceNet, "Input Except FD #%i", i); if (excnt < FD_SETSIZE) { - FD_SET(i, &exfds); + FD_SET(sock, &exfds); excnt++; } } } - // Workaround for games that set ndfs to 64 instead of socket id + 1 - if (inetLastSocket >= 0) { - if (readfds != NULL && rdcnt == 0) { - FD_SET(inetLastSocket, &rdfds); - rdcnt++; - } - if (writefds != NULL && wrcnt == 0) { - FD_SET(inetLastSocket, &wrfds); - wrcnt++; - } - if (exceptfds != NULL && excnt == 0) { - FD_SET(inetLastSocket, &exfds); - excnt++; - } - } + + // Unlikely to hit these. + _dbg_assert_(rdcnt < FD_SETSIZE); + _dbg_assert_(wrcnt < FD_SETSIZE); + _dbg_assert_(excnt < FD_SETSIZE); timeval tmout = { 5, 543210 }; // Workaround timeout value when timeout = NULL if (timeout != NULL) { @@ -286,26 +304,23 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr } VERBOSE_LOG(Log::sceNet, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, (int)tmout.tv_sec, (int)tmout.tv_usec); // TODO: Simulate blocking behaviour when timeout = NULL to prevent PPSSPP from freezing - retval = select(nfds, (readfds == NULL) ? NULL : &rdfds, (writefds == NULL) ? NULL : &wrfds, (exceptfds == NULL) ? NULL : &exfds, /*(timeout == NULL) ? NULL :*/ &tmout); - if (readfds != NULL && inetLastSocket < maxfd) NetInetFD_ZERO(readfds); // Clear it only when not needing a workaround - if (writefds != NULL && inetLastSocket < maxfd) NetInetFD_ZERO(writefds); // Clear it only when not needing a workaround - if (exceptfds != NULL) NetInetFD_ZERO(exceptfds); - for (int i = maxfd - 1; i >= 0 /*&& i >= maxfd - 64*/; i--) { - if (readfds != NULL && FD_ISSET(i, &rdfds)) - NetInetFD_SET(i, readfds); - if (writefds != NULL && FD_ISSET(i, &wrfds)) - NetInetFD_SET(i, writefds); - if (exceptfds != NULL && FD_ISSET(i, &exfds)) - NetInetFD_SET(i, exceptfds); + int retval = select(nfds, readfds ? &rdfds : nullptr, writefds ? &wrfds : nullptr, exceptfds ? &exfds : nullptr, /*(timeout == NULL) ? NULL :*/ &tmout); + if (retval < 0) { + ERROR_LOG(Log::sceNet, "selected returned an error, TODO"); } - // Workaround for games that set ndfs to 64 instead of socket id + 1 - if (inetLastSocket >= 0) { - if (readfds != NULL && rdcnt == 1 && FD_ISSET(inetLastSocket, &rdfds)) - NetInetFD_SET(inetLastSocket, readfds); - if (writefds != NULL && wrcnt == 1 && FD_ISSET(inetLastSocket, &wrfds)) - NetInetFD_SET(inetLastSocket, writefds); - if (exceptfds != NULL && excnt == 1 && FD_ISSET(inetLastSocket, &exfds)) - NetInetFD_SET(inetLastSocket, exceptfds); + if (readfds != NULL) NetInetFD_ZERO(readfds); + if (writefds != NULL) NetInetFD_ZERO(writefds); + if (exceptfds != NULL) NetInetFD_ZERO(exceptfds); + for (int i = 0; i < nfds; i++) { + if (readfds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &rdfds)) { + NetInetFD_SET(i, readfds); + } + if (writefds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &wrfds)) { + NetInetFD_SET(i, writefds); + } + if (exceptfds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &exfds)) { + NetInetFD_SET(i, exceptfds); + } } if (retval < 0) { @@ -335,12 +350,16 @@ int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout) { // timeout in milisecond inetLastErrno = EINVAL; return hleLogError(Log::sceNet, -1, "invalid socket id"); } - if (fdarray[i].fd > maxfd) maxfd = fdarray[i].fd; - FD_SET(fdarray[i].fd, &readfds); - FD_SET(fdarray[i].fd, &writefds); - FD_SET(fdarray[i].fd, &exceptfds); + if (fdarray[i].fd > maxfd) { + maxfd = fdarray[i].fd; + } + SOCKET hostSocket = GetHostSocketFromInetSocket(fdarray[i].fd); + FD_SET(hostSocket, &readfds); + FD_SET(hostSocket, &writefds); + FD_SET(hostSocket, &exceptfds); fdarray[i].revents = 0; } + timeval tmout = { 5, 543210 }; // Workaround timeout value when timeout = NULL if (timeout >= 0) { tmout.tv_sec = timeout / 1000000; // seconds @@ -355,12 +374,13 @@ int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout) { // timeout in milisecond retval = 0; for (int i = 0; i < (s32)nfds; i++) { - if ((fdarray[i].events & (INET_POLLRDNORM | INET_POLLIN)) && FD_ISSET(fdarray[i].fd, &readfds)) + SOCKET hostSocket = GetHostSocketFromInetSocket(fdarray[i].fd); + if ((fdarray[i].events & (INET_POLLRDNORM | INET_POLLIN)) && FD_ISSET(hostSocket, &readfds)) fdarray[i].revents |= (INET_POLLRDNORM | INET_POLLIN); //POLLIN_SET - if ((fdarray[i].events & (INET_POLLWRNORM | INET_POLLOUT)) && FD_ISSET(fdarray[i].fd, &writefds)) + if ((fdarray[i].events & (INET_POLLWRNORM | INET_POLLOUT)) && FD_ISSET(hostSocket, &writefds)) fdarray[i].revents |= (INET_POLLWRNORM | INET_POLLOUT); //POLLOUT_SET fdarray[i].revents &= fdarray[i].events; - if (FD_ISSET(fdarray[i].fd, &exceptfds)) + if (FD_ISSET(hostSocket, &exceptfds)) fdarray[i].revents |= (INET_POLLRDBAND | INET_POLLPRI | INET_POLLERR); //POLLEX_SET; // Can be raised on revents regardless of events bitmask? if (fdarray[i].revents) retval++; @@ -401,7 +421,7 @@ static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetSend(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(inetSock->sock, &inetSock)) { + if (!GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -433,10 +453,10 @@ static int sceNetInetSocket(int domain, int type, int protocol) { int hostType = convertSocketTypePSP2Host(type); int hostProtocol = convertSocketProtoPSP2Host(protocol); - SOCKET sock = ::socket(hostDomain, hostType, hostProtocol); - if (sock < 0) { + SOCKET hostSock = ::socket(hostDomain, hostType, hostProtocol); + if (hostSock < 0) { inetLastErrno = socket_errno; - return hleLogError(Log::sceNet, sock, "errno = %d", inetLastErrno); + return hleLogError(Log::sceNet, hostSock, "errno = %d", inetLastErrno); } // Register the socket. @@ -447,22 +467,21 @@ static int sceNetInetSocket(int domain, int type, int protocol) { return hleLogError(Log::sceNet, ERROR_NET_INTERNAL); } InetSocket *inetSock = &g_inetSockets[socket]; - inetSock->sock = socket; + inetSock->state = SocketState::Used; + inetSock->sock = hostSock; inetSock->domain = domain; inetSock->type = type; inetSock->protocol = protocol; // Ignore SIGPIPE when supported (ie. BSD/MacOS) - setSockNoSIGPIPE(sock, 1); + setSockNoSIGPIPE(hostSock, 1); // TODO: We should always use non-blocking mode and simulate blocking mode - changeBlockingMode(sock, 1); + changeBlockingMode(hostSock, 1); // Enable Port Re-use, required for multiple-instance - setSockReuseAddrPort(sock); + setSockReuseAddrPort(hostSock); // Disable Connection Reset error on UDP to avoid strange behavior - setUDPConnReset(sock, false); - - inetLastSocket = sock; - return hleLogSuccessI(Log::sceNet, sock); + setUDPConnReset(hostSock, false); + return hleLogSuccessI(Log::sceNet, socket); } static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPtr, int optlen) { From fcb3d636b4fee9b6fb504f51d727941687b3ca9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jan 2025 23:12:40 +0100 Subject: [PATCH 05/19] Fix some closesocket bugs --- Core/HLE/sceNetInet.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index cee5d3a89c..481b78b2b6 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -798,10 +798,17 @@ static int sceNetInetClose(int socket) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } - int retVal = closesocket(socket); + int retVal = closesocket(inetSock->sock); + if (retVal == 0) { + inetSock->sock = 0; + inetSock->state = SocketState::Unused; + } else { + ERROR_LOG(Log::sceNet, "closesocket(%d) failed (socket=%d)", inetSock->sock, socket); + } return hleLogSuccessI(Log::sceNet, retVal); } +// TODO: How is this different than just sceNetInetClose? static int sceNetInetCloseWithRST(int socket) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); @@ -816,6 +823,12 @@ static int sceNetInetCloseWithRST(int socket) { sl.l_linger = 0; // timeout interval in seconds setsockopt(inetSock->sock, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl)); int retVal = closesocket(inetSock->sock); + if (retVal == 0) { + inetSock->sock = 0; + inetSock->state = SocketState::Unused; + } else { + ERROR_LOG(Log::sceNet, "closesocket(%d) failed (socket=%d)", inetSock->sock, socket); + } return hleLogSuccessI(Log::sceNet, retVal); } From c33ea84db1d4e16c495ddb87a95dcb8d68f599b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 7 Jan 2025 23:13:44 +0100 Subject: [PATCH 06/19] Implement one of ANR2ME's TODOs --- Core/HLE/sceNetInet.cpp | 14 ++++++-------- Core/HLE/sceNetInet.h | 4 ++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 481b78b2b6..1f16a31530 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -472,6 +472,9 @@ static int sceNetInetSocket(int domain, int type, int protocol) { inetSock->domain = domain; inetSock->type = type; inetSock->protocol = protocol; + inetSock->hostDomain = hostDomain; + inetSock->hostType = hostType; + inetSock->hostProtocol = hostProtocol; // Ignore SIGPIPE when supported (ie. BSD/MacOS) setSockNoSIGPIPE(hostSock, 1); @@ -495,7 +498,6 @@ static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPt u32_le* optval = (u32_le*)Memory::GetPointer(optvalPtr); DEBUG_LOG(Log::sceNet, "SockOpt: Level = %s, OptName = %s, OptValue = %d", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str(), *optval); timeval tval{}; - // InetSocket* sock = pspSockets.Get(socket, error); // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-bloking mode (ie. simulated blocking mode) if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_NBIO) { //memcpy(&sock->nonblocking, (int*)optval, std::min(sizeof(sock->nonblocking), optlen)); @@ -558,7 +560,6 @@ static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPt socklen_t* optlen = (socklen_t*)Memory::GetPointer(optlenPtr); DEBUG_LOG(Log::sceNet, "SockOpt: Level = %s, OptName = %s", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str()); timeval tval{}; - // InetSocket* sock = pspSockets.Get(socket, error); // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-bloking mode (ie. simulated blocking mode) if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_NBIO) { //*optlen = std::min(sizeof(sock->nonblocking), *optlen); @@ -653,19 +654,16 @@ static int sceNetInetBind(int socket, u32 namePtr, int namelen) { // Update binded port number if it was 0 (any port) memcpy(name->sa_data, saddr.addr.sa_data, sizeof(name->sa_data)); // Enable Port-forwarding - // TODO: Check the socket type/protocol for SOCK_STREAM/SOCK_DGRAM or IPPROTO_TCP/IPPROTO_UDP instead of forwarding both protocol - // InetSocket* sock = pspSockets.Get(socket, error); - // UPnP_Add((sock->type == SOCK_STREAM)? IP_PROTOCOL_TCP: IP_PROTOCOL_UDP, port, port); + + // Check the socket type/protocol for SOCK_STREAM/SOCK_DGRAM or IPPROTO_TCP/IPPROTO_UDP instead of forwarding both protocols like in ANR2ME's original change. unsigned short port = ntohs(saddr.in.sin_port); - UPnP_Add(IP_PROTOCOL_UDP, port, port); - UPnP_Add(IP_PROTOCOL_TCP, port, port); + UPnP_Add((inetSock->type == PSP_NET_INET_SOCK_STREAM) ? IP_PROTOCOL_TCP : IP_PROTOCOL_UDP, port, port); // Workaround: Send a dummy 0 size message to AdhocServer IP to make sure the socket actually bound to an address when binded with INADDR_ANY before using getsockname, seems to fix sending DGRAM from incorrect port issue on Android /*saddr.in.sin_addr.s_addr = g_adhocServerIP.in.sin_addr.s_addr; saddr.in.sin_port = 0; sendto(socket, dummyPeekBuf64k, 0, MSG_NOSIGNAL, (struct sockaddr*)&saddr, sizeof(saddr)); */ - return hleLogSuccessI(Log::sceNet, retval); } diff --git a/Core/HLE/sceNetInet.h b/Core/HLE/sceNetInet.h index 12df443498..daa319b77d 100644 --- a/Core/HLE/sceNetInet.h +++ b/Core/HLE/sceNetInet.h @@ -200,6 +200,10 @@ struct InetSocket { int domain; int type; int protocol; + // These are the host types for convenience. + int hostDomain; + int hostType; + int hostProtocol; }; extern InetSocket g_inetSockets[256]; From 3dc2a1034d86e25d48d3fdafd73ebd41b58bc085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 09:51:24 +0100 Subject: [PATCH 07/19] Setting a socket number minimum of 20 solved the Mac problems. I guess it clashes with the adhoc code? --- Core/HLE/sceNetInet.cpp | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 1f16a31530..995d73adc3 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -22,6 +22,8 @@ #include "Core/Util/PortManager.h" #include "Core/Instance.h" +#define MIN_VALID_SOCKET 20 + int inetLastErrno = 0; // TODO: since errno can only be read once, we should keep track the value to be used on sceNetInetGetErrno bool netInetInited = false; @@ -30,7 +32,7 @@ bool netInetInited = false; InetSocket g_inetSockets[256]; static int AllocInetSocket() { - for (int i = 1; i < ARRAY_SIZE(g_inetSockets); i++) { + for (int i = MIN_VALID_SOCKET; i < ARRAY_SIZE(g_inetSockets); i++) { if (g_inetSockets[i].state == SocketState::Unused) { return i; } @@ -41,7 +43,7 @@ static int AllocInetSocket() { } static bool GetInetSocket(int sock, InetSocket **inetSocket) { - if (sock < 1 || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { + if (sock < MIN_VALID_SOCKET || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { *inetSocket = nullptr; return false; } @@ -50,22 +52,8 @@ static bool GetInetSocket(int sock, InetSocket **inetSocket) { } // Simplified mappers, only really useful in select/poll -static int GetInetSocketFromHostSocket(SOCKET hostSock) { - for (int i = 1; i < ARRAY_SIZE(g_inetSockets); i++) { - if (g_inetSockets[i].state == SocketState::Used && g_inetSockets[i].sock == hostSock) { - return i; - } - } - if (hostSock == 0) { - // Map 0 to 0, special case. - return 0; - } - _dbg_assert_(false); - return -1; -} - static SOCKET GetHostSocketFromInetSocket(int sock) { - if (sock < 1 || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { + if (sock < MIN_VALID_SOCKET || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { _dbg_assert_(false); return -1; } @@ -262,7 +250,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr // Save the mapping during setup. SOCKET hostSockets[256]{}; - for (int i = 0; i < nfds; i++) { + for (int i = MIN_VALID_SOCKET; i < nfds; i++) { if (readfds && (NetInetFD_ISSET(i, readfds))) { SOCKET sock = GetHostSocketFromInetSocket(i); hostSockets[i] = sock; @@ -311,7 +299,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (readfds != NULL) NetInetFD_ZERO(readfds); if (writefds != NULL) NetInetFD_ZERO(writefds); if (exceptfds != NULL) NetInetFD_ZERO(exceptfds); - for (int i = 0; i < nfds; i++) { + for (int i = MIN_VALID_SOCKET; i < nfds; i++) { if (readfds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &rdfds)) { NetInetFD_SET(i, readfds); } From 2c3f7f6806f7ce6dcd91467f37884a2e7a04dff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 12:04:59 +0100 Subject: [PATCH 08/19] Add a central location for managing HLE sockets --- CMakeLists.txt | 2 ++ Core/Core.vcxproj | 2 ++ Core/Core.vcxproj.filters | 6 ++++ Core/HLE/SocketManager.cpp | 54 ++++++++++++++++++++++++++++ Core/HLE/SocketManager.h | 34 ++++++++++++++++++ Core/HLE/proAdhoc.cpp | 1 - Core/HLE/sceNetInet.cpp | 55 +++-------------------------- Core/HLE/sceNetInet.h | 21 ----------- UWP/CoreUWP/CoreUWP.vcxproj | 2 ++ UWP/CoreUWP/CoreUWP.vcxproj.filters | 6 ++++ android/jni/Android.mk | 1 + libretro/Makefile.common | 1 + 12 files changed, 113 insertions(+), 72 deletions(-) create mode 100644 Core/HLE/SocketManager.cpp create mode 100644 Core/HLE/SocketManager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b44592b8e0..b923419d0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2178,6 +2178,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/AtracCtx2.h Core/HLE/NetInetConstants.cpp Core/HLE/NetInetConstants.h + Core/HLE/SocketManager.cpp + Core/HLE/SocketManager.h Core/HLE/sceAtrac.cpp Core/HLE/sceAtrac.h Core/HLE/sceAudio.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 6242bc4dc5..dbb6ca5bfa 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -580,6 +580,7 @@ + @@ -1199,6 +1200,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 174dceebb4..ad6cdd3f48 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1342,6 +1342,9 @@ HLE\Libraries + + HLE\Libraries + @@ -2160,6 +2163,9 @@ HLE\Libraries + + HLE\Libraries + diff --git a/Core/HLE/SocketManager.cpp b/Core/HLE/SocketManager.cpp new file mode 100644 index 0000000000..400d163677 --- /dev/null +++ b/Core/HLE/SocketManager.cpp @@ -0,0 +1,54 @@ +#include "Core/HLE/SocketManager.h" +#include "Common/Log.h" + +#include + +// We use this array from 1 and forward. It's probably not a good idea to return 0 as a socket. +InetSocket g_inetSockets[256]; +static std::mutex g_socketMutex; // TODO: Remove once the adhoc thread is gone + +int AllocInetSocket() { + std::lock_guard guard(g_socketMutex); + for (int i = MIN_VALID_INET_SOCKET; i < ARRAY_SIZE(g_inetSockets); i++) { + if (g_inetSockets[i].state == SocketState::Unused) { + return i; + } + } + _dbg_assert_(false); + ERROR_LOG(Log::sceNet, "Ran out of socket handles! This is BAD."); + return 0; +} + +bool GetInetSocket(int sock, InetSocket **inetSocket) { + std::lock_guard guard(g_socketMutex); + if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { + *inetSocket = nullptr; + return false; + } + *inetSocket = &g_inetSockets[sock]; + return true; +} + +// Simplified mappers, only really useful in select/poll +SOCKET GetHostSocketFromInetSocket(int sock) { + std::lock_guard guard(g_socketMutex); + if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { + _dbg_assert_(false); + return -1; + } + if (sock == 0) { + // Map 0 to 0, special case. + return 0; + } + return g_inetSockets[sock].sock; +} + +void CloseAllSockets() { + for (auto &sock : g_inetSockets) { + if (sock.state != SocketState::Unused) { + closesocket(sock.sock); + } + sock.state = SocketState::Unused; + sock.sock = 0; + } +} diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h new file mode 100644 index 0000000000..0eaac03273 --- /dev/null +++ b/Core/HLE/SocketManager.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Common/Net/SocketCompat.h" + +// Keep track of who's using a socket. +enum class SocketState { + Unused, + UsedNetInet, + UsedProAdhoc, +}; + +// Internal socket state tracking +struct InetSocket { + SOCKET sock; // native socket + SocketState state; + // NOTE: These are the PSP types for now + int domain; + int type; + int protocol; + // These are the host types for convenience. + int hostDomain; + int hostType; + int hostProtocol; +}; + +#define MIN_VALID_INET_SOCKET 20 +#define VALID_INET_SOCKET_COUNT 256 + +extern InetSocket g_inetSockets[VALID_INET_SOCKET_COUNT]; + +int AllocInetSocket(); +bool GetInetSocket(int sock, InetSocket **inetSocket); +SOCKET GetHostSocketFromInetSocket(int sock); +void CloseAllSockets(); diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index e040fc6b67..67849b3445 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -49,7 +49,6 @@ #include "Core/CoreTiming.h" #include "Core/Core.h" #include "Core/HLE/sceKernelInterrupt.h" -#include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelMemory.h" #include "Core/HLE/sceNetAdhoc.h" #include "Core/Instance.h" diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 995d73adc3..f8bf83267a 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -6,6 +6,7 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" +#include "Core/HLE/SocketManager.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceNet.h" @@ -22,63 +23,17 @@ #include "Core/Util/PortManager.h" #include "Core/Instance.h" -#define MIN_VALID_SOCKET 20 - int inetLastErrno = 0; // TODO: since errno can only be read once, we should keep track the value to be used on sceNetInetGetErrno bool netInetInited = false; -// We use this array from 1 and forward. It's probably not a good idea to return 0 as a socket. -InetSocket g_inetSockets[256]; - -static int AllocInetSocket() { - for (int i = MIN_VALID_SOCKET; i < ARRAY_SIZE(g_inetSockets); i++) { - if (g_inetSockets[i].state == SocketState::Unused) { - return i; - } - } - _dbg_assert_(false); - ERROR_LOG(Log::sceNet, "Ran out of socket handles! This is BAD."); - return 0; -} - -static bool GetInetSocket(int sock, InetSocket **inetSocket) { - if (sock < MIN_VALID_SOCKET || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { - *inetSocket = nullptr; - return false; - } - *inetSocket = &g_inetSockets[sock]; - return true; -} - -// Simplified mappers, only really useful in select/poll -static SOCKET GetHostSocketFromInetSocket(int sock) { - if (sock < MIN_VALID_SOCKET || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { - _dbg_assert_(false); - return -1; - } - if (sock == 0) { - // Map 0 to 0, special case. - return 0; - } - return g_inetSockets[sock].sock; -} - void __NetInetShutdown() { if (!netInetInited) { return; } netInetInited = false; - - for (auto &sock : g_inetSockets) { - if (sock.state != SocketState::Unused) { - closesocket(sock.sock); - } - sock.state = SocketState::Unused; - } - - // TODO: Shut down any open sockets here. + CloseAllSockets(); } static int sceNetInetInit() { @@ -250,7 +205,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr // Save the mapping during setup. SOCKET hostSockets[256]{}; - for (int i = MIN_VALID_SOCKET; i < nfds; i++) { + for (int i = MIN_VALID_INET_SOCKET; i < nfds; i++) { if (readfds && (NetInetFD_ISSET(i, readfds))) { SOCKET sock = GetHostSocketFromInetSocket(i); hostSockets[i] = sock; @@ -299,7 +254,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (readfds != NULL) NetInetFD_ZERO(readfds); if (writefds != NULL) NetInetFD_ZERO(writefds); if (exceptfds != NULL) NetInetFD_ZERO(exceptfds); - for (int i = MIN_VALID_SOCKET; i < nfds; i++) { + for (int i = MIN_VALID_INET_SOCKET; i < nfds; i++) { if (readfds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &rdfds)) { NetInetFD_SET(i, readfds); } @@ -455,7 +410,7 @@ static int sceNetInetSocket(int domain, int type, int protocol) { return hleLogError(Log::sceNet, ERROR_NET_INTERNAL); } InetSocket *inetSock = &g_inetSockets[socket]; - inetSock->state = SocketState::Used; + inetSock->state = SocketState::UsedNetInet; inetSock->sock = hostSock; inetSock->domain = domain; inetSock->type = type; diff --git a/Core/HLE/sceNetInet.h b/Core/HLE/sceNetInet.h index daa319b77d..fb84e14350 100644 --- a/Core/HLE/sceNetInet.h +++ b/Core/HLE/sceNetInet.h @@ -186,24 +186,3 @@ int sceNetApctlConnect(int connIndex); int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout); int sceNetApctlTerm(); int sceNetApctlDisconnect(); - -enum class SocketState { - Unused, - Used, -}; - -// Internal socket state tracking -struct InetSocket { - SOCKET sock; // native socket - SocketState state; - // NOTE: These are the PSP types for now - int domain; - int type; - int protocol; - // These are the host types for convenience. - int hostDomain; - int hostType; - int hostProtocol; -}; - -extern InetSocket g_inetSockets[256]; diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index ac4968d511..a8b36afc1f 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -182,6 +182,7 @@ + @@ -444,6 +445,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index a2619dff45..109f5311da 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -1232,6 +1232,9 @@ HLE + + HLE + @@ -1948,6 +1951,9 @@ HLE + + HLE + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index a10eac82d2..34b2720c74 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -665,6 +665,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/HLE/HLE.cpp \ $(SRC)/Core/HLE/KUBridge.cpp \ $(SRC)/Core/HLE/NetInetConstants.cpp \ + $(SRC)/Core/HLE/SocketManager.cpp \ $(SRC)/Core/HLE/Plugins.cpp \ $(SRC)/Core/HLE/sceAdler.cpp \ $(SRC)/Core/HLE/sceAtrac.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index f534527ce9..924a4ff019 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -691,6 +691,7 @@ SOURCES_CXX += \ $(COREDIR)/HLE/HLE.cpp \ $(COREDIR)/HLE/KUBridge.cpp \ $(COREDIR)/HLE/NetInetConstants.cpp \ + $(COREDIR)/HLE/SocketManager.cpp \ $(COREDIR)/HLE/Plugins.cpp \ $(COREDIR)/HLE/sceSha256.cpp \ $(COREDIR)/HLE/sceSircs.cpp \ From 5026e924502df18de3374e1ad353850ed218671f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 14:46:45 +0100 Subject: [PATCH 09/19] Set min socket number to 61. Somehow this fixes Linux?? --- Core/HLE/SocketManager.h | 2 +- Core/HLE/sceNetInet.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index 0eaac03273..2e35dcb480 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -23,7 +23,7 @@ struct InetSocket { int hostProtocol; }; -#define MIN_VALID_INET_SOCKET 20 +#define MIN_VALID_INET_SOCKET 61 #define VALID_INET_SOCKET_COUNT 256 extern InetSocket g_inetSockets[VALID_INET_SOCKET_COUNT]; diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index f8bf83267a..740b1ffe6a 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -209,7 +209,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (readfds && (NetInetFD_ISSET(i, readfds))) { SOCKET sock = GetHostSocketFromInetSocket(i); hostSockets[i] = sock; - VERBOSE_LOG(Log::sceNet, "Input Read FD #%i", i); + DEBUG_LOG(Log::sceNet, "Input Read FD #%i (host: %d)", i, sock); if (rdcnt < FD_SETSIZE) { FD_SET(sock, &rdfds); // This might pointed to a non-existing socket or sockets belonged to other programs on Windows, because most of the time Windows socket have an id above 1k instead of 0-255 rdcnt++; @@ -218,7 +218,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (writefds && (NetInetFD_ISSET(i, writefds))) { SOCKET sock = GetHostSocketFromInetSocket(i); hostSockets[i] = sock; - VERBOSE_LOG(Log::sceNet, "Input Write FD #%i", i); + DEBUG_LOG(Log::sceNet, "Input Write FD #%i (host: %d)", i, sock); if (wrcnt < FD_SETSIZE) { FD_SET(sock, &wrfds); wrcnt++; @@ -227,7 +227,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (exceptfds && (NetInetFD_ISSET(i, exceptfds))) { SOCKET sock = GetHostSocketFromInetSocket(i); hostSockets[i] = sock; - VERBOSE_LOG(Log::sceNet, "Input Except FD #%i", i); + DEBUG_LOG(Log::sceNet, "Input Except FD #%i (host: %d)", i, sock); if (excnt < FD_SETSIZE) { FD_SET(sock, &exfds); excnt++; @@ -245,7 +245,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr tmout.tv_sec = timeout->tv_sec; tmout.tv_usec = timeout->tv_usec; } - VERBOSE_LOG(Log::sceNet, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, (int)tmout.tv_sec, (int)tmout.tv_usec); + DEBUG_LOG(Log::sceNet, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, (int)tmout.tv_sec, (int)tmout.tv_usec); // TODO: Simulate blocking behaviour when timeout = NULL to prevent PPSSPP from freezing int retval = select(nfds, readfds ? &rdfds : nullptr, writefds ? &wrfds : nullptr, exceptfds ? &exfds : nullptr, /*(timeout == NULL) ? NULL :*/ &tmout); if (retval < 0) { @@ -446,7 +446,7 @@ static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPt //memcpy(&sock->nonblocking, (int*)optval, std::min(sizeof(sock->nonblocking), optlen)); return hleLogSuccessI(Log::sceNet, 0); } - // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), + // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), // But Infrastructure/Online play might need to use broadcast for SSDP and to support LAN MP with real PSP /*else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_BROADCAST) { //memcpy(&sock->so_broadcast, (int*)optval, std::min(sizeof(sock->so_broadcast), optlen)); @@ -506,11 +506,11 @@ static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPt // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-bloking mode (ie. simulated blocking mode) if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_NBIO) { //*optlen = std::min(sizeof(sock->nonblocking), *optlen); - //memcpy((int*)optval, &sock->nonblocking, *optlen); + //memcpy((int*)optval, &sock->nonblocking, *optlen); //if (sock->nonblocking && *optlen>0) *optval = 0x80; // on true, returning 0x80 when retrieved using getsockopt? return hleLogSuccessI(Log::sceNet, 0); } - // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), + // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), // But Infrastructure/Online play might need to use broadcast for SSDP and to support LAN MP with real PSP /*else if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_BROADCAST) { // *optlen = std::min(sizeof(sock->so_broadcast), *optlen); @@ -760,8 +760,8 @@ static int sceNetInetCloseWithRST(int socket) { // Based on http://deepix.github.io/2016/10/21/tcprst.html struct linger sl {}; - sl.l_onoff = 1; // non-zero value enables linger option in kernel - sl.l_linger = 0; // timeout interval in seconds + sl.l_onoff = 1; // non-zero value enables linger option in kernel + sl.l_linger = 0; // timeout interval in seconds setsockopt(inetSock->sock, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl)); int retVal = closesocket(inetSock->sock); if (retVal == 0) { From 155a9f9dbcd35320234acd8f886325be4d6b3f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 14:59:40 +0100 Subject: [PATCH 10/19] Turn the socket manager into a class --- Core/HLE/SocketManager.cpp | 30 +++++++++--------- Core/HLE/SocketManager.h | 34 +++++++++++++-------- Core/HLE/proAdhoc.cpp | 1 + Core/HLE/sceNetInet.cpp | 62 +++++++++++++++++++------------------- 4 files changed, 70 insertions(+), 57 deletions(-) diff --git a/Core/HLE/SocketManager.cpp b/Core/HLE/SocketManager.cpp index 400d163677..03f8c0ee5c 100644 --- a/Core/HLE/SocketManager.cpp +++ b/Core/HLE/SocketManager.cpp @@ -3,36 +3,38 @@ #include -// We use this array from 1 and forward. It's probably not a good idea to return 0 as a socket. -InetSocket g_inetSockets[256]; +SocketManager g_socketManager; static std::mutex g_socketMutex; // TODO: Remove once the adhoc thread is gone -int AllocInetSocket() { +InetSocket *SocketManager::AllocSocket(int *index) { std::lock_guard guard(g_socketMutex); - for (int i = MIN_VALID_INET_SOCKET; i < ARRAY_SIZE(g_inetSockets); i++) { - if (g_inetSockets[i].state == SocketState::Unused) { - return i; + for (int i = MIN_VALID_INET_SOCKET; i < ARRAY_SIZE(inetSockets_); i++) { + if (inetSockets_[i].state == SocketState::Unused) { + *index = i; + return inetSockets_ + i; } } _dbg_assert_(false); + ERROR_LOG(Log::sceNet, "Ran out of socket handles! This is BAD."); + *index = 0; return 0; } -bool GetInetSocket(int sock, InetSocket **inetSocket) { +bool SocketManager::GetInetSocket(int sock, InetSocket **inetSocket) { std::lock_guard guard(g_socketMutex); - if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { + if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(inetSockets_) || inetSockets_[sock].state == SocketState::Unused) { *inetSocket = nullptr; return false; } - *inetSocket = &g_inetSockets[sock]; + *inetSocket = inetSockets_ + sock; return true; } // Simplified mappers, only really useful in select/poll -SOCKET GetHostSocketFromInetSocket(int sock) { +SOCKET SocketManager::GetHostSocketFromInetSocket(int sock) { std::lock_guard guard(g_socketMutex); - if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(g_inetSockets) || g_inetSockets[sock].state == SocketState::Unused) { + if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(inetSockets_) || inetSockets_[sock].state == SocketState::Unused) { _dbg_assert_(false); return -1; } @@ -40,11 +42,11 @@ SOCKET GetHostSocketFromInetSocket(int sock) { // Map 0 to 0, special case. return 0; } - return g_inetSockets[sock].sock; + return inetSockets_[sock].sock; } -void CloseAllSockets() { - for (auto &sock : g_inetSockets) { +void SocketManager::CloseAll() { + for (auto &sock : inetSockets_) { if (sock.state != SocketState::Unused) { closesocket(sock.sock); } diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index 2e35dcb480..2a5ee61fe0 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -13,22 +13,32 @@ enum class SocketState { struct InetSocket { SOCKET sock; // native socket SocketState state; - // NOTE: These are the PSP types for now + // NOTE: These are the PSP types. Can be converted to the host types if needed. int domain; int type; int protocol; - // These are the host types for convenience. - int hostDomain; - int hostType; - int hostProtocol; }; -#define MIN_VALID_INET_SOCKET 61 -#define VALID_INET_SOCKET_COUNT 256 +class SocketManager { +public: + enum { + VALID_INET_SOCKET_COUNT = 256, + MIN_VALID_INET_SOCKET = 61, + }; -extern InetSocket g_inetSockets[VALID_INET_SOCKET_COUNT]; + InetSocket *AllocSocket(int *index); + bool GetInetSocket(int sock, InetSocket **inetSocket); + SOCKET GetHostSocketFromInetSocket(int sock); + void CloseAll(); -int AllocInetSocket(); -bool GetInetSocket(int sock, InetSocket **inetSocket); -SOCKET GetHostSocketFromInetSocket(int sock); -void CloseAllSockets(); + // For debugger + const InetSocket *Sockets() { + return inetSockets_; + } + +private: + // We use this array from MIN_VALID_INET_SOCKET and forward. It's probably not a good idea to return 0 as a socket. + InetSocket inetSockets_[VALID_INET_SOCKET_COUNT]; +}; + +extern SocketManager g_socketManager; diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 67849b3445..5deb4ce205 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -280,6 +280,7 @@ SceNetAdhocctlPeerInfo* findFriendByIP(uint32_t ip) { return peer; } +// fd is a host socket int IsSocketReady(int fd, bool readfd, bool writefd, int* errorcode, int timeoutUS) { fd_set readfds, writefds; timeval tval; diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 740b1ffe6a..5f1ca06dfd 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -33,7 +33,7 @@ void __NetInetShutdown() { } netInetInited = false; - CloseAllSockets(); + g_socketManager.CloseAll(); } static int sceNetInetInit() { @@ -123,7 +123,7 @@ static int sceNetInetGetpeername(int socket, u32 namePtr, u32 namelenPtr) { } InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -157,7 +157,7 @@ static int sceNetInetGetsockname(int socket, u32 namePtr, u32 namelenPtr) { } InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -205,9 +205,9 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr // Save the mapping during setup. SOCKET hostSockets[256]{}; - for (int i = MIN_VALID_INET_SOCKET; i < nfds; i++) { + for (int i = SocketManager::MIN_VALID_INET_SOCKET; i < nfds; i++) { if (readfds && (NetInetFD_ISSET(i, readfds))) { - SOCKET sock = GetHostSocketFromInetSocket(i); + SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); hostSockets[i] = sock; DEBUG_LOG(Log::sceNet, "Input Read FD #%i (host: %d)", i, sock); if (rdcnt < FD_SETSIZE) { @@ -216,7 +216,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr } } if (writefds && (NetInetFD_ISSET(i, writefds))) { - SOCKET sock = GetHostSocketFromInetSocket(i); + SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); hostSockets[i] = sock; DEBUG_LOG(Log::sceNet, "Input Write FD #%i (host: %d)", i, sock); if (wrcnt < FD_SETSIZE) { @@ -225,7 +225,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr } } if (exceptfds && (NetInetFD_ISSET(i, exceptfds))) { - SOCKET sock = GetHostSocketFromInetSocket(i); + SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); hostSockets[i] = sock; DEBUG_LOG(Log::sceNet, "Input Except FD #%i (host: %d)", i, sock); if (excnt < FD_SETSIZE) { @@ -254,7 +254,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (readfds != NULL) NetInetFD_ZERO(readfds); if (writefds != NULL) NetInetFD_ZERO(writefds); if (exceptfds != NULL) NetInetFD_ZERO(exceptfds); - for (int i = MIN_VALID_INET_SOCKET; i < nfds; i++) { + for (int i = SocketManager::MIN_VALID_INET_SOCKET; i < nfds; i++) { if (readfds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &rdfds)) { NetInetFD_SET(i, readfds); } @@ -296,7 +296,7 @@ int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout) { // timeout in milisecond if (fdarray[i].fd > maxfd) { maxfd = fdarray[i].fd; } - SOCKET hostSocket = GetHostSocketFromInetSocket(fdarray[i].fd); + SOCKET hostSocket = g_socketManager.GetHostSocketFromInetSocket(fdarray[i].fd); FD_SET(hostSocket, &readfds); FD_SET(hostSocket, &writefds); FD_SET(hostSocket, &exceptfds); @@ -317,7 +317,7 @@ int sceNetInetPoll(u32 fdsPtr, u32 nfds, int timeout) { // timeout in milisecond retval = 0; for (int i = 0; i < (s32)nfds; i++) { - SOCKET hostSocket = GetHostSocketFromInetSocket(fdarray[i].fd); + SOCKET hostSocket = g_socketManager.GetHostSocketFromInetSocket(fdarray[i].fd); if ((fdarray[i].events & (INET_POLLRDNORM | INET_POLLIN)) && FD_ISSET(hostSocket, &readfds)) fdarray[i].revents |= (INET_POLLRDNORM | INET_POLLIN); //POLLIN_SET if ((fdarray[i].events & (INET_POLLWRNORM | INET_POLLOUT)) && FD_ISSET(hostSocket, &writefds)) @@ -337,7 +337,7 @@ static int sceNetInetRecv(int socket, u32 bufPtr, u32 bufLen, u32 flags) { DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetRecv(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -364,7 +364,7 @@ static int sceNetInetSend(int socket, u32 bufPtr, u32 bufLen, u32 flags) { DEBUG_LOG(Log::sceNet, "UNTESTED sceNetInetSend(%i, %08x, %i, %08x) at %08x", socket, bufPtr, bufLen, flags, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -404,20 +404,19 @@ static int sceNetInetSocket(int domain, int type, int protocol) { // Register the socket. - int socket = AllocInetSocket(); + int socket; + + InetSocket *inetSock = g_socketManager.AllocSocket(&socket); if (socket < 0) { // Alloc already logged. Let's bail. return hleLogError(Log::sceNet, ERROR_NET_INTERNAL); } - InetSocket *inetSock = &g_inetSockets[socket]; + inetSock->state = SocketState::UsedNetInet; inetSock->sock = hostSock; inetSock->domain = domain; inetSock->type = type; inetSock->protocol = protocol; - inetSock->hostDomain = hostDomain; - inetSock->hostType = hostType; - inetSock->hostProtocol = hostProtocol; // Ignore SIGPIPE when supported (ie. BSD/MacOS) setSockNoSIGPIPE(hostSock, 1); @@ -427,6 +426,7 @@ static int sceNetInetSocket(int domain, int type, int protocol) { setSockReuseAddrPort(hostSock); // Disable Connection Reset error on UDP to avoid strange behavior setUDPConnReset(hostSock, false); + return hleLogSuccessI(Log::sceNet, socket); } @@ -434,7 +434,7 @@ static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPt WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i, %i, %08x, %i) at %08x", __FUNCTION__, socket, level, optname, optvalPtr, optlen, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -495,7 +495,7 @@ static int sceNetInetGetsockopt(int socket, int level, int optname, u32 optvalPt WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i, %i, %08x, %08x) at %08x", __FUNCTION__, socket, level, optname, optvalPtr, optlenPtr, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -560,7 +560,7 @@ static int sceNetInetBind(int socket, u32 namePtr, int namelen) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i) at %08x", __FUNCTION__, socket, namePtr, namelen, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -614,7 +614,7 @@ static int sceNetInetConnect(int socket, u32 sockAddrPtr, int sockAddrLen) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i) at %08x", __FUNCTION__, socket, sockAddrPtr, sockAddrLen, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -652,7 +652,7 @@ static int sceNetInetListen(int socket, int backlog) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i) at %08x", __FUNCTION__, socket, backlog, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -669,7 +669,7 @@ static int sceNetInetAccept(int socket, u32 addrPtr, u32 addrLenPtr) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, addrPtr, addrLenPtr, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -702,7 +702,7 @@ static int sceNetInetShutdown(int socket, int how) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i, %i) at %08x", __FUNCTION__, socket, how, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -722,7 +722,7 @@ static int sceNetInetSocketAbort(int socket) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i)", __FUNCTION__, socket); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -735,7 +735,7 @@ static int sceNetInetClose(int socket) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -754,7 +754,7 @@ static int sceNetInetCloseWithRST(int socket) { WARN_LOG(Log::sceNet, "UNTESTED %s(%i) at %08x", __FUNCTION__, socket, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -777,7 +777,7 @@ static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 DEBUG_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i, %08x, %08x, %08x) at %08x", __FUNCTION__, socket, bufferPtr, len, flags, fromPtr, fromlenPtr, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -825,7 +825,7 @@ static int sceNetInetSendto(int socket, u32 bufferPtr, int len, int flags, u32 t DEBUG_LOG(Log::sceNet, "UNTESTED %s(%i, %08x, %i, %08x, %08x, %d) at %08x", __FUNCTION__, socket, bufferPtr, len, flags, toPtr, tolen, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -909,7 +909,7 @@ static int sceNetInetSendmsg(int socket, u32 msghdrPtr, int flags) { } InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } @@ -1103,7 +1103,7 @@ static int sceNetInetRecvmsg(int socket, u32 msghdrPtr, int flags) { ERROR_LOG(Log::sceNet, "UNIMPL %s(%i, %08x, %08x) at %08x", __FUNCTION__, socket, msghdrPtr, flags, currentMIPS->pc); InetSocket *inetSock; - if (!GetInetSocket(socket, &inetSock)) { + if (!g_socketManager.GetInetSocket(socket, &inetSock)) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } From a1744a6989a5afed1a9480aa43fbaf150f4c6ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 15:27:54 +0100 Subject: [PATCH 11/19] Add SocketManager::CreateSocket for convenience --- Core/HLE/SocketManager.cpp | 27 ++++++++++++++++++++++++--- Core/HLE/SocketManager.h | 2 +- Core/HLE/sceNetInet.cpp | 35 ++++++++--------------------------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/Core/HLE/SocketManager.cpp b/Core/HLE/SocketManager.cpp index 03f8c0ee5c..e740db5c62 100644 --- a/Core/HLE/SocketManager.cpp +++ b/Core/HLE/SocketManager.cpp @@ -1,3 +1,5 @@ +#include "Common/Net/SocketCompat.h" +#include "Core/HLE/NetInetConstants.h" #include "Core/HLE/SocketManager.h" #include "Common/Log.h" @@ -6,19 +8,38 @@ SocketManager g_socketManager; static std::mutex g_socketMutex; // TODO: Remove once the adhoc thread is gone -InetSocket *SocketManager::AllocSocket(int *index) { +InetSocket *SocketManager::CreateSocket(int *index, SocketState state, int domain, int type, int protocol) { + _dbg_assert_(state != SocketState::Unused); + + int hostDomain = convertSocketDomainPSP2Host(domain); + int hostType = convertSocketTypePSP2Host(type); + int hostProtocol = convertSocketProtoPSP2Host(protocol); + + SOCKET hostSock = ::socket(hostDomain, hostType, hostProtocol); + if (hostSock < 0) { + return nullptr; + } + std::lock_guard guard(g_socketMutex); + for (int i = MIN_VALID_INET_SOCKET; i < ARRAY_SIZE(inetSockets_); i++) { if (inetSockets_[i].state == SocketState::Unused) { *index = i; - return inetSockets_ + i; + InetSocket *inetSock = inetSockets_ + i; + inetSock->sock = hostSock; + inetSock->state = state; + inetSock->domain = domain; + inetSock->type = type; + inetSock->protocol = protocol; + return inetSock; } } _dbg_assert_(false); ERROR_LOG(Log::sceNet, "Ran out of socket handles! This is BAD."); + closesocket(hostSock); *index = 0; - return 0; + return nullptr; } bool SocketManager::GetInetSocket(int sock, InetSocket **inetSocket) { diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index 2a5ee61fe0..888764ed79 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -26,7 +26,7 @@ public: MIN_VALID_INET_SOCKET = 61, }; - InetSocket *AllocSocket(int *index); + InetSocket *CreateSocket(int *index, SocketState state, int domain, int type, int protocol); bool GetInetSocket(int sock, InetSocket **inetSocket); SOCKET GetHostSocketFromInetSocket(int sock); void CloseAll(); diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 5f1ca06dfd..1aa38c7204 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -392,40 +392,21 @@ static int sceNetInetSocket(int domain, int type, int protocol) { WARN_LOG(Log::sceNet, "UNTESTED sceNetInetSocket(%i, %i, %i) at %08x", domain, type, protocol, currentMIPS->pc); DEBUG_LOG(Log::sceNet, "Socket: Domain = %s, Type = %s, Protocol = %s", inetSocketDomain2str(domain).c_str(), inetSocketType2str(type).c_str(), inetSocketProto2str(protocol).c_str()); - int hostDomain = convertSocketDomainPSP2Host(domain); - int hostType = convertSocketTypePSP2Host(type); - int hostProtocol = convertSocketProtoPSP2Host(protocol); - - SOCKET hostSock = ::socket(hostDomain, hostType, hostProtocol); - if (hostSock < 0) { - inetLastErrno = socket_errno; - return hleLogError(Log::sceNet, hostSock, "errno = %d", inetLastErrno); - } - - // Register the socket. - int socket; - - InetSocket *inetSock = g_socketManager.AllocSocket(&socket); - if (socket < 0) { - // Alloc already logged. Let's bail. - return hleLogError(Log::sceNet, ERROR_NET_INTERNAL); + InetSocket *inetSock = g_socketManager.CreateSocket(&socket, SocketState::UsedNetInet, domain, type, protocol); + if (!inetSock) { + inetLastErrno = socket_errno; + return hleLogError(Log::sceNet, -1, "errno = %d", inetLastErrno); } - inetSock->state = SocketState::UsedNetInet; - inetSock->sock = hostSock; - inetSock->domain = domain; - inetSock->type = type; - inetSock->protocol = protocol; - // Ignore SIGPIPE when supported (ie. BSD/MacOS) - setSockNoSIGPIPE(hostSock, 1); + setSockNoSIGPIPE(inetSock->sock, 1); // TODO: We should always use non-blocking mode and simulate blocking mode - changeBlockingMode(hostSock, 1); + changeBlockingMode(inetSock->sock, 1); // Enable Port Re-use, required for multiple-instance - setSockReuseAddrPort(hostSock); + setSockReuseAddrPort(inetSock->sock); // Disable Connection Reset error on UDP to avoid strange behavior - setUDPConnReset(hostSock, false); + setUDPConnReset(inetSock->sock, false); return hleLogSuccessI(Log::sceNet, socket); } From b97b0e4cf6bcc297d94ce0b53d0c2453974f81c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 16:25:15 +0100 Subject: [PATCH 12/19] Add Close method to socket manager --- Core/HLE/SocketManager.cpp | 11 +++++++++++ Core/HLE/SocketManager.h | 3 +++ Core/HLE/sceNetInet.cpp | 21 +++++---------------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Core/HLE/SocketManager.cpp b/Core/HLE/SocketManager.cpp index e740db5c62..6e4e904528 100644 --- a/Core/HLE/SocketManager.cpp +++ b/Core/HLE/SocketManager.cpp @@ -42,6 +42,17 @@ InetSocket *SocketManager::CreateSocket(int *index, SocketState state, int domai return nullptr; } +bool SocketManager::Close(InetSocket *inetSocket) { + _dbg_assert_(inetSocket->state != SocketState::Unused); + if (closesocket(inetSocket->sock) != 0) { + ERROR_LOG(Log::sceNet, "closesocket(%d) failed", inetSocket->sock); + return false; + } + inetSocket->state = SocketState::Unused; + inetSocket->sock = 0; + return true; +} + bool SocketManager::GetInetSocket(int sock, InetSocket **inetSocket) { std::lock_guard guard(g_socketMutex); if (sock < MIN_VALID_INET_SOCKET || sock >= ARRAY_SIZE(inetSockets_) || inetSockets_[sock].state == SocketState::Unused) { diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index 888764ed79..c2df2569a6 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -19,6 +19,8 @@ struct InetSocket { int protocol; }; +// Only use this for sockets whose ID are exposed to the game. +// Don't really need to bother with the others, as the game doesn't know about them. class SocketManager { public: enum { @@ -29,6 +31,7 @@ public: InetSocket *CreateSocket(int *index, SocketState state, int domain, int type, int protocol); bool GetInetSocket(int sock, InetSocket **inetSocket); SOCKET GetHostSocketFromInetSocket(int sock); + bool Close(InetSocket *inetSocket); void CloseAll(); // For debugger diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 1aa38c7204..5ea3ee0c4f 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -720,14 +720,9 @@ static int sceNetInetClose(int socket) { return hleLogError(Log::sceNet, ERROR_INET_EBADF, "Bad socket #%d", socket); } - int retVal = closesocket(inetSock->sock); - if (retVal == 0) { - inetSock->sock = 0; - inetSock->state = SocketState::Unused; - } else { - ERROR_LOG(Log::sceNet, "closesocket(%d) failed (socket=%d)", inetSock->sock, socket); - } - return hleLogSuccessI(Log::sceNet, retVal); + g_socketManager.Close(inetSock); + + return hleLogSuccessI(Log::sceNet, 0); } // TODO: How is this different than just sceNetInetClose? @@ -744,14 +739,8 @@ static int sceNetInetCloseWithRST(int socket) { sl.l_onoff = 1; // non-zero value enables linger option in kernel sl.l_linger = 0; // timeout interval in seconds setsockopt(inetSock->sock, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl)); - int retVal = closesocket(inetSock->sock); - if (retVal == 0) { - inetSock->sock = 0; - inetSock->state = SocketState::Unused; - } else { - ERROR_LOG(Log::sceNet, "closesocket(%d) failed (socket=%d)", inetSock->sock, socket); - } - return hleLogSuccessI(Log::sceNet, retVal); + g_socketManager.Close(inetSock); + return hleLogSuccessI(Log::sceNet, 0); } static int sceNetInetRecvfrom(int socket, u32 bufferPtr, int len, int flags, u32 fromPtr, u32 fromlenPtr) { From c9c594462f41cef722851f8d9d1a15ed1f5628fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 16:25:44 +0100 Subject: [PATCH 13/19] Track non-blocking state --- Core/HLE/SocketManager.cpp | 1 + Core/HLE/SocketManager.h | 1 + Core/HLE/proAdhoc.cpp | 2 ++ Core/HLE/sceNetInet.cpp | 6 ++++-- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Core/HLE/SocketManager.cpp b/Core/HLE/SocketManager.cpp index 6e4e904528..4b2b353901 100644 --- a/Core/HLE/SocketManager.cpp +++ b/Core/HLE/SocketManager.cpp @@ -31,6 +31,7 @@ InetSocket *SocketManager::CreateSocket(int *index, SocketState state, int domai inetSock->domain = domain; inetSock->type = type; inetSock->protocol = protocol; + inetSock->nonblocking = false; return inetSock; } } diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index c2df2569a6..549419e178 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -17,6 +17,7 @@ struct InetSocket { int domain; int type; int protocol; + bool nonblocking; }; // Only use this for sockets whose ID are exposed to the game. diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 5deb4ce205..a4779e7e7e 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -93,7 +93,9 @@ int actionAfterMatchingMipsCall; // Broadcast MAC uint8_t broadcastMAC[ETHER_ADDR_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +// NOTE: This does not need to be managed by the socket manager - not exposed to the game. std::atomic metasocket((int)INVALID_SOCKET); + SceNetAdhocctlParameter parameter; SceNetAdhocctlAdhocId product_code; std::thread friendFinderThread; diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 5ea3ee0c4f..9c00f0e581 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -422,9 +422,11 @@ static int sceNetInetSetsockopt(int socket, int level, int optname, u32 optvalPt u32_le* optval = (u32_le*)Memory::GetPointer(optvalPtr); DEBUG_LOG(Log::sceNet, "SockOpt: Level = %s, OptName = %s, OptValue = %d", inetSockoptLevel2str(level).c_str(), inetSockoptName2str(optname, level).c_str(), *optval); timeval tval{}; - // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-bloking mode (ie. simulated blocking mode) + // TODO: Ignoring SO_NBIO/SO_NONBLOCK flag if we always use non-blocking mode (ie. simulated blocking mode) if (level == PSP_NET_INET_SOL_SOCKET && optname == PSP_NET_INET_SO_NBIO) { - //memcpy(&sock->nonblocking, (int*)optval, std::min(sizeof(sock->nonblocking), optlen)); + int nonblocking; + memcpy(&nonblocking, (int*)optval, sizeof(int)); + inetSock->nonblocking = nonblocking; return hleLogSuccessI(Log::sceNet, 0); } // FIXME: Should we ignore SO_BROADCAST flag since we are using fake broadcast (ie. only broadcast to friends), From 698b73dd15e33e70e3da86cc56d4e04f0c4a9502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 16:26:26 +0100 Subject: [PATCH 14/19] ImDebugger: Add Np and Sockets debugger windows, on a new Network menu --- Core/HLE/SocketManager.cpp | 10 +++++ Core/HLE/SocketManager.h | 2 + UI/ImDebugger/ImDebugger.cpp | 81 ++++++++++++++++++++++++++++++++++++ UI/ImDebugger/ImDebugger.h | 3 ++ 4 files changed, 96 insertions(+) diff --git a/Core/HLE/SocketManager.cpp b/Core/HLE/SocketManager.cpp index 4b2b353901..533635abec 100644 --- a/Core/HLE/SocketManager.cpp +++ b/Core/HLE/SocketManager.cpp @@ -87,3 +87,13 @@ void SocketManager::CloseAll() { sock.sock = 0; } } + +const char *SocketStateToString(SocketState state) { + switch (state) { + case SocketState::Unused: return "unused"; + case SocketState::UsedNetInet: return "netInet"; + case SocketState::UsedProAdhoc: return "proAdhoc"; + default: + return "N/A"; + } +} diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index 549419e178..f16883b761 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -9,6 +9,8 @@ enum class SocketState { UsedProAdhoc, }; +const char *SocketStateToString(SocketState state); + // Internal socket state tracking struct InetSocket { SOCKET sock; // native socket diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 9cb082cf68..f37cdbb9fa 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -19,6 +19,9 @@ #include "Core/Debugger/SymbolMap.h" #include "Core/MemMap.h" #include "Core/HLE/HLE.h" +#include "Core/HLE/SocketManager.h" +#include "Core/HLE/NetInetConstants.h" +#include "Core/HLE/sceNp.h" #include "Common/System/Request.h" #include "Core/HLE/sceAtrac.h" @@ -475,6 +478,69 @@ static void DrawKernelObjects(ImConfig &cfg) { ImGui::End(); } +static void DrawNp(ImConfig &cfg) { + if (!ImGui::Begin("NP", &cfg.npOpen)) { + ImGui::End(); + return; + } + + ImGui::Text("Signed in: %d", npSigninState); + ImGui::Text("Title ID: %s", npTitleId.data); + + SceNpId id{}; + NpGetNpId(&id); + ImGui::Text("User Handle: %s", id.handle); + ImGui::End(); +} + +static void DrawSockets(ImConfig &cfg) { + if (!ImGui::Begin("Sockets", &cfg.socketsOpen)) { + ImGui::End(); + return; + } + if (ImGui::BeginTable("sock", 7, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) { + ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Host", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Non-blocking", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Created by", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Domain", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Protocol", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableHeadersRow(); + + for (int i = SocketManager::MIN_VALID_INET_SOCKET; i < SocketManager::VALID_INET_SOCKET_COUNT; i++) { + InetSocket *inetSocket; + if (!g_socketManager.GetInetSocket(i, &inetSocket)) { + continue; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%d", i); + ImGui::TableNextColumn(); + ImGui::Text("%d", (int)inetSocket->sock); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(inetSocket->nonblocking ? "Non-blocking" : "Blocking"); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(SocketStateToString(inetSocket->state)); + ImGui::TableNextColumn(); + std::string str = inetSocketDomain2str(inetSocket->domain); + ImGui::TextUnformatted(str.c_str()); + ImGui::TableNextColumn(); + str = inetSocketType2str(inetSocket->type); + ImGui::TextUnformatted(str.c_str()); + ImGui::TableNextColumn(); + str = inetSocketProto2str(inetSocket->protocol); + ImGui::TextUnformatted(str.c_str()); + ImGui::TableNextColumn(); + } + + ImGui::EndTable(); + } + ImGui::End(); +} + static const char *MemCheckConditionToString(MemCheckCondition cond) { switch (cond) { case MEMCHECK_READ: return "Read"; @@ -1070,6 +1136,11 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu ImGui::MenuItem("Decoder contexts", nullptr, &cfg_.audioDecodersOpen); ImGui::EndMenu(); } + if (ImGui::BeginMenu("Network")) { + ImGui::MenuItem("Sockets", nullptr, &cfg_.socketsOpen); + ImGui::MenuItem("NP", nullptr, &cfg_.npOpen); + ImGui::EndMenu(); + } if (ImGui::BeginMenu("Tools")) { ImGui::MenuItem("Debug stats", nullptr, &cfg_.debugStatsOpen); ImGui::MenuItem("Struct viewer", nullptr, &cfg_.structViewerOpen); @@ -1188,6 +1259,14 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu } } + if (cfg_.socketsOpen) { + DrawSockets(cfg_); + } + + if (cfg_.npOpen) { + DrawNp(cfg_); + } + // Process UI commands switch (control.command.cmd) { case ImCmd::SHOW_IN_CPU_DISASM: @@ -1543,6 +1622,8 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) { sync.Sync("geDebuggerOpen", &geDebuggerOpen, false); sync.Sync("geStateOpen", &geStateOpen, false); sync.Sync("schedulerOpen", &schedulerOpen, false); + sync.Sync("socketsOpen", &socketsOpen, false); + sync.Sync("npOpen", &npOpen, false); sync.Sync("pixelViewerOpen", &pixelViewerOpen, false); for (int i = 0; i < 4; i++) { char name[64]; diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index c0fa9a037d..b9d621e6d3 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -11,6 +11,7 @@ #include "Common/System/Request.h" #include "Core/Core.h" +#include "Core/HLE/SocketManager.h" #include "Core/Debugger/DisassemblyManager.h" #include "Core/Debugger/DebugInterface.h" @@ -140,6 +141,8 @@ struct ImConfig { bool schedulerOpen; bool watchOpen; bool pixelViewerOpen; + bool npOpen; + bool socketsOpen; bool memViewOpen[4]; // HLE explorer settings From 728268bb3f42da81aa202f60b617f14cdf11ad10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 16:35:09 +0100 Subject: [PATCH 15/19] Better CreateSocket errro handling --- Core/HLE/SocketManager.cpp | 5 ++++- Core/HLE/SocketManager.h | 2 +- Core/HLE/sceNetInet.cpp | 3 +-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Core/HLE/SocketManager.cpp b/Core/HLE/SocketManager.cpp index 533635abec..241b6fca53 100644 --- a/Core/HLE/SocketManager.cpp +++ b/Core/HLE/SocketManager.cpp @@ -8,7 +8,7 @@ SocketManager g_socketManager; static std::mutex g_socketMutex; // TODO: Remove once the adhoc thread is gone -InetSocket *SocketManager::CreateSocket(int *index, SocketState state, int domain, int type, int protocol) { +InetSocket *SocketManager::CreateSocket(int *index, int *returned_errno, SocketState state, int domain, int type, int protocol) { _dbg_assert_(state != SocketState::Unused); int hostDomain = convertSocketDomainPSP2Host(domain); @@ -17,6 +17,7 @@ InetSocket *SocketManager::CreateSocket(int *index, SocketState state, int domai SOCKET hostSock = ::socket(hostDomain, hostType, hostProtocol); if (hostSock < 0) { + *returned_errno = socket_errno; return nullptr; } @@ -32,6 +33,7 @@ InetSocket *SocketManager::CreateSocket(int *index, SocketState state, int domai inetSock->type = type; inetSock->protocol = protocol; inetSock->nonblocking = false; + *returned_errno = 0; return inetSock; } } @@ -40,6 +42,7 @@ InetSocket *SocketManager::CreateSocket(int *index, SocketState state, int domai ERROR_LOG(Log::sceNet, "Ran out of socket handles! This is BAD."); closesocket(hostSock); *index = 0; + *returned_errno = ENOMEM; // or something.. return nullptr; } diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index f16883b761..ecc9055112 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -31,7 +31,7 @@ public: MIN_VALID_INET_SOCKET = 61, }; - InetSocket *CreateSocket(int *index, SocketState state, int domain, int type, int protocol); + InetSocket *CreateSocket(int *index, int *returned_errno, SocketState state, int domain, int type, int protocol); bool GetInetSocket(int sock, InetSocket **inetSocket); SOCKET GetHostSocketFromInetSocket(int sock); bool Close(InetSocket *inetSocket); diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 9c00f0e581..457b15bc72 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -393,9 +393,8 @@ static int sceNetInetSocket(int domain, int type, int protocol) { DEBUG_LOG(Log::sceNet, "Socket: Domain = %s, Type = %s, Protocol = %s", inetSocketDomain2str(domain).c_str(), inetSocketType2str(type).c_str(), inetSocketProto2str(protocol).c_str()); int socket; - InetSocket *inetSock = g_socketManager.CreateSocket(&socket, SocketState::UsedNetInet, domain, type, protocol); + InetSocket *inetSock = g_socketManager.CreateSocket(&socket, &inetLastErrno, SocketState::UsedNetInet, domain, type, protocol); if (!inetSock) { - inetLastErrno = socket_errno; return hleLogError(Log::sceNet, -1, "errno = %d", inetLastErrno); } From 77fcb181a51957532f9b72b8b1cdc3d377669f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 17:01:27 +0100 Subject: [PATCH 16/19] Just some code formatting --- Core/HLE/sceNetInet.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 457b15bc72..98480132db 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -193,7 +193,9 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr // First, translate the specified fd_sets to host sockets. fd_set rdfds, wrfds, exfds; - FD_ZERO(&rdfds); FD_ZERO(&wrfds); FD_ZERO(&exfds); + FD_ZERO(&rdfds); + FD_ZERO(&wrfds); + FD_ZERO(&exfds); if (nfds > 256) { ERROR_LOG(Log::sceNet, "Bad nfds value: %d", nfds); @@ -213,6 +215,8 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (rdcnt < FD_SETSIZE) { FD_SET(sock, &rdfds); // This might pointed to a non-existing socket or sockets belonged to other programs on Windows, because most of the time Windows socket have an id above 1k instead of 0-255 rdcnt++; + } else { + ERROR_LOG(Log::sceNet, "Hit set size (rd)"); } } if (writefds && (NetInetFD_ISSET(i, writefds))) { @@ -222,6 +226,8 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (wrcnt < FD_SETSIZE) { FD_SET(sock, &wrfds); wrcnt++; + } else { + ERROR_LOG(Log::sceNet, "Hit set size (wr)"); } } if (exceptfds && (NetInetFD_ISSET(i, exceptfds))) { @@ -231,6 +237,8 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (excnt < FD_SETSIZE) { FD_SET(sock, &exfds); excnt++; + } else { + ERROR_LOG(Log::sceNet, "Hit set size (exc)"); } } } @@ -241,7 +249,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr _dbg_assert_(excnt < FD_SETSIZE); timeval tmout = { 5, 543210 }; // Workaround timeout value when timeout = NULL - if (timeout != NULL) { + if (timeout) { tmout.tv_sec = timeout->tv_sec; tmout.tv_usec = timeout->tv_usec; } @@ -251,9 +259,16 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr if (retval < 0) { ERROR_LOG(Log::sceNet, "selected returned an error, TODO"); } - if (readfds != NULL) NetInetFD_ZERO(readfds); - if (writefds != NULL) NetInetFD_ZERO(writefds); - if (exceptfds != NULL) NetInetFD_ZERO(exceptfds); + + // Convert the results back to PSP fd_sets. + + if (readfds) + NetInetFD_ZERO(readfds); + if (writefds) + NetInetFD_ZERO(writefds); + if (exceptfds) + NetInetFD_ZERO(exceptfds); + for (int i = SocketManager::MIN_VALID_INET_SOCKET; i < nfds; i++) { if (readfds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &rdfds)) { NetInetFD_SET(i, readfds); From f84ec05140ddec65423f614aee1acba89433072e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 17:02:39 +0100 Subject: [PATCH 17/19] Strange buildfix --- Common/ArmEmitter.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 7570e44479..c6fe15dc17 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -31,6 +31,9 @@ #define IS_SIGNED 1 << 1 #define ROUND_TO_ZERO 1 << 2 +// Unclear why we suddenly need this. +#undef VMIN + namespace ArmGen { enum ARMReg From cddd3d439d39e151d3d73f2d311e1a09798264bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 18:03:39 +0100 Subject: [PATCH 18/19] Crashfix with asan, minor --- Core/HLE/sceNetInet.cpp | 16 ++++++++++++---- UI/ImDebugger/ImDebugger.cpp | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 98480132db..7d7805d027 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -210,6 +210,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr for (int i = SocketManager::MIN_VALID_INET_SOCKET; i < nfds; i++) { if (readfds && (NetInetFD_ISSET(i, readfds))) { SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); + _dbg_assert_(sock != 0); hostSockets[i] = sock; DEBUG_LOG(Log::sceNet, "Input Read FD #%i (host: %d)", i, sock); if (rdcnt < FD_SETSIZE) { @@ -221,6 +222,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr } if (writefds && (NetInetFD_ISSET(i, writefds))) { SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); + _dbg_assert_(sock != 0); hostSockets[i] = sock; DEBUG_LOG(Log::sceNet, "Input Write FD #%i (host: %d)", i, sock); if (wrcnt < FD_SETSIZE) { @@ -232,6 +234,7 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr } if (exceptfds && (NetInetFD_ISSET(i, exceptfds))) { SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); + _dbg_assert_(sock != 0); hostSockets[i] = sock; DEBUG_LOG(Log::sceNet, "Input Except FD #%i (host: %d)", i, sock); if (excnt < FD_SETSIZE) { @@ -255,9 +258,10 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr } DEBUG_LOG(Log::sceNet, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, (int)tmout.tv_sec, (int)tmout.tv_usec); // TODO: Simulate blocking behaviour when timeout = NULL to prevent PPSSPP from freezing + // Note: select can overwrite tmout. int retval = select(nfds, readfds ? &rdfds : nullptr, writefds ? &wrfds : nullptr, exceptfds ? &exfds : nullptr, /*(timeout == NULL) ? NULL :*/ &tmout); if (retval < 0) { - ERROR_LOG(Log::sceNet, "selected returned an error, TODO"); + ERROR_LOG(Log::sceNet, "select returned an error, TODO"); } // Convert the results back to PSP fd_sets. @@ -270,13 +274,17 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr NetInetFD_ZERO(exceptfds); for (int i = SocketManager::MIN_VALID_INET_SOCKET; i < nfds; i++) { - if (readfds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &rdfds)) { + if (hostSockets[i] == 0) { + continue; + } + if (readfds && FD_ISSET(hostSockets[i], &rdfds)) { + NetInetFD_SET(i, readfds); } - if (writefds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &wrfds)) { + if (writefds && FD_ISSET(hostSockets[i], &wrfds)) { NetInetFD_SET(i, writefds); } - if (exceptfds && hostSockets[i] != 0 && FD_ISSET(hostSockets[i], &exfds)) { + if (exceptfds && FD_ISSET(hostSockets[i], &exfds)) { NetInetFD_SET(i, exceptfds); } } diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index f37cdbb9fa..e80be8586c 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -489,7 +489,7 @@ static void DrawNp(ImConfig &cfg) { SceNpId id{}; NpGetNpId(&id); - ImGui::Text("User Handle: %s", id.handle); + ImGui::Text("User Handle: %s", id.handle.data); ImGui::End(); } From 440fa80c5fe3ade3da120918013daf6a01cc7cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 8 Jan 2025 18:47:16 +0100 Subject: [PATCH 19/19] Use the correct count parameter for select --- Core/HLE/SocketManager.h | 4 ++-- Core/HLE/sceNetInet.cpp | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Core/HLE/SocketManager.h b/Core/HLE/SocketManager.h index ecc9055112..13f8e5630d 100644 --- a/Core/HLE/SocketManager.h +++ b/Core/HLE/SocketManager.h @@ -4,7 +4,7 @@ // Keep track of who's using a socket. enum class SocketState { - Unused, + Unused = 0, UsedNetInet, UsedProAdhoc, }; @@ -28,7 +28,7 @@ class SocketManager { public: enum { VALID_INET_SOCKET_COUNT = 256, - MIN_VALID_INET_SOCKET = 61, + MIN_VALID_INET_SOCKET = 1, }; InetSocket *CreateSocket(int *index, int *returned_errno, SocketState state, int domain, int type, int protocol); diff --git a/Core/HLE/sceNetInet.cpp b/Core/HLE/sceNetInet.cpp index 7d7805d027..ee37325b1b 100644 --- a/Core/HLE/sceNetInet.cpp +++ b/Core/HLE/sceNetInet.cpp @@ -204,6 +204,8 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr int rdcnt = 0, wrcnt = 0, excnt = 0; + int maxHostSocket = 0; + // Save the mapping during setup. SOCKET hostSockets[256]{}; @@ -212,6 +214,8 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); _dbg_assert_(sock != 0); hostSockets[i] = sock; + if (sock > maxHostSocket) + maxHostSocket = sock; DEBUG_LOG(Log::sceNet, "Input Read FD #%i (host: %d)", i, sock); if (rdcnt < FD_SETSIZE) { FD_SET(sock, &rdfds); // This might pointed to a non-existing socket or sockets belonged to other programs on Windows, because most of the time Windows socket have an id above 1k instead of 0-255 @@ -224,6 +228,8 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); _dbg_assert_(sock != 0); hostSockets[i] = sock; + if (sock > maxHostSocket) + maxHostSocket = sock; DEBUG_LOG(Log::sceNet, "Input Write FD #%i (host: %d)", i, sock); if (wrcnt < FD_SETSIZE) { FD_SET(sock, &wrfds); @@ -236,6 +242,8 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr SOCKET sock = g_socketManager.GetHostSocketFromInetSocket(i); _dbg_assert_(sock != 0); hostSockets[i] = sock; + if (sock > maxHostSocket) + maxHostSocket = sock; DEBUG_LOG(Log::sceNet, "Input Except FD #%i (host: %d)", i, sock); if (excnt < FD_SETSIZE) { FD_SET(sock, &exfds); @@ -256,10 +264,10 @@ int sceNetInetSelect(int nfds, u32 readfdsPtr, u32 writefdsPtr, u32 exceptfdsPtr tmout.tv_sec = timeout->tv_sec; tmout.tv_usec = timeout->tv_usec; } - DEBUG_LOG(Log::sceNet, "Select: Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", rdcnt, wrcnt, excnt, (int)tmout.tv_sec, (int)tmout.tv_usec); + DEBUG_LOG(Log::sceNet, "Select(host: %d): Read count: %d, Write count: %d, Except count: %d, TimeVal: %u.%u", maxHostSocket + 1, rdcnt, wrcnt, excnt, (int)tmout.tv_sec, (int)tmout.tv_usec); // TODO: Simulate blocking behaviour when timeout = NULL to prevent PPSSPP from freezing // Note: select can overwrite tmout. - int retval = select(nfds, readfds ? &rdfds : nullptr, writefds ? &wrfds : nullptr, exceptfds ? &exfds : nullptr, /*(timeout == NULL) ? NULL :*/ &tmout); + int retval = select(maxHostSocket + 1, readfds ? &rdfds : nullptr, writefds ? &wrfds : nullptr, exceptfds ? &exfds : nullptr, /*(timeout == NULL) ? NULL :*/ &tmout); if (retval < 0) { ERROR_LOG(Log::sceNet, "select returned an error, TODO"); }