Ryujinx/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
TSRBerry 69b6ef7a4a
[GUI] Add network interface dropdown (#4597)
* Add network adapter dropdown from LDN build

* Ava: Add NetworkInterfaces to SettingsNetworkTab

* Add headless network interface option

* Add network interface dropdown to Avalonia

* Fix handling network interfaces without a gateway address

* gtk: Actually save selected network interface to config

* Increment config version
2023-04-16 15:25:20 +00:00

203 lines
No EOL
7.5 KiB
C#

using Ryujinx.Common;
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
{
class IGeneralService : DisposableIpcService
{
private GeneralServiceDetail _generalServiceDetail;
private IPInterfaceProperties _targetPropertiesCache = null;
private UnicastIPAddressInformation _targetAddressInfoCache = null;
private string _cacheChosenInterface = null;
public IGeneralService()
{
_generalServiceDetail = new GeneralServiceDetail
{
ClientId = GeneralServiceManager.Count,
IsAnyInternetRequestAccepted = true // NOTE: Why not accept any internet request?
};
NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(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 NetworkProfileData
{
Uuid = UInt128Utils.CreateRandom()
};
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 InternetConnectionStatus
{
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;
ulong size = context.Request.PtrBuff[0].Size;
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);
}
}
}
}