Files
ryujinx/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
Aaron Robinson c76f65904d Enable full trimming
Enables full trimming for Ryujinx, and in doing so removes many usages of reflection, namely:

IUserService no longer uses reflection to find possible service types, and now has a generated switch based on name

Ryujinx.HLE.HOS.Tamper no longer uses dynamic to do operations, now using INumber<T> and friends

Cmif and Tipc commands in Ryujinx.HLE.HOS.Services no longer get resolved via reflection and are now done via generated virtual methods

Fix things broken by trimming (profile panel, DiscordRPC)
2026-03-18 14:50:17 -05:00

207 lines
7.6 KiB
C#

using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService;
using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types;
using System;
using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
{
partial class IGeneralService : DisposableIpcService
{
private readonly GeneralServiceDetail _generalServiceDetail;
private IPInterfaceProperties _targetPropertiesCache = null;
private UnicastIPAddressInformation _targetAddressInfoCache = null;
private string _cacheChosenInterface = null;
public IGeneralService(ServiceCtx context)
{
_generalServiceDetail = new GeneralServiceDetail
{
ClientId = GeneralServiceManager.Count,
IsAnyInternetRequestAccepted = !context.Device.DirtyHacks.IsEnabled(DirtyHack.NifmServiceDisableIsAnyInternetRequestAccepted), // NOTE: Why not accept any internet request?
};
NetworkChange.NetworkAddressChanged += LocalInterfaceCacheHandler;
GeneralServiceManager.Add(_generalServiceDetail);
}
[CommandCmif(1)]
// GetClientId() -> buffer<nn::nifm::ClientId, 0x1a, 4>
public ResultCode GetClientId(ServiceCtx context)
{
ulong position = context.Request.RecvListBuff[0].Position;
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(sizeof(int));
context.Memory.Write(position, _generalServiceDetail.ClientId);
return ResultCode.Success;
}
[CommandCmif(4)]
// CreateRequest(u32 version) -> object<nn::nifm::detail::IRequest>
public ResultCode CreateRequest(ServiceCtx context)
{
uint version = context.RequestData.ReadUInt32();
MakeObject(context, new IRequest(context.Device.System, version));
// Doesn't occur in our case.
// return ResultCode.ObjectIsNull;
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { version });
return ResultCode.Success;
}
[CommandCmif(5)]
// GetCurrentNetworkProfile() -> buffer<nn::nifm::detail::sf::NetworkProfileData, 0x1a, 0x17c>
public ResultCode GetCurrentNetworkProfile(ServiceCtx context)
{
ulong networkProfileDataPosition = context.Request.RecvListBuff[0].Position;
(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
if (interfaceProperties == null || unicastAddress == null)
{
return ResultCode.NoInternetConnection;
}
Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf<NetworkProfileData>());
NetworkProfileData networkProfile = new()
{
Uuid = Random.Shared.NextUInt128(),
};
networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress);
networkProfile.IpSettingData.DnsSetting = new DnsSetting(interfaceProperties);
"RyujinxNetwork"u8.CopyTo(networkProfile.Name.AsSpan());
context.Memory.Write(networkProfileDataPosition, networkProfile);
return ResultCode.Success;
}
[CommandCmif(12)]
// GetCurrentIpAddress() -> nn::nifm::IpV4Address
public ResultCode GetCurrentIpAddress(ServiceCtx context)
{
(_, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
if (unicastAddress == null)
{
return ResultCode.NoInternetConnection;
}
context.ResponseData.WriteStruct(new IpV4Address(unicastAddress.Address));
Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
return ResultCode.Success;
}
[CommandCmif(15)]
// GetCurrentIpConfigInfo() -> (nn::nifm::IpAddressSetting, nn::nifm::DnsSetting)
public ResultCode GetCurrentIpConfigInfo(ServiceCtx context)
{
(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
if (interfaceProperties == null || unicastAddress == null)
{
return ResultCode.NoInternetConnection;
}
Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
context.ResponseData.WriteStruct(new IpAddressSetting(interfaceProperties, unicastAddress));
context.ResponseData.WriteStruct(new DnsSetting(interfaceProperties));
return ResultCode.Success;
}
[CommandCmif(18)]
// GetInternetConnectionStatus() -> nn::nifm::detail::sf::InternetConnectionStatus
public ResultCode GetInternetConnectionStatus(ServiceCtx context)
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
return ResultCode.NoInternetConnection;
}
InternetConnectionStatus internetConnectionStatus = new()
{
Type = InternetConnectionType.WiFi,
WifiStrength = 3,
State = InternetConnectionState.Connected,
};
context.ResponseData.WriteStruct(internetConnectionStatus);
return ResultCode.Success;
}
[CommandCmif(21)]
// IsAnyInternetRequestAccepted(buffer<nn::nifm::ClientId, 0x19, 4>) -> bool
public ResultCode IsAnyInternetRequestAccepted(ServiceCtx context)
{
ulong position = context.Request.PtrBuff[0].Position;
#pragma warning disable IDE0059 // Remove unnecessary value assignment
ulong size = context.Request.PtrBuff[0].Size;
#pragma warning restore IDE0059
int clientId = context.Memory.Read<int>(position);
context.ResponseData.Write(GeneralServiceManager.Get(clientId).IsAnyInternetRequestAccepted);
return ResultCode.Success;
}
private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(ServiceCtx context)
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
return (null, null);
}
string chosenInterface = context.Device.Configuration.MultiplayerLanInterfaceId;
if (_targetPropertiesCache == null || _targetAddressInfoCache == null || _cacheChosenInterface != chosenInterface)
{
_cacheChosenInterface = chosenInterface;
(_targetPropertiesCache, _targetAddressInfoCache) = NetworkHelpers.GetLocalInterface(chosenInterface);
}
return (_targetPropertiesCache, _targetAddressInfoCache);
}
private void LocalInterfaceCacheHandler(object sender, EventArgs e)
{
Logger.Info?.Print(LogClass.ServiceNifm, "NetworkAddress changed, invalidating cached data.");
_targetPropertiesCache = null;
_targetAddressInfoCache = null;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
}
}
}
}