Merge pull request 'hle: Improve network service implementations and add newer firmware stubs' (#11) from feature/network-service-enhancements into main

Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/11
This commit is contained in:
Zephyron
2025-11-01 08:47:46 +00:00
7 changed files with 285 additions and 30 deletions

View File

@@ -362,8 +362,17 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
}
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_pseudo_device_id = {};
LOG_DEBUG(Service_AM, "called");
// Generate a persistent pseudo device ID for online play and telemetry
// Based on hardware/system info for consistency across sessions
// Using MakeRandomWithSeed to ensure deterministic generation
const u32 device_seed = static_cast<u32>(system.GetApplicationProcessProgramID());
*out_pseudo_device_id = Common::UUID::MakeRandomWithSeed(device_seed);
LOG_DEBUG(Service_AM, "Generated PseudoDeviceId: {}",
out_pseudo_device_id->FormattedString());
R_SUCCEED();
}

View File

@@ -42,15 +42,15 @@ IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& sys
{202, D<&IUserLocalCommunicationService::CreateNetwork>, "CreateNetwork"},
{203, D<&IUserLocalCommunicationService::CreateNetworkPrivate>, "CreateNetworkPrivate"},
{204, D<&IUserLocalCommunicationService::DestroyNetwork>, "DestroyNetwork"},
{205, nullptr, "Reject"},
{205, D<&IUserLocalCommunicationService::Reject>, "Reject"},
{206, D<&IUserLocalCommunicationService::SetAdvertiseData>, "SetAdvertiseData"},
{207, D<&IUserLocalCommunicationService::SetStationAcceptPolicy>, "SetStationAcceptPolicy"},
{208, D<&IUserLocalCommunicationService::AddAcceptFilterEntry>, "AddAcceptFilterEntry"},
{209, nullptr, "ClearAcceptFilter"},
{209, D<&IUserLocalCommunicationService::ClearAcceptFilter>, "ClearAcceptFilter"},
{300, D<&IUserLocalCommunicationService::OpenStation>, "OpenStation"},
{301, D<&IUserLocalCommunicationService::CloseStation>, "CloseStation"},
{302, D<&IUserLocalCommunicationService::Connect>, "Connect"},
{303, nullptr, "ConnectPrivate"},
{303, D<&IUserLocalCommunicationService::ConnectPrivate>, "ConnectPrivate"},
{304, D<&IUserLocalCommunicationService::Disconnect>, "Disconnect"},
{400, D<&IUserLocalCommunicationService::Initialize>, "Initialize"},
{401, D<&IUserLocalCommunicationService::Finalize>, "Finalize"},
@@ -227,6 +227,13 @@ Result IUserLocalCommunicationService::DestroyNetwork() {
R_RETURN(lan_discovery.DestroyNetwork());
}
Result IUserLocalCommunicationService::Reject(Ipv4Address ip_address, u16 port) {
LOG_WARNING(Service_LDN, "(STUBBED) called, ip_address={}.{}.{}.{}, port={}",
ip_address[0], ip_address[1], ip_address[2], ip_address[3], port);
R_SUCCEED();
}
Result IUserLocalCommunicationService::SetAdvertiseData(
InBuffer<BufferAttr_HipcAutoSelect> buffer_data) {
LOG_INFO(Service_LDN, "called");
@@ -244,6 +251,11 @@ Result IUserLocalCommunicationService::AddAcceptFilterEntry(MacAddress mac_addre
R_SUCCEED();
}
Result IUserLocalCommunicationService::ClearAcceptFilter() {
LOG_WARNING(Service_LDN, "(STUBBED) called");
R_SUCCEED();
}
Result IUserLocalCommunicationService::OpenStation() {
LOG_INFO(Service_LDN, "called");
@@ -269,6 +281,16 @@ Result IUserLocalCommunicationService::Connect(
static_cast<u16>(connect_data.local_communication_version)));
}
Result IUserLocalCommunicationService::ConnectPrivate(
const ConnectNetworkData& connect_data,
InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
// ConnectPrivate is similar to Connect but with additional private network parameters
// For now, stub it - would need to implement private network logic
R_SUCCEED();
}
Result IUserLocalCommunicationService::Disconnect() {
LOG_INFO(Service_LDN, "called");

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -63,12 +64,16 @@ private:
Result DestroyNetwork();
Result Reject(Ipv4Address ip_address, u16 port);
Result SetAdvertiseData(InBuffer<BufferAttr_HipcAutoSelect> buffer_data);
Result SetStationAcceptPolicy(AcceptPolicy accept_policy);
Result AddAcceptFilterEntry(MacAddress mac_address);
Result ClearAcceptFilter();
Result OpenStation();
Result CloseStation();
@@ -76,6 +81,9 @@ private:
Result Connect(const ConnectNetworkData& connect_data,
InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info);
Result ConnectPrivate(const ConnectNetworkData& connect_data,
InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info);
Result Disconnect();
Result Initialize(ClientProcessId aruid);

View File

@@ -26,9 +26,10 @@ namespace {
namespace Service::NIFM {
// This is nn::nifm::RequestState
// Reference: https://switchbrew.org/wiki/Network_Interface_services#RequestState
enum class RequestState : u32 {
NotSubmitted = 1,
Invalid = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
Invalid = 0,
Free = 1, ///< NotSubmitted/Free state
OnHold = 2,
Accepted = 3,
Blocking = 4,
@@ -56,6 +57,17 @@ enum class NetworkProfileType : u32 {
Temporary,
};
// This is nn::nifm::ConnectionConfirmationOption
// Reference: https://switchbrew.org/wiki/Network_Interface_services#ConnectionConfirmationOption
enum class ConnectionConfirmationOption : u32 {
Invalid = 0,
Prohibited = 1,
NotRequired = 2,
Preferred = 3,
Required = 4,
Forced = 5,
};
// This is nn::nifm::IpAddressSetting
struct IpAddressSetting {
bool is_automatic{};
@@ -258,7 +270,7 @@ public:
event1 = CreateKEvent(service_context, "IRequest:Event1");
event2 = CreateKEvent(service_context, "IRequest:Event2");
state = RequestState::NotSubmitted;
state = RequestState::Free;
}
~IRequest() override {
@@ -270,7 +282,7 @@ private:
void Submit(HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
if (state == RequestState::NotSubmitted) {
if (state == RequestState::Free) {
UpdateState(RequestState::OnHold);
}
@@ -302,7 +314,7 @@ private:
const auto result = [this] {
const auto has_connection = Network::GetHostIPv4Address().has_value();
switch (state) {
case RequestState::NotSubmitted:
case RequestState::Free:
return has_connection ? ResultSuccess : ResultNetworkCommunicationDisabled;
case RequestState::OnHold:
if (has_connection) {
@@ -322,7 +334,7 @@ private:
}
void GetSystemEventReadableHandles(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "called");
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(ResultSuccess);
@@ -337,7 +349,10 @@ private:
}
void SetConnectionConfirmationOption(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto connection_option = rp.PopEnum<ConnectionConfirmationOption>();
LOG_INFO(Service_NIFM, "called, connection_option={}", static_cast<u32>(connection_option));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -633,7 +648,7 @@ void IGeneralService::CreateTemporaryNetworkProfile(HLERequestContext& ctx) {
}
void IGeneralService::GetCurrentIpConfigInfo(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "called");
struct IpConfigInfo {
IpAddressSetting ip_address_setting{};
@@ -814,6 +829,104 @@ void IGeneralService::SetWowlTcpKeepAliveTimeout(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
void IGeneralService::IsWiredConnectionAvailable(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called IsWiredConnectionAvailable [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(1); // Wired connection available
}
void IGeneralService::IsNetworkEmulationFeatureEnabled(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called IsNetworkEmulationFeatureEnabled [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(0); // Network emulation disabled
}
void IGeneralService::SelectActiveNetworkEmulationProfileIdForDebug(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called SelectActiveNetworkEmulationProfileIdForDebug [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IGeneralService::GetScanData2(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called GetScanData [19.0.0+]");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0); // No scan data
}
void IGeneralService::ResetActiveNetworkEmulationProfileId(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called ResetActiveNetworkEmulationProfileId [20.0.0+]");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IGeneralService::GetActiveNetworkEmulationProfileId(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called GetActiveNetworkEmulationProfileId [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0); // No active profile
}
void IGeneralService::IsRewriteFeatureEnabled(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called IsRewriteFeatureEnabled [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(0); // Rewrite feature disabled
}
void IGeneralService::CreateRewriteRule(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called CreateRewriteRule [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IGeneralService::DestroyRewriteRule(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called DestroyRewriteRule [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IGeneralService::IsActiveNetworkEmulationProfileIdSelected(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called IsActiveNetworkEmulationProfileIdSelected [20.0.0+]");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(0); // No profile selected
}
void IGeneralService::SelectDefaultNetworkEmulationProfileId(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called SelectDefaultNetworkEmulationProfileId [20.0.0+]");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IGeneralService::GetDefaultNetworkEmulationProfileId(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called GetDefaultNetworkEmulationProfileId [20.0.0+]");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0); // Default profile ID
}
void IGeneralService::GetNetworkEmulationProfile(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called GetNetworkEmulationProfile [20.0.0+]");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
IGeneralService::IGeneralService(Core::System& system_)
: ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
// clang-format off
@@ -860,6 +973,19 @@ IGeneralService::IGeneralService(Core::System& system_)
{41, &IGeneralService::GetAcceptableNetworkTypeFlag, "GetAcceptableNetworkTypeFlag"},
{42, &IGeneralService::NotifyConnectionStateChanged, "NotifyConnectionStateChanged"},
{43, &IGeneralService::SetWowlDelayedWakeTime, "SetWowlDelayedWakeTime"},
{44, &IGeneralService::IsWiredConnectionAvailable, "IsWiredConnectionAvailable"},
{45, &IGeneralService::IsNetworkEmulationFeatureEnabled, "IsNetworkEmulationFeatureEnabled"},
{46, &IGeneralService::SelectActiveNetworkEmulationProfileIdForDebug, "SelectActiveNetworkEmulationProfileIdForDebug"},
{47, &IGeneralService::GetScanData2, "GetScanData"},
{48, &IGeneralService::ResetActiveNetworkEmulationProfileId, "ResetActiveNetworkEmulationProfileId"},
{49, &IGeneralService::GetActiveNetworkEmulationProfileId, "GetActiveNetworkEmulationProfileId"},
{50, &IGeneralService::IsRewriteFeatureEnabled, "IsRewriteFeatureEnabled"},
{51, &IGeneralService::CreateRewriteRule, "CreateRewriteRule"},
{52, &IGeneralService::DestroyRewriteRule, "DestroyRewriteRule"},
{53, &IGeneralService::IsActiveNetworkEmulationProfileIdSelected, "IsActiveNetworkEmulationProfileIdSelected"},
{54, &IGeneralService::SelectDefaultNetworkEmulationProfileId, "SelectDefaultNetworkEmulationProfileId"},
{55, &IGeneralService::GetDefaultNetworkEmulationProfileId, "GetDefaultNetworkEmulationProfileId"},
{56, &IGeneralService::GetNetworkEmulationProfile, "GetNetworkEmulationProfile"},
{57, &IGeneralService::SetWowlTcpKeepAliveTimeout, "SetWowlTcpKeepAliveTimeout"},
};
// clang-format on

View File

@@ -48,6 +48,19 @@ private:
void GetAcceptableNetworkTypeFlag(HLERequestContext& ctx);
void NotifyConnectionStateChanged(HLERequestContext& ctx);
void SetWowlTcpKeepAliveTimeout(HLERequestContext& ctx);
void IsWiredConnectionAvailable(HLERequestContext& ctx);
void IsNetworkEmulationFeatureEnabled(HLERequestContext& ctx);
void SelectActiveNetworkEmulationProfileIdForDebug(HLERequestContext& ctx);
void GetScanData2(HLERequestContext& ctx);
void ResetActiveNetworkEmulationProfileId(HLERequestContext& ctx);
void GetActiveNetworkEmulationProfileId(HLERequestContext& ctx);
void IsRewriteFeatureEnabled(HLERequestContext& ctx);
void CreateRewriteRule(HLERequestContext& ctx);
void DestroyRewriteRule(HLERequestContext& ctx);
void IsActiveNetworkEmulationProfileIdSelected(HLERequestContext& ctx);
void SelectDefaultNetworkEmulationProfileId(HLERequestContext& ctx);
void GetDefaultNetworkEmulationProfileId(HLERequestContext& ctx);
void GetNetworkEmulationProfile(HLERequestContext& ctx);
Network::RoomNetwork& network;
};

View File

@@ -149,19 +149,45 @@ void BSD::SendToWork::Response(HLERequestContext& ctx) {
}
void BSD::RegisterClient(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestParser rp{ctx};
// Read LibraryConfigData structure
struct LibraryConfigData {
u32 version;
u32 tcp_tx_buf_size;
u32 tcp_rx_buf_size;
u32 tcp_tx_buf_max_size;
u32 tcp_rx_buf_max_size;
u32 udp_tx_buf_size;
u32 udp_rx_buf_size;
u32 sb_efficiency;
};
const auto config = rp.PopRaw<LibraryConfigData>();
const u64 transfer_memory_size = rp.Pop<u64>();
[[maybe_unused]] const auto transfer_memory_handle = ctx.GetCopyHandle(0);
const u64 pid = ctx.GetPID();
LOG_INFO(Service, "called, version={} pid={} transfer_memory_size={:#x}",
config.version, pid, transfer_memory_size);
LOG_DEBUG(Service, " TCP: tx={:#x} rx={:#x} tx_max={:#x} rx_max={:#x}",
config.tcp_tx_buf_size, config.tcp_rx_buf_size,
config.tcp_tx_buf_max_size, config.tcp_rx_buf_max_size);
LOG_DEBUG(Service, " UDP: tx={:#x} rx={:#x} sb_efficiency={}",
config.udp_tx_buf_size, config.udp_rx_buf_size, config.sb_efficiency);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<s32>(0); // bsd errno
}
void BSD::StartMonitoring(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
LOG_INFO(Service, "called");
// StartMonitoring initializes network event monitoring for BSD sockets
// This command has no documented input parameters in switchbrew
// It enables proper event handling for socket operations
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@@ -515,10 +541,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
FileDescriptor& descriptor = *file_descriptors[fd];
// ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={}", fd);
auto room_member = room_network.GetRoomMember().lock();
if (room_member && room_member->IsConnected()) {
const bool using_proxy = room_member && room_member->IsConnected();
LOG_INFO(Service, "New socket fd={} domain={} type={} protocol={} proxy={}",
fd, domain, type, protocol, using_proxy);
if (using_proxy) {
descriptor.socket = std::make_shared<Network::ProxySocket>(room_network);
} else {
descriptor.socket = std::make_shared<Network::Socket>();
@@ -632,23 +661,41 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
LOG_ERROR(Service, "Bind failed: Invalid fd={}", fd);
return Errno::BADF;
}
ASSERT(addr.size() == sizeof(SockAddrIn));
auto addr_in = GetValue<SockAddrIn>(addr);
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
LOG_INFO(Service, "Bind fd={} to {}:{}", fd, Network::IPv4AddressToString(addr_in.ip),
addr_in.portno);
const auto result = Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
if (result != Errno::SUCCESS) {
LOG_ERROR(Service, "Bind fd={} failed with errno={}", fd, static_cast<int>(result));
}
return result;
}
Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
LOG_ERROR(Service, "Connect failed: Invalid fd={}", fd);
return Errno::BADF;
}
UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
auto addr_in = GetValue<SockAddrIn>(addr);
return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
LOG_INFO(Service, "Connect fd={} to {}:{}", fd, Network::IPv4AddressToString(addr_in.ip),
addr_in.portno);
const auto result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
if (result != Errno::SUCCESS) {
LOG_ERROR(Service, "Connect fd={} failed with errno={}", fd, static_cast<int>(result));
} else {
LOG_INFO(Service, "Connect fd={} succeeded", fd);
}
return result;
}
Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
@@ -1031,8 +1078,11 @@ BSD::BSD(Core::System& system_, const char* name)
{33, &BSD::RegisterClientShared, "RegisterClientShared"},
{34, &BSD::GetSocketStatistics, "GetSocketStatistics"},
{35, &BSD::NifIoctl, "NifIoctl"},
{39, &BSD::Unknown39, "[20.0.0+] Unknown39"},
{40, &BSD::Unknown40, "[20.0.0+] Unknown40"},
{36, &BSD::Unknown36, "Unknown36"},
{37, &BSD::Unknown37, "Unknown37"},
{38, &BSD::Unknown38, "Unknown38"},
{39, &BSD::Unknown39, "Unknown39"},
{40, &BSD::Unknown40, "Unknown40"},
{200, &BSD::SetThreadCoreMask, "SetThreadCoreMask"},
{201, &BSD::GetThreadCoreMask, "GetThreadCoreMask"},
};
@@ -1289,7 +1339,7 @@ void BSD::SendMMsg(HLERequestContext& ctx) {
}
void BSD::SetThreadCoreMask(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called SetThreadCoreMask");
LOG_WARNING(Service, "(STUBBED) called SetThreadCoreMask [15.0.0+]");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(-1);
@@ -1312,6 +1362,30 @@ void BSD::SocketExempt(HLERequestContext& ctx) {
rb.PushEnum(static_cast<Errno>(EOPNOTSUPP));
}
void BSD::Unknown36(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called Unknown36 [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(-1);
rb.PushEnum(static_cast<Errno>(EOPNOTSUPP));
}
void BSD::Unknown37(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called Unknown37 [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(-1);
rb.PushEnum(static_cast<Errno>(EOPNOTSUPP));
}
void BSD::Unknown38(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called Unknown38 [18.0.0+]");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(-1);
rb.PushEnum(static_cast<Errno>(EOPNOTSUPP));
}
void BSD::Unknown39(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called Unknown39 [20.0.0+]");
IPC::ResponseBuilder rb{ctx, 4};

View File

@@ -162,11 +162,14 @@ private:
void RegisterResourceStatisticsName(HLERequestContext& ctx);
void RegisterClientShared(HLERequestContext& ctx);
void GetSocketStatistics(HLERequestContext& ctx);
void NifIoctl(HLERequestContext& ctx);
void Unknown39(HLERequestContext& ctx); // [20.0.0+] undocumented
void Unknown40(HLERequestContext& ctx); // [20.0.0+] undocumented
void SetThreadCoreMask(HLERequestContext& ctx);
void GetThreadCoreMask(HLERequestContext& ctx);
void NifIoctl(HLERequestContext& ctx); // [17.0.0+]
void Unknown36(HLERequestContext& ctx); // [18.0.0+] undocumented
void Unknown37(HLERequestContext& ctx); // [18.0.0+] undocumented
void Unknown38(HLERequestContext& ctx); // [18.0.0+] undocumented
void Unknown39(HLERequestContext& ctx); // [20.0.0+] undocumented
void Unknown40(HLERequestContext& ctx); // [20.0.0+] undocumented
void SetThreadCoreMask(HLERequestContext& ctx); // [15.0.0+]
void GetThreadCoreMask(HLERequestContext& ctx); // [15.0.0+]
template <typename Work>
void ExecuteWork(HLERequestContext& ctx, Work work);