SDL_net: PS Vita support

From 4dd9d8418e9781d10481fd6177e3a1064ca201fe Mon Sep 17 00:00:00 2001
From: Ivan Epifanov <[EMAIL REDACTED]>
Date: Tue, 16 Jun 2026 20:02:11 +0300
Subject: [PATCH] PS Vita support

---
 src/SDL_net.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 169 insertions(+)

diff --git a/src/SDL_net.c b/src/SDL_net.c
index a4f90fa..799cda8 100644
--- a/src/SDL_net.c
+++ b/src/SDL_net.c
@@ -171,7 +171,9 @@ static int WindowsPoll(struct pollfd *fds, unsigned int nfds, int timeout)
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
+#ifndef SDL_PLATFORM_VITA
 #include <net/if.h>
+#endif
 #include <netdb.h>
 #include <errno.h>
 #include <string.h>
@@ -190,6 +192,10 @@ static int WindowsPoll(struct pollfd *fds, unsigned int nfds, int timeout)
 #include <ifaddrs.h>
 #endif
 
+#ifdef SDL_PLATFORM_VITA
+#include <psp2/net/netctl.h>
+#endif
+
 #define INVALID_SOCKET -1
 #define SOCKET_ERROR -1
 typedef int Socket;
@@ -795,6 +801,161 @@ static void RefreshInterfaces(void)  // LINUX/BSD VERSION
 #endif
 }
 
+#elif defined(SDL_PLATFORM_VITA)
+
+static int interface_change_notifications_handle;
+static SDL_Thread *interface_change_notifications_thread = NULL;
+static SDL_AtomicInt interface_change_notifications_flag;
+
+static int SDLCALL VitaInterfaceChangeNotificationThread(void *data)
+{
+    (void)data;
+
+    while(SDL_GetAtomicInt(&interface_change_notifications_flag) == 0)
+    {
+        sceNetCtlCheckCallback();
+        SDL_Delay(100);
+    }
+    return 0;
+}
+
+static void NetworkInterfaceChangedCallback(int event_type, void* arg)
+{
+    SDL_SetAtomicInt(&interfaces_have_changed, 1);
+}
+
+static bool InitInterfaceChangeNotifications(void)
+{
+    if (sceNetCtlInetRegisterCallback(NetworkInterfaceChangedCallback, NULL, &interface_change_notifications_handle) != 0)
+        return false;
+
+    interface_change_notifications_thread = SDL_CreateThread(VitaInterfaceChangeNotificationThread, "SDLNetIfaceEnum", NULL);
+    return (interface_change_notifications_thread != NULL);
+}
+
+static void QuitInterfaceChangeNotifications(void)
+{
+    sceNetCtlInetUnregisterCallback(interface_change_notifications_handle);
+    if (interface_change_notifications_thread) {
+        SDL_SetAtomicInt(&interface_change_notifications_flag, 1);
+        SDL_WaitThread(interface_change_notifications_thread, NULL);
+        SDL_SetAtomicInt(&interface_change_notifications_flag, 0);
+    }
+    interface_change_notifications_thread = NULL;
+}
+
+static NET_Address *CreateSDLNetAddrFromIp(char* hostbuf)
+{
+    NET_Address *addr = (NET_Address *) SDL_calloc(1, sizeof (NET_Address));
+    if (!addr) {
+        return NULL;
+    }
+    SDL_SetAtomicInt(&addr->status, (int) NET_SUCCESS);
+
+    struct addrinfo hints;
+    SDL_zero(hints);
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_protocol = 0;
+    hints.ai_flags = AI_NUMERICHOST;
+
+    int gairc = getaddrinfo(hostbuf, NULL, &hints, &addr->ainfo);
+    if (gairc != 0) {
+        SDL_free(addr);
+        SetGetAddrInfoError("Failed to determine address", gairc);
+        return NULL;
+    }
+
+    addr->human_readable = SDL_strdup(hostbuf);
+    if (!addr->human_readable) {
+        freeaddrinfo(addr->ainfo);
+        SDL_free(addr);
+        return NULL;
+    }
+
+    return NET_RefAddress(addr);
+}
+
+
+static void RefreshInterfaces(void)
+{
+    NetworkInterface *new_interfaces = NULL;
+    int new_num_interfaces = 0;
+    SceNetCtlInfo info;
+
+    int state;
+    if (sceNetCtlInetGetState(&state) < 0 ) {
+        goto failed;
+    }
+
+    if (state != SCE_NETCTL_STATE_CONNECTED) {
+        goto succeeded;
+    }
+
+    if (state == SCE_NETCTL_STATE_CONNECTED) {
+        new_num_interfaces = 1;
+
+        new_interfaces = SDL_calloc(new_num_interfaces, sizeof (*new_interfaces));
+        if (!new_interfaces) {
+            goto failed;
+        }
+
+        NetworkInterface *iface = &new_interfaces[0];
+
+        if (sceNetCtlInetGetInfo(SCE_NETCTL_INFO_GET_IP_ADDRESS, &info) < 0) {
+            goto failed;
+        }
+
+
+        iface->address = CreateSDLNetAddrFromIp(info.ip_address);
+
+        if (iface->address) {
+            iface->index = 0;
+
+            if (sceNetCtlInetGetInfo(SCE_NETCTL_INFO_GET_DEVICE, &info) < 0) {
+                goto failed;
+            }
+
+            switch (info.device) {
+                case 0:
+                    iface->name = SDL_strdup("wlan0");
+                    break;
+                case 1:
+                    iface->name = SDL_strdup("eth0");
+                    break;
+                case 2:
+                    iface->name = SDL_strdup("phone0");
+                    break;
+            }
+
+            if (!iface->name) {
+                goto failed;
+            }
+
+            char *ptr = SDL_strchr(iface->address->human_readable, '%');  // chop off interface name.
+            if (ptr) {
+                *ptr = '\0';
+            }
+        } else {
+            NET_UnrefAddress(iface->broadcast);  // just in case.
+            goto failed;
+        }
+    }
+
+succeeded:
+    SDL_LockRWLockForWriting(interface_rwlock);
+    NetworkInterface *old_interfaces = interfaces;
+    const int old_num_interfaces = num_interfaces;
+    interfaces = new_interfaces;
+    num_interfaces = new_num_interfaces;
+    SDL_UnlockRWLock(interface_rwlock);
+    FreeNetworkInterfaces(old_interfaces, old_num_interfaces);
+    return;
+
+failed:
+    FreeNetworkInterfaces(new_interfaces, new_num_interfaces);
+}
+
 #else
 #warning implement me for your platform.
 static bool InitInterfaceChangeNotifications(void)
