yuzu/src/core/hle/service/soc_u.cpp
archshift ef24e72b26 Asserts: break/crash program, fit to style guide; log.h->assert.h
Involves making asserts use printf instead of the log functions (log functions are asynchronous and, as such, the log won't be printed in time)
As such, the log type argument was removed (printf obviously can't use it, and it's made obsolete by the file and line printing)

Also removed some GEKKO cruft.
2015-02-10 18:30:31 -08:00

746 lines
24 KiB
C++

// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/platform.h"
#if EMU_PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#include <ws2tcpip.h>
// MinGW does not define several errno constants
#ifndef _MSC_VER
#define EBADMSG 104
#define ENODATA 120
#define ENOMSG 122
#define ENOSR 124
#define ENOSTR 125
#define ETIME 137
#define EIDRM 2001
#define ENOLINK 2002
#endif // _MSC_VER
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <poll.h>
#endif
#include "common/scope_exit.h"
#include "core/hle/hle.h"
#include "core/hle/service/soc_u.h"
#include <unordered_map>
#if EMU_PLATFORM == PLATFORM_WINDOWS
# define WSAEAGAIN WSAEWOULDBLOCK
# define WSAEMULTIHOP -1 // Invalid dummy value
# define ERRNO(x) WSA##x
# define GET_ERRNO WSAGetLastError()
# define poll(x, y, z) WSAPoll(x, y, z);
#else
# define ERRNO(x) x
# define GET_ERRNO errno
# define closesocket(x) close(x)
#endif
static const s32 SOCKET_ERROR_VALUE = -1;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace SOC_U
namespace SOC_U {
/// Holds the translation from system network errors to 3DS network errors
static const std::unordered_map<int, int> error_map = { {
{ E2BIG, 1 },
{ ERRNO(EACCES), 2 },
{ ERRNO(EADDRINUSE), 3 },
{ ERRNO(EADDRNOTAVAIL), 4 },
{ ERRNO(EAFNOSUPPORT), 5 },
{ ERRNO(EAGAIN), 6 },
{ ERRNO(EALREADY), 7 },
{ ERRNO(EBADF), 8 },
{ EBADMSG, 9 },
{ EBUSY, 10 },
{ ECANCELED, 11 },
{ ECHILD, 12 },
{ ERRNO(ECONNABORTED), 13 },
{ ERRNO(ECONNREFUSED), 14 },
{ ERRNO(ECONNRESET), 15 },
{ EDEADLK, 16 },
{ ERRNO(EDESTADDRREQ), 17 },
{ EDOM, 18 },
{ ERRNO(EDQUOT), 19 },
{ EEXIST, 20 },
{ ERRNO(EFAULT), 21 },
{ EFBIG, 22 },
{ ERRNO(EHOSTUNREACH), 23 },
{ EIDRM, 24 },
{ EILSEQ, 25 },
{ ERRNO(EINPROGRESS), 26 },
{ ERRNO(EINTR), 27 },
{ ERRNO(EINVAL), 28 },
{ EIO, 29 },
{ ERRNO(EISCONN), 30 },
{ EISDIR, 31 },
{ ERRNO(ELOOP), 32 },
{ ERRNO(EMFILE), 33 },
{ EMLINK, 34 },
{ ERRNO(EMSGSIZE), 35 },
{ ERRNO(EMULTIHOP), 36 },
{ ERRNO(ENAMETOOLONG), 37 },
{ ERRNO(ENETDOWN), 38 },
{ ERRNO(ENETRESET), 39 },
{ ERRNO(ENETUNREACH), 40 },
{ ENFILE, 41 },
{ ERRNO(ENOBUFS), 42 },
{ ENODATA, 43 },
{ ENODEV, 44 },
{ ENOENT, 45 },
{ ENOEXEC, 46 },
{ ENOLCK, 47 },
{ ENOLINK, 48 },
{ ENOMEM, 49 },
{ ENOMSG, 50 },
{ ERRNO(ENOPROTOOPT), 51 },
{ ENOSPC, 52 },
{ ENOSR, 53 },
{ ENOSTR, 54 },
{ ENOSYS, 55 },
{ ERRNO(ENOTCONN), 56 },
{ ENOTDIR, 57 },
{ ERRNO(ENOTEMPTY), 58 },
{ ERRNO(ENOTSOCK), 59 },
{ ENOTSUP, 60 },
{ ENOTTY, 61 },
{ ENXIO, 62 },
{ ERRNO(EOPNOTSUPP), 63 },
{ EOVERFLOW, 64 },
{ EPERM, 65 },
{ EPIPE, 66 },
{ EPROTO, 67 },
{ ERRNO(EPROTONOSUPPORT), 68 },
{ ERRNO(EPROTOTYPE), 69 },
{ ERANGE, 70 },
{ EROFS, 71 },
{ ESPIPE, 72 },
{ ESRCH, 73 },
{ ERRNO(ESTALE), 74 },
{ ETIME, 75 },
{ ERRNO(ETIMEDOUT), 76 }
}};
/// Converts a network error from platform-specific to 3ds-specific
static int TranslateError(int error) {
auto found = error_map.find(error);
if (found != error_map.end())
return -found->second;
return error;
}
/// Holds information about a particular socket
struct SocketHolder {
u32 socket_fd; ///< The socket descriptor
bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows.
};
/// Structure to represent the 3ds' pollfd structure, which is different than most implementations
struct CTRPollFD {
u32 fd; ///< Socket handle
union Events {
u32 hex; ///< The complete value formed by the flags
BitField<0, 1, u32> pollin;
BitField<1, 1, u32> pollpri;
BitField<2, 1, u32> pollhup;
BitField<3, 1, u32> pollerr;
BitField<4, 1, u32> pollout;
BitField<5, 1, u32> pollnval;
Events& operator=(const Events& other) {
hex = other.hex;
return *this;
}
/// Translates the resulting events of a Poll operation from platform-specific to 3ds specific
static Events TranslateTo3DS(u32 input_event) {
Events ev = {};
if (input_event & POLLIN)
ev.pollin = 1;
if (input_event & POLLPRI)
ev.pollpri = 1;
if (input_event & POLLHUP)
ev.pollhup = 1;
if (input_event & POLLERR)
ev.pollerr = 1;
if (input_event & POLLOUT)
ev.pollout = 1;
if (input_event & POLLNVAL)
ev.pollnval = 1;
return ev;
}
/// Translates the resulting events of a Poll operation from 3ds specific to platform specific
static u32 TranslateToPlatform(Events input_event) {
u32 ret = 0;
if (input_event.pollin)
ret |= POLLIN;
if (input_event.pollpri)
ret |= POLLPRI;
if (input_event.pollhup)
ret |= POLLHUP;
if (input_event.pollerr)
ret |= POLLERR;
if (input_event.pollout)
ret |= POLLOUT;
if (input_event.pollnval)
ret |= POLLNVAL;
return ret;
}
};
Events events; ///< Events to poll for (input)
Events revents; ///< Events received (output)
/// Converts a platform-specific pollfd to a 3ds specific structure
static CTRPollFD FromPlatform(pollfd const& fd) {
CTRPollFD result;
result.events.hex = Events::TranslateTo3DS(fd.events).hex;
result.revents.hex = Events::TranslateTo3DS(fd.revents).hex;
result.fd = static_cast<u32>(fd.fd);
return result;
}
/// Converts a 3ds specific pollfd to a platform-specific structure
static pollfd ToPlatform(CTRPollFD const& fd) {
pollfd result;
result.events = Events::TranslateToPlatform(fd.events);
result.revents = Events::TranslateToPlatform(fd.revents);
result.fd = fd.fd;
return result;
}
};
/// Union to represent the 3ds' sockaddr structure
union CTRSockAddr {
/// Structure to represent a raw sockaddr
struct {
u8 len; ///< The length of the entire structure, only the set fields count
u8 sa_family; ///< The address family of the sockaddr
u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family
} raw;
/// Structure to represent the 3ds' sockaddr_in structure
struct CTRSockAddrIn {
u8 len; ///< The length of the entire structure
u8 sin_family; ///< The address family of the sockaddr_in
u16 sin_port; ///< The port associated with this sockaddr_in
u32 sin_addr; ///< The actual address of the sockaddr_in
} in;
/// Convert a 3DS CTRSockAddr to a platform-specific sockaddr
static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) {
sockaddr result;
result.sa_family = ctr_addr.raw.sa_family;
memset(result.sa_data, 0, sizeof(result.sa_data));
// We can not guarantee ABI compatibility between platforms so we copy the fields manually
switch (result.sa_family) {
case AF_INET:
{
sockaddr_in* result_in = reinterpret_cast<sockaddr_in*>(&result);
result_in->sin_port = ctr_addr.in.sin_port;
result_in->sin_addr.s_addr = ctr_addr.in.sin_addr;
memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero));
break;
}
default:
ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
break;
}
return result;
}
/// Convert a platform-specific sockaddr to a 3DS CTRSockAddr
static CTRSockAddr FromPlatform(sockaddr const& addr) {
CTRSockAddr result;
result.raw.sa_family = static_cast<u8>(addr.sa_family);
// We can not guarantee ABI compatibility between platforms so we copy the fields manually
switch (result.raw.sa_family) {
case AF_INET:
{
sockaddr_in const* addr_in = reinterpret_cast<sockaddr_in const*>(&addr);
result.raw.len = sizeof(CTRSockAddrIn);
result.in.sin_port = addr_in->sin_port;
result.in.sin_addr = addr_in->sin_addr.s_addr;
break;
}
default:
ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
break;
}
return result;
}
};
/// Holds info about the currently open sockets
static std::unordered_map<u32, SocketHolder> open_sockets;
/// Close all open sockets
static void CleanupSockets() {
for (auto sock : open_sockets)
closesocket(sock.second.socket_fd);
open_sockets.clear();
}
static void Socket(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 domain = cmd_buffer[1]; // Address family
u32 type = cmd_buffer[2];
u32 protocol = cmd_buffer[3];
// Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use
if (protocol != 0) {
cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
return;
}
if (domain != AF_INET) {
cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
return;
}
if (type != SOCK_DGRAM && type != SOCK_STREAM) {
cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
return;
}
u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol));
if ((s32)socket_handle != SOCKET_ERROR_VALUE)
open_sockets[socket_handle] = { socket_handle, true };
int result = 0;
if ((s32)socket_handle == SOCKET_ERROR_VALUE)
result = TranslateError(GET_ERRNO);
cmd_buffer[1] = result;
cmd_buffer[2] = socket_handle;
}
static void Bind(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
CTRSockAddr* ctr_sock_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
if (ctr_sock_addr == nullptr) {
cmd_buffer[1] = -1; // TODO(Subv): Correct code
return;
}
sockaddr sock_addr = CTRSockAddr::ToPlatform(*ctr_sock_addr);
int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
int result = 0;
if (res != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = res;
cmd_buffer[1] = result;
}
static void Fcntl(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 ctr_cmd = cmd_buffer[2];
u32 ctr_arg = cmd_buffer[3];
int result = 0;
u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX)
SCOPE_EXIT({
cmd_buffer[1] = result;
cmd_buffer[2] = posix_ret;
});
if (ctr_cmd == 3) { // F_GETFL
#if EMU_PLATFORM == PLATFORM_WINDOWS
posix_ret = 0;
auto iter = open_sockets.find(socket_handle);
if (iter != open_sockets.end() && iter->second.blocking == false)
posix_ret |= 4; // O_NONBLOCK
#else
int ret = ::fcntl(socket_handle, F_GETFL, 0);
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
posix_ret = -1;
return;
}
posix_ret = 0;
if (ret & O_NONBLOCK)
posix_ret |= 4; // O_NONBLOCK
#endif
} else if (ctr_cmd == 4) { // F_SETFL
#if EMU_PLATFORM == PLATFORM_WINDOWS
unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
int ret = ioctlsocket(socket_handle, FIONBIO, &tmp);
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
posix_ret = -1;
return;
}
auto iter = open_sockets.find(socket_handle);
if (iter != open_sockets.end())
iter->second.blocking = (tmp == 0);
#else
int flags = ::fcntl(socket_handle, F_GETFL, 0);
if (flags == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
posix_ret = -1;
return;
}
flags &= ~O_NONBLOCK;
if (ctr_arg & 4) // O_NONBLOCK
flags |= O_NONBLOCK;
int ret = ::fcntl(socket_handle, F_SETFL, flags);
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
posix_ret = -1;
return;
}
#endif
} else {
LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call", ctr_cmd);
result = TranslateError(EINVAL); // TODO: Find the correct error
posix_ret = -1;
return;
}
}
static void Listen(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 backlog = cmd_buffer[2];
int ret = ::listen(socket_handle, backlog);
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void Accept(Service::Interface* self) {
// TODO(Subv): Calling this function on a blocking socket will block the emu thread,
// preventing graceful shutdown when closing the emulator, this can be fixed by always
// performing nonblocking operations and spinlock until the data is available
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
socklen_t max_addr_len = static_cast<socklen_t>(cmd_buffer[2]);
sockaddr addr;
socklen_t addr_len = sizeof(addr);
u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len));
if ((s32)ret != SOCKET_ERROR_VALUE)
open_sockets[ret] = { ret, true };
int result = 0;
if ((s32)ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
} else {
CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len);
}
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void GetHostId(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
char name[128];
gethostname(name, sizeof(name));
hostent* host = gethostbyname(name);
in_addr* addr = reinterpret_cast<in_addr*>(host->h_addr);
cmd_buffer[2] = addr->s_addr;
cmd_buffer[1] = 0;
}
static void Close(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
int ret = 0;
open_sockets.erase(socket_handle);
ret = closesocket(socket_handle);
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void SendTo(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
u32 flags = cmd_buffer[3];
u32 addr_len = cmd_buffer[4];
u8* input_buff = Memory::GetPointer(cmd_buffer[8]);
CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[10]));
if (ctr_dest_addr == nullptr) {
cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
return;
}
int ret = -1;
if (addr_len > 0) {
sockaddr dest_addr = CTRSockAddr::ToPlatform(*ctr_dest_addr);
ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, &dest_addr, sizeof(dest_addr));
} else {
ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, nullptr, 0);
}
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void RecvFrom(Service::Interface* self) {
// TODO(Subv): Calling this function on a blocking socket will block the emu thread,
// preventing graceful shutdown when closing the emulator, this can be fixed by always
// performing nonblocking operations and spinlock until the data is available
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
u32 flags = cmd_buffer[3];
socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]);
u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
sockaddr src_addr;
socklen_t src_addr_len = sizeof(src_addr);
int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
if (cmd_buffer[0x1A0 >> 2] != 0) {
CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2]));
*ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
}
int result = 0;
int total_received = ret;
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
total_received = 0;
}
cmd_buffer[1] = result;
cmd_buffer[2] = ret;
cmd_buffer[3] = total_received;
}
static void Poll(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 nfds = cmd_buffer[1];
int timeout = cmd_buffer[2];
CTRPollFD* input_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[6]));
CTRPollFD* output_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
// so we have to copy the data
pollfd* platform_pollfd = new pollfd[nfds];
for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]);
int ret = ::poll(platform_pollfd, nfds, timeout);
// Now update the output pollfd structure
for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]);
delete[] platform_pollfd;
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
result = TranslateError(GET_ERRNO);
cmd_buffer[1] = result;
cmd_buffer[2] = ret;
}
static void GetSockName(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
socklen_t ctr_len = cmd_buffer[2];
CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr);
int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len);
if (ctr_dest_addr != nullptr) {
*ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
} else {
cmd_buffer[1] = -1; // TODO(Subv): Verify error
return;
}
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void Shutdown(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
int how = cmd_buffer[2];
int ret = ::shutdown(socket_handle, how);
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void GetPeerName(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
socklen_t len = cmd_buffer[2];
CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr);
int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len);
if (ctr_dest_addr != nullptr) {
*ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
} else {
cmd_buffer[1] = -1;
return;
}
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void Connect(Service::Interface* self) {
// TODO(Subv): Calling this function on a blocking socket will block the emu thread,
// preventing graceful shutdown when closing the emulator, this can be fixed by always
// performing nonblocking operations and spinlock until the data is available
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
socklen_t len = cmd_buffer[2];
CTRSockAddr* ctr_input_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
if (ctr_input_addr == nullptr) {
cmd_buffer[1] = -1; // TODO(Subv): Verify error
return;
}
sockaddr input_addr = CTRSockAddr::ToPlatform(*ctr_input_addr);
int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void InitializeSockets(Service::Interface* self) {
// TODO(Subv): Implement
#if EMU_PLATFORM == PLATFORM_WINDOWS
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
#endif
u32* cmd_buffer = Kernel::GetCommandBuffer();
cmd_buffer[1] = 0;
}
static void ShutdownSockets(Service::Interface* self) {
// TODO(Subv): Implement
CleanupSockets();
#if EMU_PLATFORM == PLATFORM_WINDOWS
WSACleanup();
#endif
u32* cmd_buffer = Kernel::GetCommandBuffer();
cmd_buffer[1] = 0;
}
const Interface::FunctionInfo FunctionTable[] = {
{0x00010044, InitializeSockets, "InitializeSockets"},
{0x000200C2, Socket, "Socket"},
{0x00030082, Listen, "Listen"},
{0x00040082, Accept, "Accept"},
{0x00050084, Bind, "Bind"},
{0x00060084, Connect, "Connect"},
{0x00070104, nullptr, "recvfrom_other"},
{0x00080102, RecvFrom, "RecvFrom"},
{0x00090106, nullptr, "sendto_other"},
{0x000A0106, SendTo, "SendTo"},
{0x000B0042, Close, "Close"},
{0x000C0082, Shutdown, "Shutdown"},
{0x000D0082, nullptr, "GetHostByName"},
{0x000E00C2, nullptr, "GetHostByAddr"},
{0x000F0106, nullptr, "unknown_resolve_ip"},
{0x00110102, nullptr, "GetSockOpt"},
{0x00120104, nullptr, "SetSockOpt"},
{0x001300C2, Fcntl, "Fcntl"},
{0x00140084, Poll, "Poll"},
{0x00150042, nullptr, "SockAtMark"},
{0x00160000, GetHostId, "GetHostId"},
{0x00170082, GetSockName, "GetSockName"},
{0x00180082, GetPeerName, "GetPeerName"},
{0x00190000, ShutdownSockets, "ShutdownSockets"},
{0x001A00C0, nullptr, "GetNetworkOpt"},
{0x001B0040, nullptr, "ICMPSocket"},
{0x001C0104, nullptr, "ICMPPing"},
{0x001D0040, nullptr, "ICMPCancel"},
{0x001E0040, nullptr, "ICMPClose"},
{0x001F0040, nullptr, "GetResolverInfo"},
{0x00210002, nullptr, "CloseSockets"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
Register(FunctionTable);
}
Interface::~Interface() {
CleanupSockets();
#if EMU_PLATFORM == PLATFORM_WINDOWS
WSACleanup();
#endif
}
} // namespace