diff --git a/src/xenia/kernel/xam/xam_net.cc b/src/xenia/kernel/xam/xam_net.cc index b7333075b..315596046 100644 --- a/src/xenia/kernel/xam/xam_net.cc +++ b/src/xenia/kernel/xam/xam_net.cc @@ -248,38 +248,42 @@ DECLARE_XAM_EXPORT1(NetDll_XNetRandom, kNetworking, kStub); dword_result_t NetDll_WSAStartup_entry(dword_t caller, word_t version, pointer_t data_ptr) { -// TODO(benvanik): abstraction layer needed. + // TODO(benvanik): abstraction layer needed. + int ret = 0; + #ifdef XE_PLATFORM_WIN32 WSADATA wsaData; ZeroMemory(&wsaData, sizeof(WSADATA)); - int ret = WSAStartup(version, &wsaData); - - auto data_out = kernel_state()->memory()->TranslateVirtual(data_ptr); + ret = WSAStartup(version, &wsaData); +#endif if (data_ptr) { + auto data_out = kernel_state()->memory()->TranslateVirtual(data_ptr); + +#ifdef XE_PLATFORM_WIN32 data_ptr->version = wsaData.wVersion; data_ptr->version_high = wsaData.wHighVersion; - std::memcpy(&data_ptr->description, wsaData.szDescription, 0x100); - std::memcpy(&data_ptr->system_status, wsaData.szSystemStatus, 0x80); data_ptr->max_sockets = wsaData.iMaxSockets; data_ptr->max_udpdg = wsaData.iMaxUdpDg; + std::memcpy(&data_ptr->description, wsaData.szDescription, 0x100); + std::memcpy(&data_ptr->system_status, wsaData.szSystemStatus, 0x80); +#else + // Match Windows behavior with reasonable values + data_ptr->version = version.value(); + data_ptr->version_high = version.value(); + data_ptr->max_sockets = 100; + data_ptr->max_udpdg = 1024; + // WinSock 2.2 typically returns empty strings for these fields + std::memset(&data_ptr->description, 0, 0x100); + std::memset(&data_ptr->system_status, 0, 0x80); +#endif // Some games (5841099F) want this value round-tripped - they'll compare if // it changes and bugcheck if it does. - uint32_t vendor_ptr = xe::load_and_swap(data_out + 0x190); - xe::store_and_swap(data_out + 0x190, vendor_ptr); + // vendor_info_ptr is at offset 0x18A (after max_udpdg at 0x188) + uint32_t vendor_ptr = xe::load_and_swap(data_out + 0x18A); + xe::store_and_swap(data_out + 0x18A, vendor_ptr); } -#else - int ret = 0; - if (data_ptr) { - // Guess these values! - data_ptr->version = version.value(); - data_ptr->description[0] = '\0'; - data_ptr->system_status[0] = '\0'; - data_ptr->max_sockets = 100; - data_ptr->max_udpdg = 1024; - } -#endif // DEBUG /* @@ -659,6 +663,8 @@ dword_result_t NetDll_socket_entry(dword_t caller, dword_t af, dword_t type, socket->Release(); XThread::SetLastError(socket->GetLastWSAError()); + XELOGE("NetDll_socket: failed with error {:08X}", + socket->GetLastWSAError()); return -1; } @@ -742,6 +748,8 @@ dword_result_t NetDll_ioctlsocket_entry(dword_t caller, dword_t socket_handle, X_STATUS status = socket->IOControl(cmd, arg_ptr); if (XFAILED(status)) { XThread::SetLastError(socket->GetLastWSAError()); + XELOGE("NetDll_ioctlsocket: failed with error {:08X}", + socket->GetLastWSAError()); return -1; } @@ -757,6 +765,7 @@ dword_result_t NetDll_bind_entry(dword_t caller, dword_t socket_handle, kernel_state()->object_table()->LookupObject(socket_handle); if (!socket) { XThread::SetLastError(uint32_t(X_WSAError::X_WSAENOTSOCK)); + XELOGE("NetDll_bind: failed - invalid socket"); return -1; } @@ -764,6 +773,7 @@ dword_result_t NetDll_bind_entry(dword_t caller, dword_t socket_handle, X_STATUS status = socket->Bind(&native_name, namelen); if (XFAILED(status)) { XThread::SetLastError(socket->GetLastWSAError()); + XELOGE("NetDll_bind: failed with status {:08X}", status); return -1; } @@ -1027,7 +1037,8 @@ dword_result_t NetDll_sendto_entry(dword_t caller, dword_t socket_handle, } N_XSOCKADDR_IN native_to(to_ptr); - return socket->SendTo(buf_ptr, buf_len, flags, &native_to, to_len); + int ret = socket->SendTo(buf_ptr, buf_len, flags, &native_to, to_len); + return ret; } DECLARE_XAM_EXPORT1(NetDll_sendto, kNetworking, kImplemented); diff --git a/src/xenia/kernel/xsocket.cc b/src/xenia/kernel/xsocket.cc index 0082f34fb..3f9bbaada 100644 --- a/src/xenia/kernel/xsocket.cc +++ b/src/xenia/kernel/xsocket.cc @@ -11,6 +11,7 @@ #include +#include "xenia/base/logging.h" #include "xenia/base/platform.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/xam/xam_module.h" @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #endif @@ -91,10 +93,73 @@ X_STATUS XSocket::SetOption(uint32_t level, uint32_t optname, void* optval_ptr, return X_STATUS_SUCCESS; } - int ret = - setsockopt(native_handle_, level, optname, (char*)optval_ptr, optlen); + // Translate Xbox socket level to native + int native_level = level; + if (level == 0xFFFF) { // Xbox SOL_SOCKET + native_level = SOL_SOCKET; + } + + // Translate Xbox socket options to native + // Xbox uses Winsock constants which mostly match standard values + int native_optname = optname; + if (level == 0xFFFF) { + switch (optname) { + case 0x0001: // SO_DEBUG + native_optname = SO_DEBUG; + break; + case 0x0002: // SO_ACCEPTCONN + native_optname = SO_ACCEPTCONN; + break; + case 0x0004: // SO_REUSEADDR + native_optname = SO_REUSEADDR; + break; + case 0x0008: // SO_KEEPALIVE + native_optname = SO_KEEPALIVE; + break; + case 0x0010: // SO_DONTROUTE + native_optname = SO_DONTROUTE; + break; + case 0x0020: // SO_BROADCAST + native_optname = SO_BROADCAST; + break; + case 0x0080: // SO_LINGER + native_optname = SO_LINGER; + break; + case 0x0100: // SO_OOBINLINE + native_optname = SO_OOBINLINE; + break; + case 0x1001: // SO_SNDBUF + native_optname = SO_SNDBUF; + break; + case 0x1002: // SO_RCVBUF + native_optname = SO_RCVBUF; + break; + case 0x1003: // SO_SNDLOWAT + native_optname = SO_SNDLOWAT; + break; + case 0x1004: // SO_RCVLOWAT + native_optname = SO_RCVLOWAT; + break; + case 0x1005: // SO_SNDTIMEO + native_optname = SO_SNDTIMEO; + break; + case 0x1006: // SO_RCVTIMEO + native_optname = SO_RCVTIMEO; + break; + case 0x1007: // SO_ERROR + native_optname = SO_ERROR; + break; + case 0x1008: // SO_TYPE + native_optname = SO_TYPE; + break; + // Add more translations as needed + } + } + + int ret = setsockopt(native_handle_, native_level, native_optname, + static_cast(optval_ptr), optlen); if (ret < 0) { - // TODO: WSAGetLastError() + XELOGE("XSocket::SetOption: setsockopt failed, errno={}", errno); return X_STATUS_UNSUCCESSFUL; } @@ -113,10 +178,29 @@ X_STATUS XSocket::IOControl(uint32_t cmd, uint8_t* arg_ptr) { // TODO: Get last error return X_STATUS_UNSUCCESSFUL; } - return X_STATUS_SUCCESS; #elif XE_PLATFORM_LINUX - return X_STATUS_UNSUCCESSFUL; + // Translate Xbox/Windows ioctl commands to Linux equivalents + int native_cmd = cmd; + switch (cmd) { + case 0x8004667E: // Windows FIONBIO - set non-blocking mode + native_cmd = FIONBIO; + break; + case 0x4004667F: // Windows FIONREAD - get bytes available + native_cmd = FIONREAD; + break; + default: + XELOGW("XSocket::IOControl: unknown cmd={:08X}, passing through", cmd); + break; + } + + int ret = ioctl(native_handle_, native_cmd, arg_ptr); + if (ret < 0) { + XELOGE("XSocket::IOControl: ioctl failed, cmd={:08X} -> {:08X}, errno={}", + cmd, native_cmd, errno); + return X_STATUS_UNSUCCESSFUL; + } + return X_STATUS_SUCCESS; #endif } @@ -130,8 +214,26 @@ X_STATUS XSocket::Connect(N_XSOCKADDR* name, int name_len) { } X_STATUS XSocket::Bind(N_XSOCKADDR_IN* name, int name_len) { + // On Linux and Windows (when running under Wine), ports < 1024 require root + // privileges. Remap to port + 10000 to avoid privilege issues. + // Note: sin_port is xe::be which automatically handles endianness, + // so we use it directly without ntohs/htons. + const uint16_t original_port = uint16_t(name->sin_port); + if (original_port < 1024) { + uint16_t new_port = original_port + 10000; + name->sin_port = new_port; + XELOGW("XSocket::Bind: port {} requires privileges, remapping to port {}", + original_port, new_port); + } + int ret = bind(native_handle_, (sockaddr*)name, name_len); + if (ret < 0) { +#ifdef XE_PLATFORM_WIN32 + XELOGE("XSocket::Bind: bind() failed with WSA error {}", WSAGetLastError()); +#else + XELOGE("XSocket::Bind: bind() failed with errno={}", errno); +#endif return X_STATUS_UNSUCCESSFUL; }