mirror of
				https://github.com/Atmosphere-NX/Atmosphere-libs.git
				synced 2025-10-31 03:25:47 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			766 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			766 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) Atmosphère-NX
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms and conditions of the GNU General Public License,
 | |
|  * version 2, as published by the Free Software Foundation.
 | |
|  *
 | |
|  * This program is distributed in the hope it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | |
|  * more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| #include <stratosphere.hpp>
 | |
| #include "client/htcs_session.hpp"
 | |
| #include "client/htcs_virtual_socket_collection.hpp"
 | |
| 
 | |
| namespace ams::htcs {
 | |
| 
 | |
|     namespace {
 | |
| 
 | |
|         constinit bool g_initialized = false;
 | |
|         constinit bool g_enable_disconnection_emulation = false;
 | |
| 
 | |
|         constinit AllocateFunction g_allocate_function     = nullptr;
 | |
|         constinit DeallocateFunction g_deallocate_function = nullptr;
 | |
| 
 | |
|         constinit void *g_buffer = nullptr;
 | |
|         constinit size_t g_buffer_size = 0;
 | |
| 
 | |
|         constinit os::TlsSlot g_tls_slot = {};
 | |
| 
 | |
|         constinit tma::IHtcsManager *g_manager = nullptr;
 | |
|         constinit tma::IHtcsManager *g_monitor = nullptr;
 | |
| 
 | |
|         constinit client::VirtualSocketCollection *g_sockets = nullptr;
 | |
| 
 | |
|         void SetLastError(uintptr_t error_code) {
 | |
|             os::SetTlsValue(g_tls_slot, error_code);
 | |
|         }
 | |
| 
 | |
|         void InitializeImpl(void *buffer, size_t buffer_size, int num_sessions) {
 | |
|             /* Check the session count. */
 | |
|             AMS_ASSERT(0 < num_sessions && num_sessions <= SessionCountMax);
 | |
| 
 | |
|             /* Initialize the manager and monitor. */
 | |
|             client::InitializeSessionManager(std::addressof(g_manager), std::addressof(g_monitor), num_sessions);
 | |
| 
 | |
|             /* Register the process. */
 | |
|             const sf::ClientProcessId process_id{0};
 | |
|             R_ABORT_UNLESS(g_manager->RegisterProcessId(process_id));
 | |
|             R_ABORT_UNLESS(g_monitor->MonitorManager(process_id));
 | |
| 
 | |
|             /* Allocate a tls slot for our last error. */
 | |
|             os::SdkAllocateTlsSlot(std::addressof(g_tls_slot), nullptr);
 | |
| 
 | |
|             /* Setup the virtual socket collection. */
 | |
|             AMS_ASSERT(buffer != nullptr);
 | |
|             g_sockets = reinterpret_cast<client::VirtualSocketCollection *>(buffer);
 | |
|             std::construct_at(g_sockets);
 | |
|             g_sockets->Init(static_cast<u8 *>(buffer) + sizeof(*g_sockets), buffer_size - sizeof(*g_sockets));
 | |
| 
 | |
|             /* Mark initialized. */
 | |
|             g_initialized = true;
 | |
|         }
 | |
| 
 | |
|         void InitializeImpl(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions, int num_sockets) {
 | |
|             /* Check the session count. */
 | |
|             AMS_ASSERT(0 < num_sessions && num_sessions <= SessionCountMax);
 | |
| 
 | |
|             /* Set the allocation functions. */
 | |
|             g_allocate_function   = allocate;
 | |
|             g_deallocate_function = deallocate;
 | |
| 
 | |
|             /* Allocate a buffer. */
 | |
|             g_buffer_size = sizeof(client::VirtualSocketCollection) + client::VirtualSocketCollection::GetWorkingMemorySize(num_sockets);
 | |
|             g_buffer      = g_allocate_function(g_buffer_size);
 | |
| 
 | |
|             /* Initialize. */
 | |
|             InitializeImpl(g_buffer, g_buffer_size, num_sessions);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     bool IsInitialized() {
 | |
|         return g_initialized;
 | |
|     }
 | |
| 
 | |
|     size_t GetWorkingMemorySize(int num_sockets) {
 | |
|         AMS_ASSERT(num_sockets <= SocketCountMax);
 | |
|         return sizeof(client::VirtualSocketCollection) + client::VirtualSocketCollection::GetWorkingMemorySize(num_sockets);
 | |
|     }
 | |
| 
 | |
|     void Initialize(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions) {
 | |
|         /* Check that we're not already initialized. */
 | |
|         AMS_ASSERT(!IsInitialized());
 | |
| 
 | |
|         /* Configure disconnection emulation. */
 | |
|         g_enable_disconnection_emulation = true;
 | |
| 
 | |
|         /* Initialize. */
 | |
|         InitializeImpl(allocate, deallocate, num_sessions, htcs::SocketCountMax);
 | |
|     }
 | |
| 
 | |
|     void Initialize(void *buffer, size_t buffer_size) {
 | |
|         /* Check that we're not already initialized. */
 | |
|         AMS_ASSERT(!IsInitialized());
 | |
| 
 | |
|         /* Configure disconnection emulation. */
 | |
|         g_enable_disconnection_emulation = true;
 | |
| 
 | |
|         /* Initialize. */
 | |
|         InitializeImpl(buffer, buffer_size, htcs::SessionCountMax);
 | |
|     }
 | |
| 
 | |
|     void InitializeForDisableDisconnectionEmulation(AllocateFunction allocate, DeallocateFunction deallocate, int num_sessions) {
 | |
|         /* Check that we're not already initialized. */
 | |
|         AMS_ASSERT(!IsInitialized());
 | |
| 
 | |
|         /* Configure disconnection emulation. */
 | |
|         g_enable_disconnection_emulation = false;
 | |
| 
 | |
|         /* Initialize. */
 | |
|         InitializeImpl(allocate, deallocate, num_sessions, htcs::SocketCountMax);
 | |
|     }
 | |
| 
 | |
|     void InitializeForDisableDisconnectionEmulation(void *buffer, size_t buffer_size) {
 | |
|         /* Check that we're not already initialized. */
 | |
|         AMS_ASSERT(!IsInitialized());
 | |
| 
 | |
|         /* Configure disconnection emulation. */
 | |
|         g_enable_disconnection_emulation = false;
 | |
| 
 | |
|         /* Initialize. */
 | |
|         InitializeImpl(buffer, buffer_size, htcs::SessionCountMax);
 | |
|     }
 | |
| 
 | |
|     void InitializeForSystem(void *buffer, size_t buffer_size, int num_sessions) {
 | |
|         /* Check that we're not already initialized. */
 | |
|         AMS_ASSERT(!IsInitialized());
 | |
| 
 | |
|         /* Configure disconnection emulation. */
 | |
|         g_enable_disconnection_emulation = true;
 | |
| 
 | |
|         /* Initialize. */
 | |
|         InitializeImpl(buffer, buffer_size, num_sessions);
 | |
|     }
 | |
| 
 | |
|     void Finalize() {
 | |
|         /* Check that we're initialized. */
 | |
|         AMS_ASSERT(IsInitialized());
 | |
| 
 | |
|         /* Set not initialized. */
 | |
|         g_initialized = false;
 | |
| 
 | |
|         /* Destroy the virtual socket collection. */
 | |
|         std::destroy_at(g_sockets);
 | |
|         g_sockets = nullptr;
 | |
| 
 | |
|         /* Free the buffer, if we have one. */
 | |
|         if (g_buffer != nullptr) {
 | |
|             g_deallocate_function(g_buffer, g_buffer_size);
 | |
|             g_buffer      = nullptr;
 | |
|             g_buffer_size = 0;
 | |
|         }
 | |
| 
 | |
|         /* Free the tls slot. */
 | |
|         os::FreeTlsSlot(g_tls_slot);
 | |
| 
 | |
|         /* Release the manager objects. */
 | |
|         sf::ReleaseSharedObject(g_manager);
 | |
|         sf::ReleaseSharedObject(g_monitor);
 | |
|         g_manager = nullptr;
 | |
|         g_monitor = nullptr;
 | |
| 
 | |
|         /* Finalize the htcs client sessions. */
 | |
|         client::FinalizeSessionManager();
 | |
|     }
 | |
| 
 | |
|     const HtcsPeerName GetPeerNameAny() {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Get name. */
 | |
|         HtcsPeerName name;
 | |
|         g_manager->GetPeerNameAny(std::addressof(name));
 | |
| 
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     const HtcsPeerName GetDefaultHostName() {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Get name. */
 | |
|         HtcsPeerName name;
 | |
|         g_manager->GetDefaultHostName(std::addressof(name));
 | |
| 
 | |
|         return name;
 | |
|     }
 | |
| 
 | |
|     s32 GetLastError() {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         return static_cast<s32>(os::GetTlsValue(g_tls_slot));
 | |
|     }
 | |
| 
 | |
|     s32 Socket() {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Socket(error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     s32 Close(s32 desc) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Close(desc, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     s32 Connect(s32 desc, const SockAddrHtcs *address) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Check that the address family is correct. */
 | |
|         AMS_ASSERT(address->family == HTCS_AF_HTCS);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Connect(desc, address, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     s32 Bind(s32 desc, const SockAddrHtcs *address) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Check that the address family is correct. */
 | |
|         AMS_ASSERT(address->family == HTCS_AF_HTCS);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Bind(desc, address, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     s32 Listen(s32 desc, s32 backlog_count) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Listen(desc, backlog_count, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     s32 Accept(s32 desc, SockAddrHtcs *address) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Ensure we have an address. */
 | |
|         SockAddrHtcs tmp;
 | |
|         if (address == nullptr) {
 | |
|             address = std::addressof(tmp);
 | |
|         }
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Accept(desc, address, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     s32 Shutdown(s32 desc, s32 how) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Shutdown(desc, how, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     s32 Fcntl(s32 desc, s32 command, s32 value) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Fcntl(desc, command, value, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     s32 Select(s32 count, FdSet *read, FdSet *write, FdSet *exception, TimeVal *timeout) {
 | |
|         AMS_UNUSED(count);
 | |
| 
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Check that we have some form of input. */
 | |
|         if (read == nullptr && write == nullptr && exception == nullptr) {
 | |
|             SetLastError(static_cast<uintptr_t>(HTCS_EINVAL));
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         /* Check that the timeout is valid. */
 | |
|         if (timeout != nullptr && (timeout->tv_sec < 0 || timeout->tv_usec < 0)) {
 | |
|             SetLastError(static_cast<uintptr_t>(HTCS_EINVAL));
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const s32 ret = g_sockets->Select(read, write, exception, timeout, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, s32 flags) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const ssize_t ret = g_sockets->Recv(desc, buffer, buffer_size, flags, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags) {
 | |
|         /* Check that we have a manager. */
 | |
|         AMS_ASSERT(g_manager != nullptr);
 | |
| 
 | |
|         /* Check that we have a socket collection. */
 | |
|         AMS_ASSERT(g_sockets != nullptr);
 | |
| 
 | |
|         /* Perform the operation. */
 | |
|         s32 error_code = 0;
 | |
|         const ssize_t ret = g_sockets->Send(desc, buffer, buffer_size, flags, error_code);
 | |
|         if (ret < 0) {
 | |
|             SetLastError(static_cast<uintptr_t>(error_code));
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
|     }
 | |
| 
 | |
|     void FdSetZero(FdSet *set) {
 | |
|         AMS_ASSERT(set != nullptr);
 | |
| 
 | |
|         std::memset(set, 0, sizeof(*set));
 | |
|     }
 | |
| 
 | |
|     void FdSetSet(s32 fd, FdSet *set) {
 | |
|         AMS_ASSERT(set != nullptr);
 | |
| 
 | |
|         for (auto i = 0; i < FdSetSize; ++i) {
 | |
|             if (set->fds[i] == 0) {
 | |
|                 set->fds[i] = fd;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void FdSetClr(s32 fd, FdSet *set) {
 | |
|         AMS_ASSERT(set != nullptr);
 | |
| 
 | |
|         for (auto i = 0; i < FdSetSize; ++i) {
 | |
|             if (set->fds[i] == fd) {
 | |
|                 std::memcpy(set->fds + i, set->fds + i + 1, (FdSetSize - (i + 1)) * sizeof(fd));
 | |
|                 set->fds[FdSetSize - 1] = 0;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool FdSetIsSet(s32 fd, const FdSet *set) {
 | |
|         AMS_ASSERT(set != nullptr);
 | |
| 
 | |
|         for (auto i = 0; i < FdSetSize; ++i) {
 | |
|             if (set->fds[i] == fd) {
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     namespace client {
 | |
| 
 | |
|         sf::SharedPointer<tma::ISocket> socket(s32 &last_error) {
 | |
|             sf::SharedPointer<tma::ISocket> socket = nullptr;
 | |
|             R_ABORT_UNLESS(g_manager->CreateSocket(std::addressof(last_error), std::addressof(socket), g_enable_disconnection_emulation));
 | |
|             return socket;
 | |
|         }
 | |
| 
 | |
|         s32 close(sf::SharedPointer<tma::ISocket> socket, s32 &last_error) {
 | |
|             s32 res;
 | |
|             socket->Close(std::addressof(last_error), std::addressof(res));
 | |
|             return res;
 | |
|         }
 | |
| 
 | |
|         s32 bind(sf::SharedPointer<tma::ISocket> socket, const htcs::SockAddrHtcs *address, s32 &last_error) {
 | |
|             /* Create null-terminated address. */
 | |
|             htcs::SockAddrHtcs null_terminated_address;
 | |
|             null_terminated_address.family = address->family;
 | |
|             util::Strlcpy(null_terminated_address.peer_name.name, address->peer_name.name, PeerNameBufferLength);
 | |
|             util::Strlcpy(null_terminated_address.port_name.name, address->port_name.name, PortNameBufferLength);
 | |
| 
 | |
|             s32 res;
 | |
|             socket->Bind(std::addressof(last_error), std::addressof(res), null_terminated_address);
 | |
|             return res;
 | |
|         }
 | |
| 
 | |
|         s32 listen(sf::SharedPointer<tma::ISocket> socket, s32 backlog_count, s32 &last_error) {
 | |
|             s32 res;
 | |
|             socket->Listen(std::addressof(last_error), std::addressof(res), backlog_count);
 | |
|             return res;
 | |
|         }
 | |
| 
 | |
|         sf::SharedPointer<tma::ISocket> accept(sf::SharedPointer<tma::ISocket> socket, htcs::SockAddrHtcs *address, s32 &last_error) {
 | |
|             /* Begin the accept. */
 | |
|             sf::SharedPointer<tma::ISocket> res = nullptr;
 | |
|             u32 task_id = 0;
 | |
|             sf::NativeHandle event_handle;
 | |
|             if (R_SUCCEEDED(socket->AcceptStart(std::addressof(task_id), std::addressof(event_handle)))) {
 | |
|                 /* Create system event. */
 | |
|                 os::SystemEventType event;
 | |
|                 os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetOsHandle(), event_handle.IsManaged(), os::EventClearMode_ManualClear);
 | |
|                 event_handle.Detach();
 | |
| 
 | |
|                 /* When we're done, clean up the event. */
 | |
|                 ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); };
 | |
| 
 | |
|                 /* Wait for the accept to finish. */
 | |
|                 os::WaitSystemEvent(std::addressof(event));
 | |
| 
 | |
|                 /* End the accept. */
 | |
|                 socket->AcceptResults(std::addressof(last_error), std::addressof(res), address, task_id);
 | |
|             } else {
 | |
|                 /* Set error. */
 | |
|                 last_error = HTCS_EINTR;
 | |
|             }
 | |
| 
 | |
|             /* Sleep, if an error occurred. */
 | |
|             if (last_error != HTCS_ENONE) {
 | |
|                 os::SleepThread(TimeSpan::FromMilliSeconds(1));
 | |
|             }
 | |
| 
 | |
|             return res;
 | |
|         }
 | |
| 
 | |
|         s32 fcntl(sf::SharedPointer<tma::ISocket> socket, s32 command, s32 value, s32 &last_error) {
 | |
|             s32 res;
 | |
|             socket->Fcntl(std::addressof(last_error), std::addressof(res), command, value);
 | |
|             return res;
 | |
|         }
 | |
| 
 | |
|         s32 shutdown(sf::SharedPointer<tma::ISocket> socket, s32 how, s32 &last_error) {
 | |
|             s32 res;
 | |
|             socket->Shutdown(std::addressof(last_error), std::addressof(res), how);
 | |
|             return res;
 | |
|         }
 | |
| 
 | |
|         s32 connect(sf::SharedPointer<tma::ISocket> socket, const htcs::SockAddrHtcs *address, s32 &last_error) {
 | |
|             /* Create null-terminated address. */
 | |
|             htcs::SockAddrHtcs null_terminated_address;
 | |
|             null_terminated_address.family = address->family;
 | |
|             util::Strlcpy(null_terminated_address.peer_name.name, address->peer_name.name, PeerNameBufferLength);
 | |
|             util::Strlcpy(null_terminated_address.port_name.name, address->port_name.name, PortNameBufferLength);
 | |
| 
 | |
|             s32 res;
 | |
|             socket->Connect(std::addressof(last_error), std::addressof(res), null_terminated_address);
 | |
|             return res;
 | |
|         }
 | |
| 
 | |
|         s32 select(s32 * const read, s32 &num_read, s32 * const write, s32 &num_write, s32 * const except, s32 &num_except, htcs::TimeVal *timeout, s32 &last_error) {
 | |
|             /* Determine the timeout values. */
 | |
|             s64 tv_sec  = -1;
 | |
|             s64 tv_usec = -1;
 | |
|             if (timeout != nullptr) {
 | |
|                 tv_sec  = timeout->tv_sec;
 | |
|                 tv_usec = timeout->tv_usec;
 | |
|             }
 | |
| 
 | |
|             using InArray  = sf::InMapAliasArray<s32>;
 | |
|             using OutArray = sf::OutMapAliasArray<s32>;
 | |
| 
 | |
|             /* Begin the select. */
 | |
|             s32 res = -1;
 | |
|             u32 task_id = 0;
 | |
|             sf::NativeHandle event_handle;
 | |
|             if (R_SUCCEEDED(g_manager->StartSelect(std::addressof(task_id), std::addressof(event_handle), InArray(read, num_read), InArray(write, num_write), InArray(except, num_except), tv_sec, tv_usec))) {
 | |
|                 /* Create system event. */
 | |
|                 os::SystemEventType event;
 | |
|                 os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetOsHandle(), event_handle.IsManaged(), os::EventClearMode_ManualClear);
 | |
|                 event_handle.Detach();
 | |
| 
 | |
|                 /* When we're done, clean up the event. */
 | |
|                 ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); };
 | |
| 
 | |
|                 /* Wait for the select to finish. */
 | |
|                 os::WaitSystemEvent(std::addressof(event));
 | |
| 
 | |
|                 /* End the select. */
 | |
|                 g_manager->EndSelect(std::addressof(last_error), std::addressof(res), OutArray(read, num_read), OutArray(write, num_write), OutArray(except, num_except), task_id);
 | |
|             } else {
 | |
|                 /* Set error. */
 | |
|                 last_error = HTCS_EINTR;
 | |
|                 os::SleepThread(TimeSpan::FromMilliSeconds(1));
 | |
|             }
 | |
| 
 | |
|             return res;
 | |
|         }
 | |
| 
 | |
|         namespace {
 | |
| 
 | |
|             constexpr size_t MaximumBufferSizeForSmallTransfer = 0xDFE0;
 | |
| 
 | |
|             ssize_t recvLarge(sf::SharedPointer<tma::ISocket> socket, void *buffer, size_t buffer_size, s32 flags, s32 &last_error) {
 | |
|                 /* Setup. */
 | |
|                 s64 res = -1;
 | |
|                 last_error = HTCS_EINTR;
 | |
| 
 | |
|                 /* Start the receive. */
 | |
|                 u32 task_id = 0;
 | |
|                 sf::NativeHandle event_handle;
 | |
|                 if (R_SUCCEEDED(socket->StartRecv(std::addressof(task_id), std::addressof(event_handle), static_cast<s64>(buffer_size), flags))) {
 | |
|                     /* Create system event. */
 | |
|                     os::SystemEventType event;
 | |
|                     os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetOsHandle(), event_handle.IsManaged(), os::EventClearMode_ManualClear);
 | |
|                     event_handle.Detach();
 | |
| 
 | |
|                     /* When we're done, clean up the event. */
 | |
|                     ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); };
 | |
| 
 | |
|                     /* Wait for the receive to finish. */
 | |
|                     os::WaitSystemEvent(std::addressof(event));
 | |
| 
 | |
|                     /* End the receive. */
 | |
|                     socket->EndRecv(std::addressof(last_error), std::addressof(res), sf::OutAutoSelectBuffer(buffer, buffer_size), task_id);
 | |
|                 } else {
 | |
|                     /* Set error. */
 | |
|                     last_error = HTCS_EINTR;
 | |
|                     os::SleepThread(TimeSpan::FromMilliSeconds(1));
 | |
|                 }
 | |
| 
 | |
|                 return static_cast<ssize_t>(res);
 | |
|             }
 | |
| 
 | |
|             ssize_t sendLarge(sf::SharedPointer<tma::ISocket> socket, const void *buffer, size_t buffer_size, s32 flags, s32 &last_error) {
 | |
|                 /* Setup. */
 | |
|                 s64 res = -1;
 | |
|                 last_error = HTCS_EINTR;
 | |
| 
 | |
|                 /* Start the send. */
 | |
|                 u32 task_id = 0;
 | |
|                 s64 max_size = 0;
 | |
|                 sf::NativeHandle event_handle;
 | |
|                 if (R_SUCCEEDED(socket->StartSend(std::addressof(task_id), std::addressof(event_handle), std::addressof(max_size), static_cast<s64>(buffer_size), flags))) {
 | |
|                     /* Create system event. */
 | |
|                     os::SystemEventType event;
 | |
|                     os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetOsHandle(), event_handle.IsManaged(), os::EventClearMode_ManualClear);
 | |
|                     event_handle.Detach();
 | |
| 
 | |
|                     /* When we're done, clean up the event. */
 | |
|                     ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); };
 | |
| 
 | |
|                     /* Send all the data. */
 | |
|                     bool done = false;
 | |
|                     size_t sent = 0;
 | |
|                     while (sent < buffer_size) {
 | |
|                         /* Determine how much to send, this iteration. */
 | |
|                         const u8 *cur = static_cast<const u8 *>(buffer) + sent;
 | |
|                         const s64 cur_size = std::min(max_size, static_cast<s64>(buffer_size - sent));
 | |
| 
 | |
|                         /* Continue sending data. */
 | |
|                         s64 cur_sent = 0;
 | |
|                         bool wait = false;
 | |
|                         const Result result = socket->ContinueSend(std::addressof(cur_sent), std::addressof(wait), sf::InNonSecureAutoSelectBuffer(cur, cur_size), task_id);
 | |
|                         if (cur_sent <= 0 || R_FAILED(result)) {
 | |
|                             done = true;
 | |
|                             break;
 | |
|                         }
 | |
| 
 | |
|                         /* Wait if we should. */
 | |
|                         if (wait) {
 | |
|                             os::WaitSystemEvent(std::addressof(event));
 | |
|                             os::ClearSystemEvent(std::addressof(event));
 | |
|                         }
 | |
| 
 | |
|                         /* Advance. */
 | |
|                         sent += cur_sent;
 | |
|                     }
 | |
| 
 | |
|                     /* Wait for the send to finish. */
 | |
|                     if (!done) {
 | |
|                         os::WaitSystemEvent(std::addressof(event));
 | |
|                     }
 | |
| 
 | |
|                     /* End the send. */
 | |
|                     socket->EndSend(std::addressof(last_error), std::addressof(res), task_id);
 | |
|                 } else {
 | |
|                     /* Set error. */
 | |
|                     last_error = HTCS_EINTR;
 | |
|                     os::SleepThread(TimeSpan::FromMilliSeconds(1));
 | |
|                 }
 | |
| 
 | |
|                 return static_cast<ssize_t>(res);
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         ssize_t recv(sf::SharedPointer<tma::ISocket> socket, void *buffer, size_t buffer_size, s32 flags, s32 &last_error) {
 | |
|             /* Determine how much to receive. */
 | |
|             size_t recv_size = buffer_size;
 | |
| 
 | |
|             if ((flags & HTCS_MSG_WAITALL) == 0) {
 | |
|                 recv_size = std::min(MaximumBufferSizeForSmallTransfer, buffer_size);
 | |
|             }
 | |
| 
 | |
|             /* Perform a large receive, if we have to. */
 | |
|             if (recv_size > MaximumBufferSizeForSmallTransfer) {
 | |
|                 return recvLarge(socket, buffer, recv_size, flags, last_error);
 | |
|             }
 | |
| 
 | |
|             /* Start the receive. */
 | |
|             s64 res = -1;
 | |
|             u32 task_id = 0;
 | |
|             sf::NativeHandle event_handle;
 | |
|             if (R_SUCCEEDED(socket->RecvStart(std::addressof(task_id), std::addressof(event_handle), static_cast<s32>(recv_size), flags))) {
 | |
|                 /* Create system event. */
 | |
|                 os::SystemEventType event;
 | |
|                 os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetOsHandle(), event_handle.IsManaged(), os::EventClearMode_ManualClear);
 | |
|                 event_handle.Detach();
 | |
| 
 | |
|                 /* When we're done, clean up the event. */
 | |
|                 ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); };
 | |
| 
 | |
|                 /* Wait for the receive to finish. */
 | |
|                 os::WaitSystemEvent(std::addressof(event));
 | |
| 
 | |
|                 /* End the receive. */
 | |
|                 socket->RecvResults(std::addressof(last_error), std::addressof(res), sf::OutAutoSelectBuffer(buffer, recv_size), task_id);
 | |
|             } else {
 | |
|                 /* Set error. */
 | |
|                 last_error = HTCS_EINTR;
 | |
|                 os::SleepThread(TimeSpan::FromMilliSeconds(1));
 | |
|             }
 | |
| 
 | |
|             return static_cast<ssize_t>(res);
 | |
|         }
 | |
| 
 | |
|         ssize_t send(sf::SharedPointer<tma::ISocket> socket, const void *buffer, size_t buffer_size, s32 flags, s32 &last_error) {
 | |
|             /* Perform a large send, if we have to. */
 | |
|             if (buffer_size > MaximumBufferSizeForSmallTransfer) {
 | |
|                 return sendLarge(socket, buffer, buffer_size, flags, last_error);
 | |
|             }
 | |
| 
 | |
|             /* Start the send. */
 | |
|             s64 res = -1;
 | |
|             u32 task_id = 0;
 | |
|             sf::NativeHandle event_handle;
 | |
|             if (R_SUCCEEDED(socket->SendStart(std::addressof(task_id), std::addressof(event_handle), sf::InNonSecureAutoSelectBuffer(buffer, buffer_size), flags))) {
 | |
|                 /* Create system event. */
 | |
|                 os::SystemEventType event;
 | |
|                 os::AttachReadableHandleToSystemEvent(std::addressof(event), event_handle.GetOsHandle(), event_handle.IsManaged(), os::EventClearMode_ManualClear);
 | |
|                 event_handle.Detach();
 | |
| 
 | |
|                 /* When we're done, clean up the event. */
 | |
|                 ON_SCOPE_EXIT { os::DestroySystemEvent(std::addressof(event)); };
 | |
| 
 | |
|                 /* Wait for the send to finish. */
 | |
|                 os::WaitSystemEvent(std::addressof(event));
 | |
| 
 | |
|                 /* End the send. */
 | |
|                 socket->SendResults(std::addressof(last_error), std::addressof(res), task_id);
 | |
|             } else {
 | |
|                 /* Set error. */
 | |
|                 last_error = HTCS_EINTR;
 | |
|                 os::SleepThread(TimeSpan::FromMilliSeconds(1));
 | |
|             }
 | |
| 
 | |
|             return static_cast<ssize_t>(res);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
| }
 |