diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 180850217..b9e6d0c51 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -28,6 +28,7 @@ #ifdef _WIN64 #include +#include #else #include #endif @@ -230,6 +231,124 @@ int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s, LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode); return -1; } + +#ifdef _WIN32 + +int IsSocket(int fd) { + intptr_t handle = _get_osfhandle(fd); + WSANETWORKEVENTS ne; + SOCKET s = (SOCKET)handle; + WSAEnumNetworkEvents(s, NULL, &ne); + return (WSAGetLastError() != WSAENOTSOCK); +} + +#endif + +int PS4_SYSV_ABI posix_select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, + timeval* timeout) { +#ifdef _WIN32 + fd_set sock_r, sock_w, sock_x; + FD_ZERO(&sock_r); + FD_ZERO(&sock_w); + FD_ZERO(&sock_x); + int fd; + + // Separate sockets for select() + for (fd = 0; fd < nfds; ++fd) { + if (IsSocket(fd)) { + SOCKET s = (SOCKET)_get_osfhandle(fd); + if (readfds && FD_ISSET(fd, readfds)) + FD_SET(s, &sock_r); + if (writefds && FD_ISSET(fd, writefds)) + FD_SET(s, &sock_w); + if (exceptfds && FD_ISSET(fd, exceptfds)) + FD_SET(s, &sock_x); + } + } + + int result = select(0, &sock_r, &sock_w, &sock_x, timeout); + int total_ready = result; + + for (fd = 0; fd < nfds; ++fd) { + HANDLE h = (HANDLE)_get_osfhandle(fd); + DWORD type = GetFileType(h); + if (type == FILE_TYPE_UNKNOWN || IsSocket(fd)) + continue; + + // READ check + if (readfds && FD_ISSET(fd, readfds)) { + if (type == FILE_TYPE_PIPE) { + DWORD avail = 0; + if (PeekNamedPipe(h, NULL, 0, NULL, &avail, NULL) && avail > 0) { + total_ready++; + } else { + FD_CLR(fd, readfds); + } + } else if (type == FILE_TYPE_DISK) { + char tmp; + long pos = _lseek(fd, 0, SEEK_CUR); + if (_read(fd, &tmp, 1) > 0) { + _lseek(fd, pos, SEEK_SET); + total_ready++; + } else { + FD_CLR(fd, readfds); + } + } + } + + // WRITE check + if (writefds && FD_ISSET(fd, writefds)) { + DWORD written = 0; + char dummy = 0; + BOOL writable = FALSE; + if (type == FILE_TYPE_PIPE) { + // Try writing 0 bytes to check if it's broken + writable = WriteFile(h, &dummy, 0, &written, NULL); + } else if (type == FILE_TYPE_DISK) { + writable = true; // Disk files are always "writable" + } + if (writable) { + total_ready++; + } else { + FD_CLR(fd, writefds); + } + } + + // EXCEPTION check + if (exceptfds && FD_ISSET(fd, exceptfds)) { + DWORD err = 0, size = sizeof(err); + BOOL error_detected = false; + + if (type == FILE_TYPE_PIPE) { + // Check broken pipe by trying non-blocking zero write + DWORD written; + char tmp = 0; + if (!WriteFile(h, &tmp, 0, &written, NULL)) { + DWORD e = GetLastError(); + if (e == ERROR_NO_DATA || e == ERROR_BROKEN_PIPE) { + error_detected = true; + } + } + } + + if (error_detected) { + total_ready++; + } else { + FD_CLR(fd, exceptfds); + } + } + } + + return total_ready; + +#else + // return select(nfds, readfds, writefds, exceptfds, timeout); + // nfds is shared with pipes , sockets , files in which we use separate maps so it can't work + // like this + LOG_ERROR(Lib_Net, "unimplemented (suck it linux) nfds={}", nfds); + return 0; +#endif +} void RegisterKernel(Core::Loader::SymbolsResolver* sym) { service_thread = std::jthread{KernelServiceThread}; @@ -273,6 +392,7 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ... LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ... + LIB_FUNCTION("T8fER+tIGgk", "libScePosix", 1, "libkernel", 1, 1, posix_select); } } // namespace Libraries::Kernel