IGeneralService.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using Ryujinx.Common;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Common.Utilities;
  4. using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService;
  5. using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types;
  6. using System;
  7. using System.Net.NetworkInformation;
  8. using System.Runtime.CompilerServices;
  9. namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
  10. {
  11. class IGeneralService : DisposableIpcService
  12. {
  13. private GeneralServiceDetail _generalServiceDetail;
  14. private IPInterfaceProperties _targetPropertiesCache = null;
  15. private UnicastIPAddressInformation _targetAddressInfoCache = null;
  16. private string _cacheChosenInterface = null;
  17. public IGeneralService()
  18. {
  19. _generalServiceDetail = new GeneralServiceDetail
  20. {
  21. ClientId = GeneralServiceManager.Count,
  22. IsAnyInternetRequestAccepted = true // NOTE: Why not accept any internet request?
  23. };
  24. NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(LocalInterfaceCacheHandler);
  25. GeneralServiceManager.Add(_generalServiceDetail);
  26. }
  27. [CommandCmif(1)]
  28. // GetClientId() -> buffer<nn::nifm::ClientId, 0x1a, 4>
  29. public ResultCode GetClientId(ServiceCtx context)
  30. {
  31. ulong position = context.Request.RecvListBuff[0].Position;
  32. context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(sizeof(int));
  33. context.Memory.Write(position, _generalServiceDetail.ClientId);
  34. return ResultCode.Success;
  35. }
  36. [CommandCmif(4)]
  37. // CreateRequest(u32 version) -> object<nn::nifm::detail::IRequest>
  38. public ResultCode CreateRequest(ServiceCtx context)
  39. {
  40. uint version = context.RequestData.ReadUInt32();
  41. MakeObject(context, new IRequest(context.Device.System, version));
  42. // Doesn't occur in our case.
  43. // return ResultCode.ObjectIsNull;
  44. Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { version });
  45. return ResultCode.Success;
  46. }
  47. [CommandCmif(5)]
  48. // GetCurrentNetworkProfile() -> buffer<nn::nifm::detail::sf::NetworkProfileData, 0x1a, 0x17c>
  49. public ResultCode GetCurrentNetworkProfile(ServiceCtx context)
  50. {
  51. ulong networkProfileDataPosition = context.Request.RecvListBuff[0].Position;
  52. (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
  53. if (interfaceProperties == null || unicastAddress == null)
  54. {
  55. return ResultCode.NoInternetConnection;
  56. }
  57. Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
  58. context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf<NetworkProfileData>());
  59. NetworkProfileData networkProfile = new NetworkProfileData
  60. {
  61. Uuid = UInt128Utils.CreateRandom()
  62. };
  63. networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress);
  64. networkProfile.IpSettingData.DnsSetting = new DnsSetting(interfaceProperties);
  65. "RyujinxNetwork"u8.CopyTo(networkProfile.Name.AsSpan());
  66. context.Memory.Write(networkProfileDataPosition, networkProfile);
  67. return ResultCode.Success;
  68. }
  69. [CommandCmif(12)]
  70. // GetCurrentIpAddress() -> nn::nifm::IpV4Address
  71. public ResultCode GetCurrentIpAddress(ServiceCtx context)
  72. {
  73. (_, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
  74. if (unicastAddress == null)
  75. {
  76. return ResultCode.NoInternetConnection;
  77. }
  78. context.ResponseData.WriteStruct(new IpV4Address(unicastAddress.Address));
  79. Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
  80. return ResultCode.Success;
  81. }
  82. [CommandCmif(15)]
  83. // GetCurrentIpConfigInfo() -> (nn::nifm::IpAddressSetting, nn::nifm::DnsSetting)
  84. public ResultCode GetCurrentIpConfigInfo(ServiceCtx context)
  85. {
  86. (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
  87. if (interfaceProperties == null || unicastAddress == null)
  88. {
  89. return ResultCode.NoInternetConnection;
  90. }
  91. Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
  92. context.ResponseData.WriteStruct(new IpAddressSetting(interfaceProperties, unicastAddress));
  93. context.ResponseData.WriteStruct(new DnsSetting(interfaceProperties));
  94. return ResultCode.Success;
  95. }
  96. [CommandCmif(18)]
  97. // GetInternetConnectionStatus() -> nn::nifm::detail::sf::InternetConnectionStatus
  98. public ResultCode GetInternetConnectionStatus(ServiceCtx context)
  99. {
  100. if (!NetworkInterface.GetIsNetworkAvailable())
  101. {
  102. return ResultCode.NoInternetConnection;
  103. }
  104. InternetConnectionStatus internetConnectionStatus = new InternetConnectionStatus
  105. {
  106. Type = InternetConnectionType.WiFi,
  107. WifiStrength = 3,
  108. State = InternetConnectionState.Connected,
  109. };
  110. context.ResponseData.WriteStruct(internetConnectionStatus);
  111. return ResultCode.Success;
  112. }
  113. [CommandCmif(21)]
  114. // IsAnyInternetRequestAccepted(buffer<nn::nifm::ClientId, 0x19, 4>) -> bool
  115. public ResultCode IsAnyInternetRequestAccepted(ServiceCtx context)
  116. {
  117. ulong position = context.Request.PtrBuff[0].Position;
  118. ulong size = context.Request.PtrBuff[0].Size;
  119. int clientId = context.Memory.Read<int>(position);
  120. context.ResponseData.Write(GeneralServiceManager.Get(clientId).IsAnyInternetRequestAccepted);
  121. return ResultCode.Success;
  122. }
  123. private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(ServiceCtx context)
  124. {
  125. if (!NetworkInterface.GetIsNetworkAvailable())
  126. {
  127. return (null, null);
  128. }
  129. string chosenInterface = context.Device.Configuration.MultiplayerLanInterfaceId;
  130. if (_targetPropertiesCache == null || _targetAddressInfoCache == null || _cacheChosenInterface != chosenInterface)
  131. {
  132. _cacheChosenInterface = chosenInterface;
  133. (_targetPropertiesCache, _targetAddressInfoCache) = NetworkHelpers.GetLocalInterface(chosenInterface);
  134. }
  135. return (_targetPropertiesCache, _targetAddressInfoCache);
  136. }
  137. private void LocalInterfaceCacheHandler(object sender, EventArgs e)
  138. {
  139. Logger.Info?.Print(LogClass.ServiceNifm, $"NetworkAddress changed, invalidating cached data.");
  140. _targetPropertiesCache = null;
  141. _targetAddressInfoCache = null;
  142. }
  143. protected override void Dispose(bool isDisposing)
  144. {
  145. if (isDisposing)
  146. {
  147. NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
  148. GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
  149. }
  150. }
  151. }
  152. }