@@ -1024,6 +1185,9 @@ bool NET_Init(void)
     if (WSAStartup(MAKEWORD(1, 1), &data) != 0) {
         return SetSocketErrorBool("WSAStartup() failed", LastSocketError());
     }
+    #elif defined(SDL_PLATFORM_VITA)
+    // trigger vita network stack startup
+    gethostname(NULL,0);
     #else
     signal(SIGPIPE, SIG_IGN);
     #endif
@@ -2097,6 +2261,10 @@ NET_DatagramSocket *NET_CreateDatagramSocket(NET_Address *addr, Uint16 port, SDL
                 SDL_SetError("Failed to determine broadcast address for this interface");
                 goto failed;
             } else if (ainfo->ai_family == AF_INET6) { // fake broadcast support via multicasting to all-nodes link-local group, ff02::1.
+#ifdef SDL_PLATFORM_VITA
+                SDL_SetError("No IPv6 support");
+                goto failed;
+#else
                 SDL_assert(socket_handle->broadcast == ipv6_broadcast_addr);
                 const struct addrinfo *bai = socket_handle->broadcast->ainfo;
                 SDL_assert(bai != NULL);
@@ -2113,6 +2281,7 @@ NET_DatagramSocket *NET_CreateDatagramSocket(NET_Address *addr, Uint16 port, SDL
                 }
                 const int ifidx = (int) interface_index;
                 setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char *) &ifidx, (SockLen) sizeof (ifidx));  // multicast sends go through the same interface.
+#endif
             }
         }
     }