diff --git a/src/ControlStream.c b/src/ControlStream.c index a92412f..119a6c1 100644 --- a/src/ControlStream.c +++ b/src/ControlStream.c @@ -362,7 +362,7 @@ void destroyControlStream(void) { static void queueFrameInvalidationTuple(uint32_t startFrame, uint32_t endFrame) { LC_ASSERT(startFrame <= endFrame); - + if (isReferenceFrameInvalidationEnabled()) { PQUEUED_FRAME_INVALIDATION_TUPLE qfit; qfit = malloc(sizeof(*qfit)); @@ -411,7 +411,7 @@ void connectionSendFrameFecStatus(PSS_FRAME_FEC_STATUS fecStatus) { if (!IS_SUNSHINE()) { return; } - + // Queue a frame FEC status message. This is best-effort only. PQUEUED_FRAME_FEC_STATUS queuedFecStatus = malloc(sizeof(*queuedFecStatus)); if (queuedFecStatus != NULL) { @@ -1334,7 +1334,7 @@ static void lossStatsThreadFunc(void* context) { free(queuedFrameStatus); return; } - + free(queuedFrameStatus); } } @@ -1362,7 +1362,7 @@ static void lossStatsThreadFunc(void* context) { } else { char* lossStatsPayload; - + // Sunshine should use the newer codepath above LC_ASSERT(!IS_SUNSHINE()); @@ -1538,7 +1538,7 @@ int stopControlStream(void) { if (ctlSock != INVALID_SOCKET) { shutdownTcpSocket(ctlSock); } - + PltInterruptThread(&lossStatsThread); PltInterruptThread(&requestIdrFrameThread); PltInterruptThread(&controlReceiveThread); @@ -1571,7 +1571,7 @@ int stopControlStream(void) { enet_host_destroy(client); client = NULL; } - + if (ctlSock != INVALID_SOCKET) { closeSocket(ctlSock); ctlSock = INVALID_SOCKET; @@ -1642,11 +1642,16 @@ int startControlStream(void) { if (AppVersionQuad[0] >= 5) { ENetAddress remoteAddress, localAddress; ENetEvent event; - + LC_ASSERT(ControlPortNumber != 0); enet_address_set_address(&localAddress, (struct sockaddr *)&LocalAddr, AddrLen); +#ifdef __3DS__ + // binding to wildcard port is broken on the 3DS, so we need to define a port manually + enet_address_set_port(&localAddress, htons(n3ds_udp_port++)); +#else enet_address_set_port(&localAddress, 0); // Wildcard port +#endif enet_address_set_address(&remoteAddress, (struct sockaddr *)&RemoteAddr, AddrLen); enet_address_set_port(&remoteAddress, ControlPortNumber); @@ -1711,9 +1716,15 @@ int startControlStream(void) { // Ensure the connect verify ACK is sent immediately enet_host_flush(client); - + +#ifdef __3DS__ + // Set the peer timeout to 1 minute and limit backoff to 2x RTT + // The 3DS can take a bit longer to set up when starting fresh + enet_peer_timeout(peer, 2, 60000, 60000); +#else // Set the peer timeout to 10 seconds and limit backoff to 2x RTT enet_peer_timeout(peer, 2, 10000, 10000); +#endif } else { // NB: Do NOT use ControlPortNumber here. 47995 is correct for these old versions. diff --git a/src/Platform.c b/src/Platform.c index 7751a46..d3ec2dc 100644 --- a/src/Platform.c +++ b/src/Platform.c @@ -93,7 +93,7 @@ void* ThreadProc(void* context) { free(ctx); #endif -#if defined(LC_WINDOWS) || defined(__vita__) || defined(__WIIU__) +#if defined(LC_WINDOWS) || defined(__vita__) || defined(__WIIU__) || defined(__3DS__) return 0; #else return NULL; @@ -105,6 +105,9 @@ void PltSleepMs(int ms) { SleepEx(ms, FALSE); #elif defined(__vita__) sceKernelDelayThread(ms * 1000); +#elif defined(__3DS__) + s64 nsecs = ms * 1000000; + svcSleepThread(nsecs); #else useconds_t usecs = ms * 1000; usleep(usecs); @@ -129,6 +132,8 @@ int PltCreateMutex(PLT_MUTEX* mutex) { } #elif defined(__WIIU__) OSFastMutex_Init(mutex, ""); +#elif defined(__3DS__) + LightLock_Init(mutex); #else int err = pthread_mutex_init(mutex, NULL); if (err != 0) { @@ -145,7 +150,7 @@ void PltDeleteMutex(PLT_MUTEX* mutex) { // No-op to destroy a SRWLOCK #elif defined(__vita__) sceKernelDeleteMutex(*mutex); -#elif defined(__WIIU__) +#elif defined(__WIIU__) || defined(__3DS__) #else pthread_mutex_destroy(mutex); @@ -159,6 +164,8 @@ void PltLockMutex(PLT_MUTEX* mutex) { sceKernelLockMutex(*mutex, 1, NULL); #elif defined(__WIIU__) OSFastMutex_Lock(mutex); +#elif defined(__3DS__) + LightLock_Lock(mutex); #else pthread_mutex_lock(mutex); #endif @@ -171,6 +178,8 @@ void PltUnlockMutex(PLT_MUTEX* mutex) { sceKernelUnlockMutex(*mutex, 1); #elif defined(__WIIU__) OSFastMutex_Unlock(mutex); +#elif defined(__3DS__) + LightLock_Unlock(mutex); #else pthread_mutex_unlock(mutex); #endif @@ -187,6 +196,9 @@ void PltJoinThread(PLT_THREAD* thread) { free(thread->context); #elif defined(__WIIU__) OSJoinThread(&thread->thread, NULL); +#elif defined(__3DS__) + threadJoin(thread->thread, U64_MAX); + threadFree(thread->thread); #else pthread_join(thread->thread, NULL); #endif @@ -226,7 +238,7 @@ int PltCreateThread(const char* name, ThreadEntry entry, void* context, PLT_THRE ctx->entry = entry; ctx->context = context; ctx->name = name; - + thread->cancelled = false; #if defined(LC_WINDOWS) @@ -265,6 +277,22 @@ int PltCreateThread(const char* name, ThreadEntry entry, void* context, PLT_THRE OSSetThreadDeallocator(&thread->thread, thread_deallocator); OSResumeThread(&thread->thread); +#elif defined(__3DS__) + { + s32 priority = 0x30; + size_t stack_size = 1024 * 1024; + svcGetThreadPriority(&priority, CUR_THREAD_HANDLE); + thread->thread = threadCreate(ThreadProc, + ctx, + stack_size, + priority, + -1, + false); + if (thread->thread == NULL) { + free(ctx); + return -1; + } + } #else { int err = pthread_create(&thread->thread, NULL, ThreadProc, ctx); @@ -351,6 +379,8 @@ int PltCreateConditionVariable(PLT_COND* cond, PLT_MUTEX* mutex) { } #elif defined(__WIIU__) OSFastCond_Init(cond, ""); +#elif defined(__3DS__) + CondVar_Init(cond); #else pthread_cond_init(cond, NULL); #endif @@ -364,6 +394,8 @@ void PltDeleteConditionVariable(PLT_COND* cond) { sceKernelDeleteCond(*cond); #elif defined(__WIIU__) // No-op to delete an OSFastCondition +#elif defined(__3DS__) + // No-op to delete CondVar #else pthread_cond_destroy(cond); #endif @@ -376,6 +408,8 @@ void PltSignalConditionVariable(PLT_COND* cond) { sceKernelSignalCond(*cond); #elif defined(__WIIU__) OSFastCond_Signal(cond); +#elif defined(__3DS__) + CondVar_Signal(cond); #else pthread_cond_signal(cond); #endif @@ -388,6 +422,8 @@ void PltWaitForConditionVariable(PLT_COND* cond, PLT_MUTEX* mutex) { sceKernelWaitCond(*cond, NULL); #elif defined(__WIIU__) OSFastCond_Wait(cond, mutex); +#elif defined(__3DS__) + CondVar_Wait(cond, mutex); #else pthread_cond_wait(cond, mutex); #endif @@ -452,7 +488,7 @@ int initializePlatform(void) { if (err != 0) { return err; } - + err = enet_initialize(); if (err != 0) { return err; @@ -460,14 +496,14 @@ int initializePlatform(void) { enterLowLatencyMode(); - return 0; + return 0; } void cleanupPlatform(void) { exitLowLatencyMode(); cleanupPlatformSockets(); - + enet_deinitialize(); LC_ASSERT(activeThreads == 0); diff --git a/src/Platform.h b/src/Platform.h index a169a7c..20d03f5 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -32,6 +32,9 @@ #include #include #include +#elif defined(__3DS__) +#include <3ds.h> +#include #else #include #include diff --git a/src/PlatformSockets.c b/src/PlatformSockets.c index c87e91b..4ef27a6 100644 --- a/src/PlatformSockets.c +++ b/src/PlatformSockets.c @@ -35,6 +35,10 @@ DWORD (WINAPI *pfnWlanSetInterface)(HANDLE hClientHandle, CONST GUID *pInterface #endif +#ifdef __3DS__ +in_port_t n3ds_udp_port = 47998; +#endif + void addrToUrlSafeString(struct sockaddr_storage* addr, char* string, size_t stringLength) { char addrstr[URLSAFESTRING_LEN]; @@ -74,8 +78,8 @@ int setNonFatalRecvTimeoutMs(SOCKET s, int timeoutMs) { // losing some data in a very rare case is fine, especially because we get to // halve the number of syscalls per packet by avoiding select(). return setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeoutMs, sizeof(timeoutMs)); -#elif defined(__WIIU__) - // timeouts aren't supported on Wii U +#elif defined(__WIIU__) || defined(__3DS__) + // timeouts aren't supported on Wii U or 3DS return -1; #else struct timeval val; @@ -142,6 +146,15 @@ int pollSockets(struct pollfd* pollFds, int pollFdsCount, int timeoutMs) { } } + return err; +#elif defined(__3DS__) + int err; + for (int i = 0; i < timeoutMs; i++) { + err = poll(pollFds, pollFdsCount, 1); // need to do this on 3ds since poll will block even if socket is ready before + if (err) { + break; + } + } return err; #else return poll(pollFds, pollFdsCount, timeoutMs); @@ -164,7 +177,7 @@ bool isSocketReadable(SOCKET s) { int recvUdpSocket(SOCKET s, char* buffer, int size, bool useSelect) { int err; - + do { if (useSelect) { struct pollfd pfd; @@ -251,6 +264,11 @@ SOCKET bindUdpSocket(int addressFamily, struct sockaddr_storage* localAddr, SOCK #endif } +#ifdef __3DS__ + // binding to wildcard port is broken on the 3DS, so we need to define a port manually + struct sockaddr_in *n3ds_addr = &bindAddr; + n3ds_addr->sin_port = htons(n3ds_udp_port++); +#endif if (bind(s, (struct sockaddr*) &bindAddr, addrLen) == SOCKET_ERROR) { err = LastSocketError(); Limelog("bind() failed: %d\n", err); @@ -353,6 +371,9 @@ SOCKET createSocket(int addressFamily, int socketType, int protocol, bool nonBlo setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (char*)&val, sizeof(val)); } #endif +#ifdef __3DS__ + SOCU_AddGlobalSocket(s); +#endif if (nonBlocking) { setSocketNonBlocking(s, true); @@ -426,7 +447,7 @@ SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, goto Exit; } } - + // Wait for the connection to complete or the timeout to elapse pfd.fd = s; pfd.events = POLLOUT; @@ -446,6 +467,17 @@ SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, SetLastSocketError(ETIMEDOUT); return INVALID_SOCKET; } +#ifdef __3DS__ //SO_ERROR is unreliable on 3DS + else { + char test_buffer[1]; + err = (int)recv(s, test_buffer, 1, MSG_PEEK); + if (err < 0 && + (LastSocketError() == EWOULDBLOCK || + LastSocketError() == EAGAIN)) { + err = 0; + } + } +#else else { // The socket was signalled SOCKADDR_LEN len = sizeof(err); @@ -455,10 +487,11 @@ SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, err = (err != 0) ? err : LastSocketFail(); } } +#endif // Disable non-blocking I/O now that the connection is established setSocketNonBlocking(s, false); - + Exit: if (err != 0) { Limelog("connect() failed: %d\n", err); @@ -521,7 +554,7 @@ int resolveHostName(const char* host, int family, int tcpTestPort, struct sockad Limelog("getaddrinfo(%s) returned success without addresses\n", host); return -1; } - + for (currentAddr = res; currentAddr != NULL; currentAddr = currentAddr->ai_next) { // Use the test port to ensure this address is working if: // a) We have multiple addresses @@ -539,10 +572,10 @@ int resolveHostName(const char* host, int family, int tcpTestPort, struct sockad closeSocket(testSocket); } } - + memcpy(addr, currentAddr->ai_addr, currentAddr->ai_addrlen); *addrLen = (SOCKADDR_LEN)currentAddr->ai_addrlen; - + freeaddrinfo(res); return 0; } @@ -555,14 +588,14 @@ int resolveHostName(const char* host, int family, int tcpTestPort, struct sockad #ifdef AF_INET6 bool isInSubnetV6(struct sockaddr_in6* sin6, unsigned char* subnet, int prefixLength) { int i; - + for (i = 0; i < prefixLength; i++) { unsigned char mask = 1 << (i % 8); if ((sin6->sin6_addr.s6_addr[i / 8] & mask) != (subnet[i / 8] & mask)) { return false; } } - + return true; } #endif @@ -575,7 +608,7 @@ bool isPrivateNetworkAddress(struct sockaddr_storage* address) { memcpy(&addr, &((struct sockaddr_in*)address)->sin_addr, sizeof(addr)); addr = htonl(addr); - + // 10.0.0.0/8 if ((addr & 0xFF000000) == 0x0A000000) { return true; @@ -723,7 +756,7 @@ int initializePlatformSockets(void) { #if defined(LC_WINDOWS) WSADATA data; return WSAStartup(MAKEWORD(2, 0), &data); -#elif defined(__vita__) || defined(__WIIU__) +#elif defined(__vita__) || defined(__WIIU__) || defined(__3DS__) return 0; // already initialized #elif defined(LC_POSIX) && !defined(LC_CHROME) // Disable SIGPIPE signals to avoid us getting diff --git a/src/PlatformSockets.h b/src/PlatformSockets.h index 10df63e..290461b 100644 --- a/src/PlatformSockets.h +++ b/src/PlatformSockets.h @@ -2,6 +2,15 @@ #include "Limelight.h" #include "Platform.h" +#ifdef __3DS__ +#include + +#ifdef AF_INET6 +#undef AF_INET6 +#endif + +extern in_port_t n3ds_udp_port; +#endif #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN diff --git a/src/PlatformThreads.h b/src/PlatformThreads.h index 6f37e58..67cdb5e 100644 --- a/src/PlatformThreads.h +++ b/src/PlatformThreads.h @@ -28,6 +28,13 @@ typedef struct _PLT_THREAD { OSThread thread; int cancelled; } PLT_THREAD; +#elif defined(__3DS__) +typedef LightLock PLT_MUTEX; +typedef CondVar PLT_COND; +typedef struct _PLT_THREAD { + Thread thread; + bool cancelled; +} PLT_THREAD; #elif defined (LC_POSIX) typedef pthread_mutex_t PLT_MUTEX; typedef pthread_cond_t PLT_COND;