diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp index 9ffd03cc9a..37e4e8550d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp @@ -22,6 +22,8 @@ lv2_socket_native::lv2_socket_native(lv2_socket_family family, lv2_socket_type t lv2_socket_native::lv2_socket_native(utils::serial& ar, lv2_socket_type type) : lv2_socket(stx::make_exact(ar), type) { + [[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(lv2_net); + #ifdef _WIN32 ar(so_reuseaddr, so_reuseport); #else @@ -33,16 +35,26 @@ lv2_socket_native::lv2_socket_native(utils::serial& ar, lv2_socket_type type) sys_net.error("[Native] Savestate tried to load Win32 specific data, compatibility may be affected"); } #endif + + if (version >= 2) + { + // Flag to signal failure of TCP connection on socket start + ar(feign_tcp_conn_failure); + } } void lv2_socket_native::save(utils::serial& ar) { + USING_SERIALIZATION_VERSION(lv2_net); + lv2_socket::save(ar, true); #ifdef _WIN32 ar(so_reuseaddr, so_reuseport); #else ar(std::array{}); #endif + + ar(is_socket_connected()); } lv2_socket_native::~lv2_socket_native() @@ -106,6 +118,11 @@ std::tuple, sys_net_sockaddr> lv2_socket_ ::sockaddr_storage native_addr; ::socklen_t native_addrlen = sizeof(native_addr); + if (feign_tcp_conn_failure) + { + sys_net.error("Calling socket::accpet() from a previously connected socket!"); + } + socket_type native_socket = ::accept(socket, reinterpret_cast(&native_addr), &native_addrlen); if (native_socket != invalid_socket) @@ -143,6 +160,11 @@ s32 lv2_socket_native::bind(const sys_net_sockaddr& addr) saddr = std::bit_cast(psa_in->sin_addr); } + if (feign_tcp_conn_failure) + { + sys_net.error("Calling socket::bind() from a previously connected socket!"); + } + ::sockaddr_in native_addr{}; native_addr.sin_family = AF_INET; native_addr.sin_port = std::bit_cast(psa_in->sin_port); @@ -225,6 +247,12 @@ std::optional lv2_socket_native::connect(const sys_net_sockaddr& addr) bool was_connecting = connecting; #endif + if (feign_tcp_conn_failure) + { + // As if still connected + return -SYS_NET_EALREADY; + } + if (::connect(socket, reinterpret_cast(&native_addr), native_addr_len) == 0) { return CELL_OK; @@ -873,6 +901,13 @@ std::optional, sys_net_sockaddr>> lv2_socket_nat lock.lock(); } + if (feign_tcp_conn_failure) + { + // As if just lost the connection + feign_tcp_conn_failure = false; + return {{-SYS_NET_ECONNRESET, {},{}}}; + } + int native_flags = 0; ::sockaddr_storage native_addr{}; ::socklen_t native_addrlen = sizeof(native_addr); @@ -963,6 +998,12 @@ std::optional lv2_socket_native::sendto(s32 flags, const std::vector& b return -SYS_NET_EADDRNOTAVAIL; } } + else if (feign_tcp_conn_failure) + { + // As if just lost the connection + feign_tcp_conn_failure = false; + return -SYS_NET_ECONNRESET; + } sys_net_error result{}; @@ -1029,6 +1070,12 @@ std::optional lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& m native_flags |= MSG_WAITALL; } + if (feign_tcp_conn_failure) + { + // As if just lost the connection + feign_tcp_conn_failure = false; + return {-SYS_NET_ECONNRESET}; + } for (int i = 0; i < msg.msg_iovlen; i++) { @@ -1082,6 +1129,12 @@ s32 lv2_socket_native::shutdown(s32 how) { std::lock_guard lock(mutex); + if (feign_tcp_conn_failure) + { + // As if still connected + return CELL_OK; + } + #ifdef _WIN32 const int native_how = how == SYS_NET_SHUT_RD ? SD_RECEIVE : @@ -1172,3 +1225,47 @@ void lv2_socket_native::set_non_blocking() ::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK); #endif } + +bool lv2_socket_native::is_socket_connected() +{ + if (type != SYS_NET_SOCK_STREAM) + { + return false; + } + + std::lock_guard lock(mutex); + + int listening = 0; + socklen_t len = sizeof(listening); + + if (::getsockopt(socket, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast(&listening), &len) == -1) + { + return false; + } + + if (listening) + { + // Would be handled in other ways + return false; + } + + fd_set readfds, writefds; + struct timeval timeout{0, 0}; // Zero timeout + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_SET(socket, &readfds); + FD_SET(socket, &writefds); + + // Use select to check for readability and writability + const int result = ::select(1, &readfds, &writefds, NULL, &timeout); + + if (result < 0) + { + // Error occurred + return false; + } + + // Socket is connected if it's readable or writable + return FD_ISSET(socket, &readfds) || FD_ISSET(socket, &writefds); +} diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h index f6b221b97d..0ecfdf7278 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h @@ -53,6 +53,8 @@ public: s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override; std::tuple select(bs_t selected, pollfd& native_pfd) override; + bool is_socket_connected(); + s32 listen(s32 backlog) override; void close() override; s32 shutdown(s32 how) override; @@ -69,4 +71,5 @@ private: s32 so_reuseport = 0; #endif u16 bound_port = 0; + bool feign_tcp_conn_failure = false; // Savestate load related }; diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index f9a6a393fc..122e7b16c5 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -46,7 +46,7 @@ SERIALIZATION_VER(ppu, 1, 1, 2/*PPU sleep SERIALIZATION_VER(spu, 2, 1) SERIALIZATION_VER(lv2_sync, 3, 1) SERIALIZATION_VER(lv2_vm, 4, 1) -SERIALIZATION_VER(lv2_net, 5, 1) +SERIALIZATION_VER(lv2_net, 5, 1, 2/*TCP Feign conection loss*/) SERIALIZATION_VER(lv2_fs, 6, 1, 2/*NPDRM key saving*/) SERIALIZATION_VER(lv2_prx_overlay, 7, 1) SERIALIZATION_VER(lv2_memory, 8, 1)