From 50926d6a2fc111617c0f356ed7518857861e9ad6 Mon Sep 17 00:00:00 2001 From: Jacobwasbeast Date: Sat, 8 Feb 2025 21:10:51 -0600 Subject: [PATCH] Implement QLaunch Co-authored-by: Alula <6276139+alula@users.noreply.github.com> --- src/Ryujinx.Common/Logging/LogClass.cs | 6 +- src/Ryujinx.Common/Utilities/UInt128Utils.cs | 5 + src/Ryujinx.HLE/HLEConfiguration.cs | 11 + .../Acc/AccountService/IAdministrator.cs | 32 ++ .../IManagerForSystemService.cs | 10 + .../Acc/AccountService/IProfileEditor.cs | 10 + .../Acc/IAccountServiceForAdministrator.cs | 19 + .../ISystemProcessCommonFunctions.cs | 7 + .../SystemAppletProxy/ICommonStateGetter.cs | 14 + .../ICradleFirmwareUpdater.cs | 31 ++ .../IGlobalStateController.cs | 8 + .../SystemAppletProxy/IHomeMenuFunctions.cs | 30 ++ .../SystemAppletProxy/ISelfController.cs | 16 + .../IAllSystemAppletProxiesService.cs | 9 + .../HOS/Services/Am/AppletAE/ILockAccessor.cs | 94 +++++ .../HOS/Services/Audio/IAudioController.cs | 191 ++++++++++ .../BtmSystem/IBtmSystemCore.cs | 81 +++++ .../Services/BluetoothManager/IBtmSystem.cs | 20 ++ src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs | 12 + .../HOS/Services/Hid/ButtonDevice.cs | 80 +++++ src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs | 8 + .../HOS/Services/Hid/IHidSystemServer.cs | 335 +++++++++++++++++- .../Types/SharedMemory/Button/ButtonState.cs | 12 + .../Hid/Types/SharedMemory/SharedMemory.cs | 19 + src/Ryujinx.HLE/HOS/Services/IpcService.cs | 50 +++ .../HOS/Services/Ldn/IMonitorService.cs | 38 ++ .../Services/Ldn/IMonitorServiceCreator.cs | 9 + .../Ldn/Lp2p/ISfMonitorServiceCreator.cs | 22 ++ .../Services/Ldn/Lp2p/ISfServiceMonitor.cs | 5 +- .../ISfMonitorService.cs | 27 ++ .../HOS/Services/Ldn/Types/GroupInfo.cs | 10 + .../Services/News/INewlyArrivedEventHolder.cs | 39 ++ .../HOS/Services/News/INewsDataService.cs | 7 + .../HOS/Services/News/INewsDatabaseService.cs | 36 ++ .../HOS/Services/News/INewsService.cs | 30 ++ .../Services/News/IOverwriteEventHolder.cs | 40 +++ .../HOS/Services/News/IServiceCreator.cs | 41 +++ .../Nifm/StaticService/IGeneralService.cs | 177 ++++++++- .../Nifm/StaticService/INetworkProfile.cs | 61 ++++ .../Nifm/StaticService/IScanRequest.cs | 66 ++++ .../INotificationServicesForSystem.cs | 31 ++ .../INotificationSystemEventAccessor.cs | 36 ++ .../HOS/Services/Npns/INpnsSystem.cs | 45 ++- .../Ns/IApplicationManagerInterface.cs | 328 ++++++++++++++++- .../Services/Ns/IDynamicRightsInterface.cs | 7 + ...ReadOnlyApplicationControlDataInterface.cs | 29 +- .../Services/Ns/IServiceGetterInterface.cs | 20 +- .../HOS/Services/Ns/ISystemUpdateInterface.cs | 44 ++- .../Services/Ns/Types/ApplicationRecord.cs | 21 ++ .../Ns/Types/ApplicationRecordType.cs | 11 + .../HOS/Services/Ns/Types/ApplicationView.cs | 15 + .../Types/ApplicationViewWithPromotionInfo.cs | 11 + .../Ns/Types/BackgroundNetworkUpdateState.cs | 9 + .../HOS/Services/Ns/Types/PromotionInfo.cs | 26 ++ .../Services/Ns/Types/RyuApplicationData.cs | 22 ++ .../HOS/Services/Olsc/IDaemonController.cs | 7 + .../Olsc/IOlscServiceForSystemService.cs | 38 ++ .../INativeHandleHolder.cs | 35 ++ .../ITransferTaskListController.cs | 31 ++ .../IParentalControlService.cs | 188 ++++++++++ .../Settings/ISystemSettingsServer.cs | 250 ++++++++++++- .../Types/AccountNotificationSettings.cs | 19 + .../Settings/Types/AccountSettings.cs | 10 + .../HOS/Services/Settings/Types/CmuMode.cs | 10 + .../Settings/Types/ConsoleSleepPlan.cs | 12 + .../Types/ErrorReportSharePermission.cs | 9 + .../Services/Settings/Types/EulaVersion.cs | 16 + .../Settings/Types/HandheldSleepPlan.cs | 12 + .../Settings/Types/HdmiContentType.cs | 11 + .../Settings/Types/InitialLaunchFlag.cs | 13 + .../Settings/Types/InitialLaunchSettings.cs | 14 + .../Settings/Types/NotificationFlag.cs | 14 + .../Settings/Types/NotificationSettings.cs | 13 + .../Settings/Types/NotificationTime.cs | 11 + .../Settings/Types/NotificationVolume.cs | 11 + .../Settings/Types/PrimaryAlbumStorage.cs | 8 + .../HOS/Services/Settings/Types/RgbRange.cs | 9 + .../Services/Settings/Types/SleepSettings.cs | 12 + .../HOS/Services/Settings/Types/TvFlag.cs | 14 + .../Services/Settings/Types/TvResolution.cs | 10 + .../HOS/Services/Settings/Types/TvSettings.cs | 17 + .../Settings/Types/UserSelectorFlag.cs | 12 + .../Settings/Types/UserSelectorSettings.cs | 10 + .../ISystemDisplayService.cs | 9 + .../RootService/IApplicationDisplayService.cs | 2 +- src/Ryujinx.HLE/UI/IHostUIHandler.cs | 5 + .../Bcat/Ipc/ServiceCreator/BcatService.cs | 9 + src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs | 10 + src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs | 10 + src/Ryujinx.Horizon/Ovln/Ipc/SenderService.cs | 13 + src/Ryujinx.Horizon/Sdk/Ovln/ISender.cs | 9 + src/Ryujinx/AppHost.cs | 13 + src/Ryujinx/Assets/locales.json | 50 +++ src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 3 + src/Ryujinx/Headless/Windows/WindowBase.cs | 5 + src/Ryujinx/UI/Applet/AvaHostUIHandler.cs | 5 + .../UI/Views/Main/MainMenuBarView.axaml | 5 + .../UI/Views/Main/MainMenuBarView.axaml.cs | 11 + 98 files changed, 3321 insertions(+), 27 deletions(-) create mode 100644 src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IAdministrator.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemProcessCommonFunctions.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICradleFirmwareUpdater.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Am/AppletAE/ILockAccessor.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmSystem/IBtmSystemCore.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Hid/ButtonDevice.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Button/ButtonState.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfMonitorServiceCreator.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/SfMonitorServiceCreator/ISfMonitorService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ldn/Types/GroupInfo.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/News/INewlyArrivedEventHolder.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/News/INewsDataService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/News/INewsDatabaseService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/News/INewsService.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/News/IOverwriteEventHolder.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/INetworkProfile.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IScanRequest.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Notification/INotificationSystemEventAccessor.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ns/IDynamicRightsInterface.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecord.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecordType.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationView.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationViewWithPromotionInfo.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ns/Types/BackgroundNetworkUpdateState.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ns/Types/PromotionInfo.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Ns/Types/RyuApplicationData.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Olsc/IDaemonController.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Olsc/OlscServiceForSystemService/INativeHandleHolder.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Olsc/OlscServiceForSystemService/ITransferTaskListController.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/AccountNotificationSettings.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/AccountSettings.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/CmuMode.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/ConsoleSleepPlan.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/ErrorReportSharePermission.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/EulaVersion.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/HandheldSleepPlan.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/HdmiContentType.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/InitialLaunchFlag.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/InitialLaunchSettings.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationFlag.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationSettings.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationTime.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationVolume.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/PrimaryAlbumStorage.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/RgbRange.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/SleepSettings.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/TvFlag.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/TvResolution.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/TvSettings.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/UserSelectorFlag.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Settings/Types/UserSelectorSettings.cs create mode 100644 src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Ovln/ISender.cs diff --git a/src/Ryujinx.Common/Logging/LogClass.cs b/src/Ryujinx.Common/Logging/LogClass.cs index a4117580e..744f12548 100644 --- a/src/Ryujinx.Common/Logging/LogClass.cs +++ b/src/Ryujinx.Common/Logging/LogClass.cs @@ -39,6 +39,7 @@ namespace Ryujinx.Common.Logging ServiceFs, ServiceHid, ServiceIrs, + ServiceLbl, ServiceLdn, ServiceLdr, ServiceLm, @@ -50,11 +51,14 @@ namespace Ryujinx.Common.Logging ServiceNgct, ServiceNifm, ServiceNim, + ServiceNotif, ServiceNs, ServiceNsd, ServiceNtc, ServiceNv, + ServiceNpns, ServiceOlsc, + ServiceOvln, ServicePctl, ServicePcv, ServicePl, @@ -72,6 +76,6 @@ namespace Ryujinx.Common.Logging TamperMachine, UI, Vic, - XCIFileTrimmer + XCIFileTrimmer, } } diff --git a/src/Ryujinx.Common/Utilities/UInt128Utils.cs b/src/Ryujinx.Common/Utilities/UInt128Utils.cs index 23afaa3a1..e9f581b9f 100644 --- a/src/Ryujinx.Common/Utilities/UInt128Utils.cs +++ b/src/Ryujinx.Common/Utilities/UInt128Utils.cs @@ -16,5 +16,10 @@ namespace Ryujinx.Common.Utilities public static UInt128 NextUInt128(this Random rand) => new((ulong)rand.NextInt64(), (ulong)rand.NextInt64()); + + public static UInt128 CreateRandom() + { + return new UInt128((ulong)Random.Shared.NextInt64(), (ulong)Random.Shared.NextInt64()); + } } } diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index 0b7ae3974..2ec1d03f5 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -6,9 +6,12 @@ using Ryujinx.Graphics.GAL; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.Services.Ns.Types; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.UI; using System; +using System.Collections.Generic; +using System.Collections.Immutable; namespace Ryujinx.HLE { @@ -195,10 +198,17 @@ namespace Ryujinx.HLE /// This cannot be changed after instantiation. public EnabledDirtyHack[] Hacks { internal get; set; } + /// + /// The list of title ids found byApplicationLibrary. + /// + /// This cannot be changed after instantiation. + internal readonly IImmutableList Titles; + public HLEConfiguration(VirtualFileSystem virtualFileSystem, LibHacHorizonManager libHacHorizonManager, ContentManager contentManager, AccountManager accountManager, + IImmutableList titles, UserChannelPersistence userChannelPersistence, IRenderer gpuRenderer, IHardwareDeviceDriver audioDeviceDriver, @@ -231,6 +241,7 @@ namespace Ryujinx.HLE LibHacHorizonManager = libHacHorizonManager; AccountManager = accountManager; ContentManager = contentManager; + Titles = titles; UserChannelPersistence = userChannelPersistence; GpuRenderer = gpuRenderer; AudioDeviceDriver = audioDeviceDriver; diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IAdministrator.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IAdministrator.cs new file mode 100644 index 000000000..9f2e29710 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IAdministrator.cs @@ -0,0 +1,32 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService +{ + class IAdministrator : IpcService + { + public IAdministrator(UserId userId) + { + + } + + [CommandCmif(0)] + // CheckAvailability() + public ResultCode CheckAvailability(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + + [CommandCmif(250)] + // IsLinkedWithNintendoAccount() -> bool + public ResultCode IsLinkedWithNintendoAccount(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs index 8510837b2..07ef2eb6e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs @@ -1,3 +1,5 @@ +using Ryujinx.Common.Logging; + namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { class IManagerForSystemService : IpcService @@ -43,5 +45,13 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { return _managerServer.LoadIdTokenCache(context); } + + [CommandCmif(143)] + // GetNetworkServiceLicenseCacheEx() + public ResultCode GetNetworkServiceLicenseCacheEx(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs index 5d5d0dd69..2ad350029 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs @@ -1,3 +1,5 @@ +using Ryujinx.Common.Logging; + namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { class IProfileEditor : IpcService @@ -36,6 +38,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { return _profileServer.LoadImage(context); } + + [CommandCmif(30)] + // GetImageId() + public ResultCode GetImageId(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + return ResultCode.Success; + } [CommandCmif(100)] // Store(nn::account::profile::ProfileBase, buffer) diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs index 74c135aed..bb959fbf8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs @@ -125,5 +125,24 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } + + [CommandCmif(250)] + // GetBaasAccountAdministrator(nn::account::Uid) -> object + public ResultCode GetBaasAccountAdministrator(ServiceCtx context) + { + ResultCode resultCode = _applicationServiceServer.CheckUserId(context, out UserId userId); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + MakeObject(context, new IAdministrator(userId)); + + // Doesn't occur in our case. + // return ResultCode.NullObject; + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemProcessCommonFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemProcessCommonFunctions.cs new file mode 100644 index 000000000..637a50da7 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemProcessCommonFunctions.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService +{ + class ISystemProcessCommonFunctions : IpcService + { + + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs index 3bb703724..84c2f5cca 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs @@ -155,6 +155,20 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + + [CommandCmif(31)] + [CommandCmif(32)] + // GetReaderLockAccessorEx(u32) -> object + public ResultCode GetReaderLockAccessorEx(ServiceCtx context) + { + int lockId = context.RequestData.ReadInt32(); + + MakeObject(context, new ILockAccessor(lockId, context.Device.System)); + + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } [CommandCmif(50)] // 3.0.0+ // IsVrModeEnabled() -> b8 diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICradleFirmwareUpdater.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICradleFirmwareUpdater.cs new file mode 100644 index 000000000..27a3fce70 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICradleFirmwareUpdater.cs @@ -0,0 +1,31 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy +{ + class ICradleFirmwareUpdater : IpcService + { + [CommandCmif(1)] + // Finish() + public ResultCode Finish(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetUpdateDeviceStatus() + public ResultCode GetUpdateDeviceStatus(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + return ResultCode.Success; + } + + [CommandCmif(3)] + // GetUpdateProgress() + public ResultCode GetUpdateProgress(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs index a26140871..6cf0e6819 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IGlobalStateController.cs @@ -42,5 +42,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys Logger.Stub?.PrintStub(LogClass.ServiceAm); return ResultCode.Success; } + + [CommandCmif(30)] + // OpenCradleFirmwareUpdater() -> ICradleFirmwareUpdater. + public ResultCode OpenCradleFirmwareUpdater(ServiceCtx context) + { + MakeObject(context, new ICradleFirmwareUpdater()); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs index 78f47e0e9..fdac7fca3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs @@ -26,6 +26,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandCmif(11)] + // LockForeground() + public ResultCode LockForeground(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + [CommandCmif(12)] + // UnlockForeground() + public ResultCode UnlockForeground(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + [CommandCmif(21)] // GetPopFromGeneralChannelEvent() -> handle public ResultCode GetPopFromGeneralChannelEvent(ServiceCtx context) @@ -44,5 +61,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + + [CommandCmif(31)] + // GetWriterLockAccessorEx(i32) -> object + public ResultCode GetWriterLockAccessorEx(ServiceCtx context) + { + int lockId = context.RequestData.ReadInt32(); + + MakeObject(context, new ILockAccessor(lockId, context.Device.System)); + + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 71b4b482a..2250867c8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -276,6 +276,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandCmif(60)] + // OverrideAutoSleepTimeAndDimmingTime() + public ResultCode OverrideAutoSleepTimeAndDimmingTime(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + return ResultCode.Success; + } + [CommandCmif(62)] // SetIdleTimeDetectionExtension(u32) public ResultCode SetIdleTimeDetectionExtension(ServiceCtx context) @@ -356,6 +364,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandCmif(72)] + // SetInputDetectionPolicy() + public ResultCode SetInputDetectionPolicy(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + return ResultCode.Success; + } + [CommandCmif(80)] // 4.0.0+ // SetWirelessPriorityMode(s32 wireless_priority_mode) public ResultCode SetWirelessPriorityMode(ServiceCtx context) diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs index b8741b22b..df0d72deb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs @@ -35,5 +35,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } + + [CommandCmif(450)] + // ISystemProcessCommonFunctions() -> object + public ResultCode ISystemProcessCommonFunctions(ServiceCtx context) + { + MakeObject(context, new ISystemProcessCommonFunctions()); + return ResultCode.Success; + } + } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/ILockAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/ILockAccessor.cs new file mode 100644 index 000000000..dcf426213 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/ILockAccessor.cs @@ -0,0 +1,94 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + class ILockAccessor : IpcService + { + private readonly int _lockId; + private bool _isLocked; + private KEvent _lockEvent; + private int _lockEventHandle; + + public ILockAccessor(int lockId, Horizon system) + { + _lockId = lockId; + _isLocked = false; + _lockEvent = new KEvent(system.KernelContext); + _lockEvent.ReadableEvent.Signal(); + } + + [CommandCmif(1)] + // TryLock(bool) -> (bool, IpcService) + public ResultCode TryLock(ServiceCtx context) + { + bool returnHandle = context.RequestData.ReadBoolean(); + + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + if (_lockEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_lockEvent.ReadableEvent, out _lockEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + _isLocked = true; + _lockEvent.ReadableEvent.Signal(); + + context.ResponseData.Write(_isLocked); + if (returnHandle) + { + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_lockEventHandle); + } + + return ResultCode.Success; + } + + [CommandCmif(2)] + // Unlock() + public ResultCode Unlock(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + _isLocked = false; + _lockEvent.ReadableEvent.Signal(); + + return ResultCode.Success; + } + + [CommandCmif(3)] + // GetEvent() -> handle + public ResultCode GetEvent(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + if (_lockEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_lockEvent.ReadableEvent, out _lockEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_lockEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(4)] + // IsLocked() -> bool + public ResultCode IsLocked(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + context.ResponseData.Write(_isLocked); + + return ResultCode.Success; + } + } +}; diff --git a/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs new file mode 100644 index 000000000..89dfad2ac --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs @@ -0,0 +1,191 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Audio +{ + [Service("audctl")] + class IAudioController : IpcService + { + public IAudioController(ServiceCtx context) { } + + [CommandCmif(9)] + // GetAudioOutputMode(s32) -> s32 + public ResultCode GetAudioOutputMode(ServiceCtx context) + { + context.ResponseData.Write(0); // todo? + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10)] + // SetAudioOutputMode(s32, s32) + public ResultCode SetAudioOutputMode(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(11)] + // SetForceMutePolicy(u32) + public ResultCode SetForceMutePolicy(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(12)] + // GetForceMutePolicy() -> u32 + public ResultCode GetForceMutePolicy(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(13)] + // GetOutputModeSetting(u32) -> u32 + public ResultCode GetOutputModeSetting(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(18)] // 3.0.0+ + // GetHeadphoneOutputLevelMode() -> u32 + public ResultCode GetHeadphoneOutputLevelMode(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(31)] // 13.0.0+ + // IsSpeakerAutoMuteEnabled() -> b8 + public ResultCode IsSpeakerAutoMuteEnabled(ServiceCtx context) + { + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(5000)] + // Unknown5000() -> bool // 19.0.0+ + public ResultCode Unknown5000(ServiceCtx context) + { + MakeObject(context, new IAudioController(context)); + + return ResultCode.Success; + } + + [CommandCmif(10000)] + // NotifyAudioOutputTargetForPlayReport() + public ResultCode NotifyAudioOutputTargetForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10001)] + // NotifyAudioOutputChannelCountForPlayReport() + public ResultCode NotifyAudioOutputChannelCountForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10002)] + // NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport() + public ResultCode NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10100)] + // GetAudioVolumeDataForPlayReport() + public ResultCode GetAudioVolumeDataForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10101)] + // BindAudioVolumeUpdateEventForPlayReport() + public ResultCode BindAudioVolumeUpdateEventForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10102)] + // BindAudioOutputTargetUpdateEventForPlayReport() + public ResultCode BindAudioOutputTargetUpdateEventForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10103)] + // GetAudioOutputTargetForPlayReport() + public ResultCode GetAudioOutputTargetForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10104)] + // GetAudioOutputChannelCountForPlayReport() + public ResultCode GetAudioOutputChannelCountForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10105)] + // BindAudioOutputChannelCountUpdateEventForPlayReport() + public ResultCode BindAudioOutputChannelCountUpdateEventForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(10106)] + // GetDefaultAudioOutputTargetForPlayReport() + public ResultCode GetDefaultAudioOutputTargetForPlayReport(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + + [CommandCmif(50000)] + // SetAnalogInputBoostGainForPrototyping() + public ResultCode SetAnalogInputBoostGainForPrototyping(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAudio); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmSystem/IBtmSystemCore.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmSystem/IBtmSystemCore.cs new file mode 100644 index 000000000..304cfe03b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/BtmSystem/IBtmSystemCore.cs @@ -0,0 +1,81 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; + +namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmSystem +{ + class IBtmSystemCore : IpcService + { + public KEvent _radioEvent; + public int _radioEventhandle; + + public KEvent _gamepadPairingEvent; + public int _gamepadPairingEventHandle; + + public IBtmSystemCore() { } + + [CommandCmif(6)] + // IsRadioEnabled() -> b8 + public ResultCode IsRadioEnabled(ServiceCtx context) + { + context.ResponseData.Write(true); + + Logger.Stub?.PrintStub(LogClass.ServiceBtm); + + return ResultCode.Success; + } + + [CommandCmif(7)] // 3.0.0+ + // AcquireRadioEvent() -> (byte<1>, handle) + public ResultCode AcquireRadioEvent(ServiceCtx context) + { + Result result = Result.Success; + + if (_radioEventhandle == 0) + { + _radioEvent = new KEvent(context.Device.System.KernelContext); + + result = context.Process.HandleTable.GenerateHandle(_radioEvent.ReadableEvent, out _radioEventhandle); + + if (result != Result.Success) + { + // NOTE: We use a Logging instead of an exception because the call return a boolean if succeed or not. + Logger.Error?.Print(LogClass.ServiceBsd, "Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_radioEventhandle); + + context.ResponseData.Write(result == Result.Success ? 1 : 0); + + return ResultCode.Success; + } + + [CommandCmif(8)] // 3.0.0+ + // AcquireGamepadPairingEvent() -> (byte<1>, handle) + public ResultCode AcquireGamepadPairingEvent(ServiceCtx context) + { + Result result = Result.Success; + + if (_gamepadPairingEventHandle == 0) + { + _gamepadPairingEvent = new KEvent(context.Device.System.KernelContext); + + result = context.Process.HandleTable.GenerateHandle(_gamepadPairingEvent.ReadableEvent, out _gamepadPairingEventHandle); + + if (result != Result.Success) + { + // NOTE: We use a Logging instead of an exception because the call return a boolean if succeed or not. + Logger.Error?.Print(LogClass.ServiceBsd, "Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gamepadPairingEventHandle); + + context.ResponseData.Write(result == Result.Success ? 1 : 0); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs index 0efab8763..d1b389c59 100644 --- a/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/BluetoothManager/IBtmSystem.cs @@ -1,8 +1,28 @@ +using Ryujinx.HLE.HOS.Services.BluetoothManager.BtmSystem; + namespace Ryujinx.HLE.HOS.Services.BluetoothManager { [Service("btm:sys")] class IBtmSystem : IpcService { public IBtmSystem(ServiceCtx context) { } + + [CommandCmif(0)] + // GetCoreImpl() -> object + public ResultCode GetCoreImpl(ServiceCtx context) + { + MakeObject(context, new IBtmSystemCore()); + + return ResultCode.Success; + } + + [CommandCmif(6)] + // IsRadioEnabled() -> b8 + public ResultCode IsRadioEnabled(ServiceCtx context) + { + context.ResponseData.Write(true); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs b/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs index bd0880db7..abf000411 100644 --- a/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Erpt/IContext.cs @@ -1,8 +1,20 @@ +using Ryujinx.Common.Logging; + namespace Ryujinx.HLE.HOS.Services.Erpt { [Service("erpt:c")] class IContext : IpcService { public IContext(ServiceCtx context) { } + + [CommandCmif(0)] + // SubmitContext(buffer, buffer) -> u32 + public ResultCode SubmitContext(ServiceCtx context) + { + Logger.Info?.PrintStub(LogClass.Service, $"ContextEntry size: {context.Request.SendBuff[0].Size}"); + Logger.Info?.PrintStub(LogClass.Service, $"FieldList size: {context.Request.SendBuff[1].Size}"); + context.ResponseData.Write(0); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/ButtonDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/ButtonDevice.cs new file mode 100644 index 000000000..1e555192b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/ButtonDevice.cs @@ -0,0 +1,80 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Button; +using System; +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public enum ButtonDeviceType + { + HomeButton, + SleepButton, + CaptureButton, + } + + public class ButtonDevice : BaseDevice + { + private ButtonDeviceType _type; + private KEvent _event; + private bool isDown; + + + public ButtonDevice(Switch device, bool active, ButtonDeviceType type) : base(device, active) + { + _type = type; + _event = new KEvent(device.System.KernelContext); + } + + internal ref KEvent GetEvent() + { + return ref _event; + } + + private ref RingLifo GetButtonStateLifo() + { + switch (_type) + { + case ButtonDeviceType.HomeButton: + return ref _device.Hid.SharedMemory.HomeButton; + case ButtonDeviceType.SleepButton: + return ref _device.Hid.SharedMemory.SleepButton; + case ButtonDeviceType.CaptureButton: + return ref _device.Hid.SharedMemory.CaptureButton; + default: + throw new ArgumentOutOfRangeException(nameof(_type)); + } + } + + public void Update(bool state) + { + ref RingLifo lifo = ref GetButtonStateLifo(); + + if (!Active) + { + lifo.Clear(); + + return; + } + + bool shouldSignal = state != isDown; + isDown = state; + + if (!shouldSignal) + { + return; + } + + ref ButtonState previousEntry = ref lifo.GetCurrentEntryRef(); + + ButtonState newState = new() + { + SamplingNumber = previousEntry.SamplingNumber + 1, + Buttons = state ? 1UL : 0UL + }; + + lifo.Write(ref newState); + + _event.ReadableEvent.Signal(); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs index 66b5a5cba..1a0280648 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs @@ -4,6 +4,7 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Button; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad; @@ -32,6 +33,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid public DebugMouseDevice DebugMouse; public KeyboardDevice Keyboard; public NpadDevices Npads; + public ButtonDevice HomeButton; + public ButtonDevice SleepButton; + public ButtonDevice CaptureButton; private static void CheckTypeSizeOrThrow(int expectedSize) { @@ -48,6 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid CheckTypeSizeOrThrow>(0x350); CheckTypeSizeOrThrow>(0x350); CheckTypeSizeOrThrow>(0x3D8); + CheckTypeSizeOrThrow>(0x1B8); CheckTypeSizeOrThrow>(0x32000); CheckTypeSizeOrThrow(Horizon.HidSize); } @@ -70,6 +75,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid DebugMouse = new DebugMouseDevice(_device, false); Keyboard = new KeyboardDevice(_device, false); Npads = new NpadDevices(_device, true); + HomeButton = new ButtonDevice(_device, true, ButtonDeviceType.HomeButton); + SleepButton = new ButtonDevice(_device, true, ButtonDeviceType.SleepButton); + CaptureButton = new ButtonDevice(_device, true, ButtonDeviceType.CaptureButton); } public void RefreshInputConfig(List inputConfig) diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs index 5307292a5..1808db666 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs @@ -1,13 +1,135 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Hid.HidServer; using Ryujinx.HLE.HOS.Services.Hid.Types; +using Ryujinx.Horizon.Common; +using System; namespace Ryujinx.HLE.HOS.Services.Hid { [Service("hid:sys")] class IHidSystemServer : IpcService { - public IHidSystemServer(ServiceCtx context) { } + private KEvent _deviceRegisteredEventForControllerSupport; + private int _deviceRegisteredEventForControllerSupportHandle; + + private KEvent _connectionTriggerTimeoutEvent; + private int _connectionTriggerTimeoutEventHandle; + + private KEvent _joyDetachOnBluetoothOffEvent; + private int _joyDetachOnBluetoothOffEventHandle; + + private KEvent _playReportControllerUsageUpdateEvent; + private int _playReportControllerUsageUpdateEventHandle; + + private KEvent _playReportRegisteredDeviceUpdateEvent; + private int _playReportRegisteredDeviceUpdateEventHandle; + + private IHidServer _hidServer; + + private int _homeButtonEventHandle; + private int _sleepButtonEventHandle; + private int _captureButtonEventHandle; + + public IHidSystemServer(ServiceCtx context) + { + _deviceRegisteredEventForControllerSupport = new KEvent(context.Device.System.KernelContext); + _connectionTriggerTimeoutEvent = new KEvent(context.Device.System.KernelContext); + _joyDetachOnBluetoothOffEvent = new KEvent(context.Device.System.KernelContext); + _playReportControllerUsageUpdateEvent = new KEvent(context.Device.System.KernelContext); + _playReportRegisteredDeviceUpdateEvent = new KEvent(context.Device.System.KernelContext); + _hidServer = new IHidServer(context); + } + + [CommandCmif(101)] + // AcquireHomeButtonEventHandle() -> handle + public ResultCode AcquireHomeButtonEventHandle(ServiceCtx context) + { + if (_homeButtonEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(context.Device.Hid.HomeButton.GetEvent().ReadableEvent, out _homeButtonEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_homeButtonEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(111)] + // ActivateHomeButton(nn::applet::AppletResourceUserId, pid) + public ResultCode ActivateHomeButton(ServiceCtx context) + { + context.Device.Hid.HomeButton.Active = true; + + return ResultCode.Success; + } + + [CommandCmif(121)] + // AcquireSleepButtonEventHandle() -> handle + public ResultCode AcquireSleepButtonEventHandle(ServiceCtx context) + { + if (_sleepButtonEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(context.Device.Hid.SleepButton.GetEvent().ReadableEvent, out _sleepButtonEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_sleepButtonEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(131)] + // ActivateSleepButton(nn::applet::AppletResourceUserId, pid) + public ResultCode ActivateSleepButton(ServiceCtx context) + { + context.Device.Hid.SleepButton.Active = true; + + return ResultCode.Success; + } + + [CommandCmif(141)] + // AcquireCaptureButtonEventHandle() -> handle + public ResultCode AcquireCaptureButtonEventHandle(ServiceCtx context) + { + if (_captureButtonEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(context.Device.Hid.CaptureButton.GetEvent().ReadableEvent, out _captureButtonEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_captureButtonEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(151)] + // ActivateCaptureButton(nn::applet::AppletResourceUserId, pid) + public ResultCode ActivateCaptureButton(ServiceCtx context) + { + context.Device.Hid.CaptureButton.Active = true; + + return ResultCode.Success; + } + + [CommandCmif(301)] + // ActivateNpadSystem(u32) + public ResultCode ActivateNpadSystem(ServiceCtx context) + { + uint npadSystem = context.RequestData.ReadUInt32(); + + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { npadSystem }); + + return ResultCode.Success; + } [CommandCmif(303)] // ApplyNpadSystemCommonPolicy(u64) @@ -21,18 +143,23 @@ namespace Ryujinx.HLE.HOS.Services.Hid } [CommandCmif(306)] - // GetLastActiveNpad(u32) -> u8, u8 + // GetLastActiveNpad() -> nn::hid::NpadIdType public ResultCode GetLastActiveNpad(ServiceCtx context) { + // TODO: RequestData seems to have garbage data, reading an extra uint seems to fix the issue. + context.RequestData.ReadUInt32(); + context.ResponseData.Write((byte)context.Device.Hid.Npads.GetLastActiveNpadId()); return ResultCode.Success; } [CommandCmif(307)] - // GetNpadSystemExtStyle() -> u64 + // GetNpadSystemExtStyle(u32) -> (u64, u64) public ResultCode GetNpadSystemExtStyle(ServiceCtx context) { + context.RequestData.ReadUInt32(); + foreach (PlayerIndex playerIndex in context.Device.Hid.Npads.GetSupportedPlayers()) { if (HidUtils.GetNpadIdTypeFromIndex(playerIndex) > NpadIdType.Handheld) @@ -41,7 +168,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid } } - context.ResponseData.Write((ulong)context.Device.Hid.Npads.SupportedStyleSets); + // todo: wrong + // context.ResponseData.Write((ulong)context.Device.Hid.Npads.SupportedStyleSets); + context.ResponseData.Write((ulong)AppletFooterUiType.JoyDual); + context.ResponseData.Write((ulong)0); return ResultCode.Success; } @@ -57,6 +187,203 @@ namespace Ryujinx.HLE.HOS.Services.Hid return resultCode; } + [CommandCmif(321)] + // GetUniquePadsFromNpad(u32) -> (u64, buffer) + public ResultCode GetUniquePadsFromNpad(ServiceCtx context) + { + context.ResponseData.Write(0L); + + return ResultCode.Success; + } + + [CommandCmif(525)] + // IsJoyConAttachedOnAllRail() -> bool + public ResultCode IsJoyConAttachedOnAllRail(ServiceCtx context) + { + context.ResponseData.Write(true); + return ResultCode.Success; + } + + [CommandCmif(540)] + // AcquirePlayReportControllerUsageUpdateEvent() -> handle + public ResultCode AcquirePlayReportControllerUsageUpdateEvent(ServiceCtx context) + { + if (_playReportControllerUsageUpdateEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_playReportControllerUsageUpdateEvent.ReadableEvent, out _playReportControllerUsageUpdateEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_playReportControllerUsageUpdateEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(541)] + // GetPlayReportControllerUsages() -> (u64, buffer) + public ResultCode GetPlayReportControllerUsages(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceHid); + + context.ResponseData.Write(0L); + + return ResultCode.Success; + } + + [CommandCmif(542)] + // AcquirePlayReportRegisteredDeviceUpdateEvent() -> handle + public ResultCode AcquirePlayReportRegisteredDeviceUpdateEvent(ServiceCtx context) + { + if (_playReportRegisteredDeviceUpdateEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_playReportRegisteredDeviceUpdateEvent.ReadableEvent, out _playReportRegisteredDeviceUpdateEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_playReportRegisteredDeviceUpdateEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(543)] + // GetRegisteredDevicesOld() -> (u64, buffer) + public ResultCode GetRegisteredDevicesOld(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceHid); + + context.ResponseData.Write(0L); + + return ResultCode.Success; + } + + [CommandCmif(544)] + // AcquireConnectionTriggerTimeoutEvent() -> handle + public ResultCode AcquireConnectionTriggerTimeoutEvent(ServiceCtx context) + { + if (_connectionTriggerTimeoutEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_connectionTriggerTimeoutEvent.ReadableEvent, + out _connectionTriggerTimeoutEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_connectionTriggerTimeoutEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(545)] + // SendConnectionTrigger(nn::bluetooth::Address) + public ResultCode SendConnectionTrigger(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceHid); + + return ResultCode.Success; + } + + [CommandCmif(546)] + // AcquireDeviceRegisteredEventForControllerSupport() -> handle + public ResultCode AcquireDeviceRegisteredEventForControllerSupport(ServiceCtx context) + { + if (_deviceRegisteredEventForControllerSupportHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_deviceRegisteredEventForControllerSupport.ReadableEvent, + out _deviceRegisteredEventForControllerSupportHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_deviceRegisteredEventForControllerSupportHandle); + + return ResultCode.Success; + } + + [CommandCmif(703)] + // GetUniquePadIds() -> (u64, buffer) + public ResultCode GetUniquePadIds(ServiceCtx context) + { + context.ResponseData.Write(0L); + + return ResultCode.Success; + } + + [CommandCmif(751)] + // AcquireJoyDetachOnBluetoothOffEventHandle(nn::applet::AppletResourceUserId, pid) -> handle + public ResultCode AcquireJoyDetachOnBluetoothOffEventHandle(ServiceCtx context) + { + if (_joyDetachOnBluetoothOffEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_joyDetachOnBluetoothOffEvent.ReadableEvent, + out _joyDetachOnBluetoothOffEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_joyDetachOnBluetoothOffEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(850)] + // IsUsbFullKeyControllerEnabled() -> bool + public ResultCode IsUsbFullKeyControllerEnabled(ServiceCtx context) + { + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServiceHid); + + return ResultCode.Success; + } + + [CommandCmif(1120)] + // Unknown1120() + public ResultCode Unknown1120(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceHid); + return ResultCode.Success; + } + + [CommandCmif(1131)] + // FinalizeUsbFirmwareUpdate() + public ResultCode FinalizeUsbFirmwareUpdate(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceHid); + return ResultCode.Success; + } + + [CommandCmif(1132)] + // CheckUsbFirmwareUpdateRequired() + public ResultCode CheckUsbFirmwareUpdateRequired(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceHid); + return ResultCode.Success; + } + + [CommandCmif(1135)] + // InitializeUsbFirmwareUpdateWithoutMemory() + public ResultCode InitializeUsbFirmwareUpdateWithoutMemory(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceHid); + return ResultCode.Success; + } + + [CommandCmif(1153)] + // GetTouchScreenDefaultConfiguration() -> unknown + public ResultCode GetTouchScreenDefaultConfiguration(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceHid); + + return ResultCode.Success; + } + private ResultCode GetAppletFooterUiTypeImpl(ServiceCtx context, out AppletFooterUiType appletFooterUiType) { NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32(); diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Button/ButtonState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Button/ButtonState.cs new file mode 100644 index 000000000..96cd8f72b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Button/ButtonState.cs @@ -0,0 +1,12 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Button +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ButtonState : ISampledDataStruct + { + public ulong SamplingNumber; + public ulong Buttons; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs index 59d8f4489..98952f1d1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Button; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad; @@ -39,6 +40,24 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory /// [FieldOffset(0x3800)] public RingLifo Keyboard; + + /// + /// Home Button. + /// + [FieldOffset(0x4C00)] + public RingLifo HomeButton; + + /// + /// Sleep Button. + /// + [FieldOffset(0x4E00)] + public RingLifo SleepButton; + + /// + /// Capture Button. + /// + [FieldOffset(0x5000)] + public RingLifo CaptureButton; /// /// Nintendo Pads. diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs index 1b95b6712..e775211ab 100644 --- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services { @@ -280,5 +281,54 @@ namespace Ryujinx.HLE.HOS.Services _domainObjects.Clear(); } + + public static byte[] StructToBytes(T structure) where T : struct + { + int size = Marshal.SizeOf(structure); + byte[] bytes = new byte[size]; + IntPtr ptr = Marshal.AllocHGlobal(size); + + try + { + Marshal.StructureToPtr(structure, ptr, false); + Marshal.Copy(ptr, bytes, 0, size); + } + finally + { + Marshal.FreeHGlobal(ptr); + } + + return bytes; + } + + public Span CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput) + { + byte[] rawData; + + if (isOutput) + { + rawData = new byte[ipcBuff.Size]; + } + else + { + rawData = new byte[ipcBuff.Size]; + + context.Memory.Read(ipcBuff.Position, rawData); + } + + return new Span(rawData); + } + + public Span CreateSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput) where T : unmanaged + { + return MemoryMarshal.Cast(CreateByteSpanFromBuffer(context, ipcBuff, isOutput)); + } + + public void WriteSpanToBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, Span span) where T : unmanaged + { + Span rawData = MemoryMarshal.Cast(span); + + context.Memory.Write(ipcBuff.Position, rawData); + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorService.cs new file mode 100644 index 000000000..09ff483eb --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorService.cs @@ -0,0 +1,38 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Nfc.NfcManager; + +namespace Ryujinx.HLE.HOS.Services.Ldn +{ + class IMonitorService : IpcService + { + public IMonitorService() { } + + [CommandCmif(0)] + // GetStateForMonitor() -> u32 + public ResultCode GetStateForMonitor(ServiceCtx context) + { + State state = State.Initialized; + context.ResponseData.Write((uint)state); + + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + return ResultCode.Success; + } + + [CommandCmif(100)] + // InitializeMonitor() + public ResultCode InitializeMonitor(ServiceCtx context) + { + Logger.Info?.PrintStub(LogClass.ServiceLdn); + return ResultCode.Success; + } + + [CommandCmif(288)] + // ???() + public ResultCode Unknown288(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs index 4bea3945a..0307a73ae 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorServiceCreator.cs @@ -4,5 +4,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn class IMonitorServiceCreator : IpcService { public IMonitorServiceCreator(ServiceCtx context) { } + + [CommandCmif(0)] + // CreateMonitorService() -> object + public ResultCode CreateMonitorService(ServiceCtx context) + { + MakeObject(context, new IMonitorService()); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfMonitorServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfMonitorServiceCreator.cs new file mode 100644 index 000000000..64f0b5d2b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfMonitorServiceCreator.cs @@ -0,0 +1,22 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Ldn.Lp2p.SfMonitorServiceCreator; + +namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p +{ + [Service("lp2p:m")] // 9.1.0+ + class ISfMonitorServiceCreator : IpcService + { + public ISfMonitorServiceCreator(ServiceCtx context) { } + + [CommandCmif(0)] + // CreateMonitorService(pid, u64, u64) -> object + public ResultCode CreateMonitorService(ServiceCtx context) + { + MakeObject(context, new ISfMonitorService(context)); + + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfServiceMonitor.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfServiceMonitor.cs index d3a8bead2..d10f9c67b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfServiceMonitor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfServiceMonitor.cs @@ -1,6 +1,8 @@ using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Ldn.Types; using Ryujinx.Horizon.Common; using System; @@ -47,7 +49,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p public ResultCode GetGroupInfo(ServiceCtx context) { Logger.Stub?.PrintStub(LogClass.ServiceLdn); - + GroupInfo info = new(); + context.Memory.Write(context.Response.ReceiveBuff[0].Position,SpanHelpers.AsByteSpan(ref info).ToArray()); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/SfMonitorServiceCreator/ISfMonitorService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/SfMonitorServiceCreator/ISfMonitorService.cs new file mode 100644 index 000000000..270ebdecc --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/SfMonitorServiceCreator/ISfMonitorService.cs @@ -0,0 +1,27 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p.SfMonitorServiceCreator +{ + class ISfMonitorService : IpcService + { + public ISfMonitorService(ServiceCtx context) { } + + [CommandCmif(0)] + // Initialize() + public ResultCode Initialize(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + + [CommandCmif(288)] + // GetGroupInfo() -> buffer + public ResultCode GetGroupInfo(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/GroupInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/GroupInfo.cs new file mode 100644 index 000000000..7f919920f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/GroupInfo.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.Types +{ + [StructLayout(LayoutKind.Sequential)] + unsafe struct GroupInfo + { + public fixed byte info[0x200]; // Fixed size buffer + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/News/INewlyArrivedEventHolder.cs b/src/Ryujinx.HLE/HOS/Services/News/INewlyArrivedEventHolder.cs new file mode 100644 index 000000000..24c1811a6 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/News/INewlyArrivedEventHolder.cs @@ -0,0 +1,39 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; + +namespace Ryujinx.HLE.HOS.Services.News +{ + class INewlyArrivedEventHolder : IpcService + { + KEvent _newlyArrivedEvent; + int _newlyArrivedEventHandle; + + public INewlyArrivedEventHolder(ServiceCtx context) + { + _newlyArrivedEvent = new KEvent(context.Device.System.KernelContext); + _newlyArrivedEventHandle = -1; + } + + [CommandCmif(0)] + // Get() -> handle + public ResultCode Get(ServiceCtx context) + { + if (_newlyArrivedEventHandle == -1) + { + Result resultCode = context.Process.HandleTable.GenerateHandle(_newlyArrivedEvent.ReadableEvent, out _newlyArrivedEventHandle); + + if (resultCode != Result.Success) + { + return (ResultCode)resultCode.ErrorCode; + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_newlyArrivedEventHandle); + + Logger.Stub?.PrintStub(LogClass.Service); + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/News/INewsDataService.cs b/src/Ryujinx.HLE/HOS/Services/News/INewsDataService.cs new file mode 100644 index 000000000..8e416f9b5 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/News/INewsDataService.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.News +{ + class INewsDataService : IpcService + { + public INewsDataService(ServiceCtx context) { } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/News/INewsDatabaseService.cs b/src/Ryujinx.HLE/HOS/Services/News/INewsDatabaseService.cs new file mode 100644 index 000000000..d4b56b1a2 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/News/INewsDatabaseService.cs @@ -0,0 +1,36 @@ +using Ryujinx.Common.Logging; +using System; + +namespace Ryujinx.HLE.HOS.Services.News +{ + class INewsDatabaseService : IpcService + { + public INewsDatabaseService(ServiceCtx context) { } + + [CommandCmif(0)] + // GetListV1(unknown<4>, buffer, buffer) -> (unknown<4>, buffer) + public ResultCode GetListV1(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.Service); + return ResultCode.Success; + } + + [CommandCmif(1)] + // Count(buffer) -> u32 + public ResultCode Count(ServiceCtx context) + { + // TODO: Implement news database count + context.ResponseData.Write(0); + Logger.Stub?.PrintStub(LogClass.Service); + return ResultCode.Success; + } + + [CommandCmif(3)] + // UpdateIntegerValue(unknown<4>, buffer, buffer) + public ResultCode UpdateIntegerValue(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.Service); + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/News/INewsService.cs b/src/Ryujinx.HLE/HOS/Services/News/INewsService.cs new file mode 100644 index 000000000..bce42adf8 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/News/INewsService.cs @@ -0,0 +1,30 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.News +{ + class INewsService : IpcService + { + public INewsService(ServiceCtx context) { } + + [CommandCmif(30100)] + // GetSubscriptionStatus() -> u32 + public ResultCode GetSubscriptionStatus(ServiceCtx context) + { + // TODO: Implement this properly + Logger.Stub?.PrintStub(LogClass.Service); + context.ResponseData.Write((uint)0); + return ResultCode.Success; + } + + [CommandCmif(30200)] + // IsSystemUpdateRequired() -> bool + public ResultCode IsSystemUpdateRequired(ServiceCtx context) + { + // TODO: Implement this properly + Logger.Stub?.PrintStub(LogClass.Service); + context.ResponseData.Write(false); + return ResultCode.Success; + } + + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/News/IOverwriteEventHolder.cs b/src/Ryujinx.HLE/HOS/Services/News/IOverwriteEventHolder.cs new file mode 100644 index 000000000..3376975dc --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/News/IOverwriteEventHolder.cs @@ -0,0 +1,40 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.News +{ + class IOverwriteEventHolder : IpcService + { + KEvent _overwriteEvent; + int _overwriteEventHandle; + public IOverwriteEventHolder(ServiceCtx context) + { + _overwriteEvent = new KEvent(context.Device.System.KernelContext); + _overwriteEventHandle = -1; + } + + [CommandCmif(0)] + // Get() -> handle + public ResultCode Get(ServiceCtx context) + { + if (_overwriteEventHandle == -1) + { + Result resultCode = context.Process.HandleTable.GenerateHandle(_overwriteEvent.ReadableEvent, out _overwriteEventHandle); + + if (resultCode != Result.Success) + { + return (ResultCode)resultCode.ErrorCode; + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_overwriteEventHandle); + + Logger.Stub?.PrintStub(LogClass.Service); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs index 3d14629ca..bbb8180bc 100644 --- a/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/News/IServiceCreator.cs @@ -8,5 +8,46 @@ namespace Ryujinx.HLE.HOS.Services.News class IServiceCreator : IpcService { public IServiceCreator(ServiceCtx context) { } + + [CommandCmif(0)] + // CreateNewsService() -> object + public ResultCode CreateNewsService(ServiceCtx context) + { + MakeObject(context, new INewsService(context)); + return ResultCode.Success; + } + + [CommandCmif(30900)] + [CommandCmif(1)] + // CreateNewlyArrivedEventHolder() -> object + public ResultCode CreateNewlyArrivedEventHolder(ServiceCtx context) + { + MakeObject(context, new INewlyArrivedEventHolder(context)); + return ResultCode.Success; + } + + [CommandCmif(2)] + // CreateNewsDataService() -> object + public ResultCode CreateNewsDataService(ServiceCtx context) + { + MakeObject(context, new INewsDataService(context)); + return ResultCode.Success; + } + + [CommandCmif(3)] + // CreateNewsDatabaseService() -> object + public ResultCode CreateNewsDatabaseService(ServiceCtx context) + { + MakeObject(context, new INewsDatabaseService(context)); + return ResultCode.Success; + } + + [CommandCmif(4)] + // CreateOverwriteEventHolder() -> object + public ResultCode CreateOverwriteEventHolder(ServiceCtx context) + { + MakeObject(context, new IOverwriteEventHolder(context)); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index a5a822db3..76b7e5957 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -1,6 +1,7 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; +using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types; using System; @@ -17,6 +18,8 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService private UnicastIPAddressInformation _targetAddressInfoCache = null; private string _cacheChosenInterface = null; + private readonly UInt128 _interfaceId = UInt128Utils.CreateRandom(); + public IGeneralService() { _generalServiceDetail = new GeneralServiceDetail @@ -43,6 +46,17 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } + [CommandCmif(2)] + // CreateScanRequest() -> object + public ResultCode CreateScanRequest(ServiceCtx context) + { + MakeObject(context, new IScanRequest(context.Device.System)); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + + return ResultCode.Success; + } + [CommandCmif(4)] // CreateRequest(u32 version) -> object public ResultCode CreateRequest(ServiceCtx context) @@ -78,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService NetworkProfileData networkProfile = new() { - Uuid = Random.Shared.NextUInt128(), + Uuid = _interfaceId, }; networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress); @@ -91,6 +105,111 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } + [CommandCmif(6)] + // EnumerateNetworkInterfaces() -> (u32, buffer) + public ResultCode EnumerateNetworkInterfaces(ServiceCtx context) + { + context.ResponseData.Write(1); // account crashes if we don't have at least one interface + + // TODO: write interface info + + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + + return ResultCode.Success; + } + + [CommandCmif(7)] + // EnumerateNetworkProfiles() -> (u32, buffer) + public ResultCode EnumerateNetworkProfiles(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + + return ResultCode.Success; + } + + [CommandCmif(8)] + // GetNetworkProfile(nn::util::Uuid) -> buffer + public ResultCode GetNetworkProfile(ServiceCtx context) + { + ulong networkProfileDataPosition = context.Request.RecvListBuff[0].Position; + UInt128 uuid = context.RequestData.ReadStruct(); + + if (uuid != _interfaceId) + { + return ResultCode.NoInternetConnection; + } + + (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 networkProfile = new() + { + Uuid = _interfaceId, + }; + + 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(9)] + // SetNetworkProfile(buffer) + public ResultCode SetNetworkProfile(ServiceCtx context) + { + ulong networkProfileDataPosition = context.Request.PtrBuff[0].Position; + + NetworkProfileData networkProfile = context.Memory.Read(networkProfileDataPosition); + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { networkProfile }); + + return ResultCode.Success; + } + + [CommandCmif(10)] + // RemoveNetworkProfile(nn::util::Uuid) + public ResultCode RemoveNetworkProfile(ServiceCtx context) + { + UInt128 uuid = context.RequestData.ReadStruct(); + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { uuid }); + + return ResultCode.Success; + } + + [CommandCmif(11)] + // GetScanData(u32) -> (u32, buffer) + public ResultCode GetScanData(ServiceCtx context) + { + SystemVersion version = context.Device.System.ContentManager.GetCurrentFirmwareVersion(); + if (version.Major >= 4) + { + // TODO: write AccessPointData + } + else + { + // TOO: write AccessPointDataOld + } + + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + + return ResultCode.Success; + } + [CommandCmif(12)] // GetCurrentIpAddress() -> nn::nifm::IpV4Address public ResultCode GetCurrentIpAddress(ServiceCtx context) @@ -109,6 +228,24 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } + [CommandCmif(14)] + // CreateTemporaryNetworkProfile(buffer) -> (nn::util::Uuid, object) + public ResultCode CreateTemporaryNetworkProfile(ServiceCtx context) + { + ulong networkProfileDataPosition = context.Request.PtrBuff[0].Position; + + NetworkProfileData networkProfile = context.Memory.Read(networkProfileDataPosition); + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { networkProfile }); + + var uuid = UInt128Utils.CreateRandom(); + var networkProfileObject = new INetworkProfile(uuid, networkProfile); + + context.ResponseData.WriteStruct(uuid); + MakeObject(context, networkProfileObject); + + return ResultCode.Success; + } + [CommandCmif(15)] // GetCurrentIpConfigInfo() -> (nn::nifm::IpAddressSetting, nn::nifm::DnsSetting) public ResultCode GetCurrentIpConfigInfo(ServiceCtx context) @@ -128,6 +265,17 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } + [CommandCmif(17)] + // IsWirelessCommunicationEnabled() -> b8 + public ResultCode IsWirelessCommunicationEnabled(ServiceCtx context) + { + context.ResponseData.Write(true); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + + return ResultCode.Success; + } + [CommandCmif(18)] // GetInternetConnectionStatus() -> nn::nifm::detail::sf::InternetConnectionStatus public ResultCode GetInternetConnectionStatus(ServiceCtx context) @@ -165,6 +313,30 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } + [CommandCmif(26)] + // SetExclusiveClient(buffer) + public ResultCode SetExclusiveClient(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(position); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + + return ResultCode.Success; + } + + [CommandCmif(33)] + // ConfirmSystemAvailability() + public ResultCode ConfirmSystemAvailability(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + return ResultCode.Success; + } + private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(ServiceCtx context) { if (!NetworkInterface.GetIsNetworkAvailable()) @@ -198,7 +370,8 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler; - GeneralServiceManager.Remove(_generalServiceDetail.ClientId); + if (_generalServiceDetail.ClientId < GeneralServiceManager.Count) + GeneralServiceManager.Remove(_generalServiceDetail.ClientId); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/INetworkProfile.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/INetworkProfile.cs new file mode 100644 index 000000000..3020eb99f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/INetworkProfile.cs @@ -0,0 +1,61 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types; +using System; + +namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService +{ + class INetworkProfile : IpcService + { + public UInt128 Uuid { get; private set; } + public NetworkProfileData ProfileData; + + public INetworkProfile(UInt128 uuid, NetworkProfileData profileData) + { + Uuid = uuid; + ProfileData = profileData; + } + + [CommandCmif(0)] + // Update(buffer) -> nn::util::Uuid; + public ResultCode Update(ServiceCtx context) + { + ulong networkProfileDataPosition = context.Request.PtrBuff[0].Position; + NetworkProfileData networkProfile = context.Memory.Read(networkProfileDataPosition); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { networkProfile }); + + ProfileData = networkProfile; + + context.ResponseData.WriteStruct(Uuid); + + return ResultCode.Success; + } + + [CommandCmif(1)] + // Persist(nn::util::Uuid) -> nn::util::Uuid; + public ResultCode Persist1(ServiceCtx context) + { + UInt128 uuid = context.RequestData.ReadStruct(); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { uuid }); + + Uuid = uuid; + + context.ResponseData.WriteStruct(Uuid); + + return ResultCode.Success; + } + + [CommandCmif(2)] + // Persist() -> nn::util::Uuid; + public ResultCode Persist2(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + + context.ResponseData.WriteStruct(Uuid); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IScanRequest.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IScanRequest.cs new file mode 100644 index 000000000..50bd96f3e --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IScanRequest.cs @@ -0,0 +1,66 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService +{ + class IScanRequest : IpcService + { + private readonly KEvent _systemEvent; + private int _systemEventHandle; + + public IScanRequest(Horizon system) + { + _systemEvent = new KEvent(system.KernelContext); + } + + [CommandCmif(0)] + // Submit() + public ResultCode Submit(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + _systemEvent.ReadableEvent.Signal(); + + return ResultCode.Success; + } + + [CommandCmif(1)] + // IsProcessing() -> bool + public ResultCode IsProcessing(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + context.ResponseData.Write(false); + + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetResult() -> u32 + public ResultCode GetResult(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + context.ResponseData.Write(0); + + return ResultCode.Success; + } + + [CommandCmif(3)] + // GetSystemEventReadableHandle() -> (handle) + public ResultCode GetSystemEventReadableHandle(ServiceCtx context) + { + if (_systemEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_systemEvent.ReadableEvent, out _systemEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_systemEventHandle); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs index c5946be84..b20e40d3a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationServicesForSystem.cs @@ -1,8 +1,39 @@ +using Ryujinx.Common.Logging; + namespace Ryujinx.HLE.HOS.Services.Notification { [Service("notif:s")] // 9.0.0+ class INotificationServicesForSystem : IpcService { public INotificationServicesForSystem(ServiceCtx context) { } + + [CommandCmif(520)] + // ListAlarmSettings() -> (s32, buffer) + public ResultCode ListAlarmSettings(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceNotif); + + return ResultCode.Success; + } + + [CommandCmif(1040)] + // OpenNotificationSystemEventAccessor() -> object + public ResultCode OpenNotificationSystemEventAccessor(ServiceCtx context) + { + MakeObject(context, new INotificationSystemEventAccessor(context)); + + return ResultCode.Success; + } + + [CommandCmif(1510)] + // GetPresentationSetting() -> unknown + public ResultCode GetPresentationSetting(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNotif); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Notification/INotificationSystemEventAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationSystemEventAccessor.cs new file mode 100644 index 000000000..fa98bc8de --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Notification/INotificationSystemEventAccessor.cs @@ -0,0 +1,36 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Notification +{ + class INotificationSystemEventAccessor : IpcService + { + private KEvent _event; + private int _eventHandle; + + public INotificationSystemEventAccessor(ServiceCtx context) + { + _event = new KEvent(context.Device.System.KernelContext); + } + + [CommandCmif(0)] + // GetEvent() -> handle + public ResultCode GetEvent(ServiceCtx context) + { + if (_eventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs index dc707e6f7..aa5cc1cca 100644 --- a/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs +++ b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsSystem.cs @@ -1,8 +1,51 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; + namespace Ryujinx.HLE.HOS.Services.Npns { [Service("npns:s")] class INpnsSystem : IpcService { - public INpnsSystem(ServiceCtx context) { } + public KEvent ListenEvent; + public int ListenHandle = 0; + public INpnsSystem(ServiceCtx context) + { + ListenEvent = new KEvent(context.Device.System.KernelContext); + } + + [CommandCmif(2)] + // ListenTo(u64) + public ResultCode ListenTo(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNpns); + return ResultCode.Success; + } + + [CommandCmif(5)] + // GetReceiveEvent() -> handle + public ResultCode GetReceiveEvent(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNpns); + if (ListenHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(ListenEvent.ReadableEvent, out ListenHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(ListenHandle); + return ResultCode.Success; + } + + [CommandCmif(8)] + // ListenToByName() + public ResultCode ListenToByName(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNpns); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index f510da594..6954a54cf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -1,12 +1,232 @@ using LibHac.Ns; +using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; +using Ryujinx.Horizon.Common; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Ns.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using ApplicationId = LibHac.ApplicationId; namespace Ryujinx.HLE.HOS.Services.Ns { [Service("ns:am")] class IApplicationManagerInterface : IpcService { - public IApplicationManagerInterface(ServiceCtx context) { } + private KEvent _applicationRecordUpdateSystemEvent; + private int _applicationRecordUpdateSystemEventHandle; + + private KEvent _sdCardMountStatusChangedEvent; + private int _sdCardMountStatusChangedEventHandle; + + private KEvent _gameCardUpdateDetectionEvent; + private int _gameCardUpdateDetectionEventHandle; + + private KEvent _gameCardMountFailureEvent; + private int _gameCardMountFailureEventHandle; + + public IApplicationManagerInterface(ServiceCtx context) + { + _applicationRecordUpdateSystemEvent = new KEvent(context.Device.System.KernelContext); + _sdCardMountStatusChangedEvent = new KEvent(context.Device.System.KernelContext); + _gameCardUpdateDetectionEvent = new KEvent(context.Device.System.KernelContext); + _gameCardMountFailureEvent = new KEvent(context.Device.System.KernelContext); + } + + + [CommandCmif(0)] + // ListApplicationRecord(s32) -> (s32, buffer) + // entry_offset -> (out_entrycount, ApplicationRecord[]) + public ResultCode ListApplicationRecord(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + int entryOffset = context.RequestData.ReadInt32(); + ulong position = context.Request.ReceiveBuff[0].Position; + List records = new(); + int sID = 24; + foreach (RyuApplicationData title in context.Device.Configuration.Titles) + { + records.Add(new ApplicationRecord() + { + AppId = title.AppId, + Type = ApplicationRecordType.Installed, + Unknown = 0, + Unknown2 = (byte)sID++ + }); + } + + records.Sort((x, y) => (int)(x.AppId.Value - y.AppId.Value)); + if (entryOffset > 0) + { + records = records.Skip(entryOffset - 1).ToList(); + } + + context.ResponseData.Write(records.Count); + foreach (var record in records) + { + context.Memory.Write(position, StructToBytes(record)); + position += (ulong)Marshal.SizeOf(); + } + + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetApplicationRecordUpdateSystemEvent() -> handle + public ResultCode GetApplicationRecordUpdateSystemEvent(ServiceCtx context) + { + if (_applicationRecordUpdateSystemEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_applicationRecordUpdateSystemEvent.ReadableEvent, out _applicationRecordUpdateSystemEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_applicationRecordUpdateSystemEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(3)] + // GetApplicationView(buffer) -> buffer + public ResultCode GetApplicationViewDeperecated(ServiceCtx context) + { + ulong inPosition = context.Request.SendBuff[0].Position; + ulong inSize = context.Request.SendBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; + + List applicationIds = new(); + for (ulong i = 0; i < inSize / sizeof(ulong); i++) + { + ulong position = inPosition + (i * sizeof(ulong)); + applicationIds.Add(new(context.Memory.Read(position))); + } + + List views = new(); + + foreach (ApplicationId applicationId in applicationIds) + { + views.Add(new() + { + AppId = applicationId, + Unknown1 = 0x70000, + Flags = 0x401f17, + Unknown2 = new byte[0x40] + }); + } + + context.ResponseData.Write(views.Count); + foreach (var view in views) + { + context.Memory.Write(outputPosition, StructToBytes(view)); + outputPosition += (ulong)Marshal.SizeOf(); + } + + return ResultCode.Success; + } + + [CommandCmif(44)] + // GetSdCardMountStatusChangedEvent() -> handle + public ResultCode GetSdCardMountStatusChangedEvent(ServiceCtx context) + { + if (_sdCardMountStatusChangedEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_sdCardMountStatusChangedEvent.ReadableEvent, out _sdCardMountStatusChangedEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_sdCardMountStatusChangedEventHandle); + + return ResultCode.Success; + } + + const long storageFreeAndTotalSpaceSize = 6999999999999L; + [CommandCmif(47)] + // GetTotalSpaceSize(u8 storage_id) -> u64 + public ResultCode GetTotalSpaceSize(ServiceCtx context) + { + long storageId = context.RequestData.ReadByte(); + + + context.ResponseData.Write(storageFreeAndTotalSpaceSize); + + return ResultCode.Success; + } + + [CommandCmif(48)] + // GetFreeSpaceSize(u8 storage_id) -> u64 + public ResultCode GetFreeSpaceSize(ServiceCtx context) + { + long storageId = context.RequestData.ReadByte(); + + context.ResponseData.Write(storageFreeAndTotalSpaceSize); + + return ResultCode.Success; + } + + [CommandCmif(52)] + // GetGameCardUpdateDetectionEvent() -> handle + public ResultCode GetGameCardUpdateDetectionEvent(ServiceCtx context) + { + if (_gameCardUpdateDetectionEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_gameCardUpdateDetectionEvent.ReadableEvent, out _gameCardUpdateDetectionEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gameCardUpdateDetectionEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(55)] + // GetApplicationDesiredLanguage(u8) -> u8 + public ResultCode GetApplicationDesiredLanguage(ServiceCtx context) + { + byte source = context.RequestData.ReadByte(); + byte language = 0; + + Logger.Stub?.PrintStub(LogClass.ServiceNs, new { source, language }); + + context.ResponseData.Write(language); + + return ResultCode.Success; + } + + [CommandCmif(70)] + // ResumeAll() + public ResultCode ResumeAll(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNs); + + return ResultCode.Success; + } + + [CommandCmif(505)] + // GetGameCardMountFailureEvent() -> handle + public ResultCode GetGameCardMountFailureEvent(ServiceCtx context) + { + if (_gameCardMountFailureEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_gameCardMountFailureEvent.ReadableEvent, out _gameCardMountFailureEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gameCardMountFailureEventHandle); + + return ResultCode.Success; + } [CommandCmif(400)] // GetApplicationControlData(u8, u64) -> (unknown<4>, buffer) @@ -19,9 +239,113 @@ namespace Ryujinx.HLE.HOS.Services.Ns ulong position = context.Request.ReceiveBuff[0].Position; - ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + var title = context.Device.Configuration.Titles.FirstOrDefault(x => x.AppId.Value == titleId); + + ApplicationControlProperty nacp = title.Nacp; context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); + if (title.Icon?.Length > 0) + { + context.Memory.Write(position + 0x4000, title.Icon); + + context.ResponseData.Write(0x4000 + title.Icon.Length); + } + + return ResultCode.Success; + } + + [CommandCmif(403)] + // GetMaxApplicationControlCacheCount() -> u32 + public ResultCode GetMaxApplicationControlCacheCount(ServiceCtx context) + { + // TODO: Implement this method properly. + Logger.Stub?.PrintStub(LogClass.Service); + return ResultCode.Success; + } + + [CommandCmif(1701)] + // GetApplicationView(buffer) -> buffer + public ResultCode GetApplicationView(ServiceCtx context) + { + ulong inPosition = context.Request.SendBuff[0].Position; + ulong inSize = context.Request.SendBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; + + List applicationIds = new(); + for (ulong i = 0; i < inSize / sizeof(ulong); i++) + { + ulong position = inPosition + (i * sizeof(ulong)); + applicationIds.Add(new(context.Memory.Read(position))); + } + + List views = new(); + + foreach (ApplicationId applicationId in applicationIds) + { + views.Add(new() + { + AppId = applicationId, + Unknown1 = 0x70000, + Flags = 0x401f17, + Unknown2 = new byte[0x40] + }); + } + + context.ResponseData.Write(views.Count); + foreach (var view in views) + { + context.Memory.Write(outputPosition, StructToBytes(view)); + outputPosition += (ulong)Marshal.SizeOf(); + } + + return ResultCode.Success; + } + [CommandCmif(1704)] + // GetApplicationView(buffer) -> buffer + public ResultCode GetApplicationViewWithPromotionInfo(ServiceCtx context) + { + ulong inPosition = context.Request.SendBuff[0].Position; + ulong inSize = context.Request.SendBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; + + List applicationIds = new(); + for (ulong i = 0; i < inSize / sizeof(ulong); i++) + { + ulong position = inPosition + (i * sizeof(ulong)); + applicationIds.Add(new(context.Memory.Read(position))); + } + + List views = new(); + + foreach (ApplicationId applicationId in applicationIds) + { + views.Add(new() + { + AppId = applicationId, + Unknown1 = 0x70000, + Flags = 0x401f17, + Unknown2 = new byte[0x40] + }); + } + + List promotions = new(); + foreach(ApplicationView view in views) + { + promotions.Add(new() + { + AppView = view, + Promotion = new() + }); + } + + context.ResponseData.Write(views.Count); + foreach (var promotion in promotions) + { + context.Memory.Write(outputPosition, StructToBytes(promotion)); + outputPosition += (ulong)Marshal.SizeOf(); + } return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IDynamicRightsInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IDynamicRightsInterface.cs new file mode 100644 index 000000000..22e66ba20 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IDynamicRightsInterface.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Ns +{ + class IDynamicRightsInterface : IpcService + { + + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs index 73a164135..17bf2cc15 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs @@ -1,24 +1,47 @@ using LibHac.Common; using LibHac.Ns; +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Ns.Types; +using System; +using System.Linq; +using ApplicationId = LibHac.ApplicationId; namespace Ryujinx.HLE.HOS.Services.Ns { class IReadOnlyApplicationControlDataInterface : IpcService { public IReadOnlyApplicationControlDataInterface(ServiceCtx context) { } - + [CommandCmif(0)] // GetApplicationControlData(u8, u64) -> (unknown<4>, buffer) public ResultCode GetApplicationControlData(ServiceCtx context) { #pragma warning disable IDE0059 // Remove unnecessary value assignment byte source = (byte)context.RequestData.ReadInt64(); - ulong titleId = context.RequestData.ReadUInt64(); + ApplicationId titleId = context.RequestData.ReadStruct(); #pragma warning restore IDE0059 ulong position = context.Request.ReceiveBuff[0].Position; - + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + foreach (RyuApplicationData ryuApplicationData in context.Device.Configuration.Titles) + { + if (ryuApplicationData.AppId != titleId) + { + continue; + } + + nacp = ryuApplicationData.Nacp; + // NOTE: this is hacky but it works and prevents a crash + nacp.Title[1] = ryuApplicationData.Nacp.Title[0]; + if (ryuApplicationData.Icon?.Length > 0) + { + context.Memory.Write(position + 0x4000, ryuApplicationData.Icon); + context.ResponseData.Write(0x4000 + ryuApplicationData.Icon.Length); + } + break; + } context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs index d4af1afa6..b58d1d85c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs @@ -8,16 +8,17 @@ namespace Ryujinx.HLE.HOS.Services.Ns class IServiceGetterInterface : IpcService { public IServiceGetterInterface(ServiceCtx context) { } + - [CommandCmif(7996)] - // GetApplicationManagerInterface() -> object - public ResultCode GetApplicationManagerInterface(ServiceCtx context) + [CommandCmif(7988)] + // GetDynamicRightsInterface() -> object + public ResultCode GetDynamicRightsInterface(ServiceCtx context) { - MakeObject(context, new IApplicationManagerInterface(context)); + MakeObject(context, new IDynamicRightsInterface()); return ResultCode.Success; } - + [CommandCmif(7989)] // GetReadOnlyApplicationControlDataInterface() -> object public ResultCode GetReadOnlyApplicationControlDataInterface(ServiceCtx context) @@ -27,6 +28,15 @@ namespace Ryujinx.HLE.HOS.Services.Ns return ResultCode.Success; } + [CommandCmif(7996)] + // GetApplicationManagerInterface() -> object + public ResultCode GetApplicationManagerInterface(ServiceCtx context) + { + MakeObject(context, new IApplicationManagerInterface(context)); + + return ResultCode.Success; + } + [CommandCmif(7997)] // GetDownloadTaskInterface() -> object public ResultCode GetDownloadTaskInterface(ServiceCtx context) diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs index 1108778c3..860e9f210 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs @@ -1,8 +1,50 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Ns.Types; +using Ryujinx.Horizon.Common; +using System; + namespace Ryujinx.HLE.HOS.Services.Ns { [Service("ns:su")] class ISystemUpdateInterface : IpcService { - public ISystemUpdateInterface(ServiceCtx context) { } + KEvent _systemUpdateEvent; + int _systemUpdateEventHandle; + public ISystemUpdateInterface(ServiceCtx context) + { + _systemUpdateEvent = new KEvent(context.Device.System.KernelContext); + _systemUpdateEventHandle = -1; + } + + [CommandCmif(0)] + // GetBackgroundNetworkUpdateState() -> u32 + public ResultCode GetBackgroundNetworkUpdateState(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.Service); + context.ResponseData.Write((byte)BackgroundNetworkUpdateState.None); + return ResultCode.Success; + } + + [CommandCmif(9)] + // GetSystemUpdateNotificationEventForContentDelivery() -> handle + public ResultCode GetSystemUpdateNotificationEventForContentDelivery(ServiceCtx context) + { + if (_systemUpdateEventHandle == -1) + { + Result resultCode = context.Process.HandleTable.GenerateHandle(_systemUpdateEvent.ReadableEvent, out _systemUpdateEventHandle); + + if (resultCode != Result.Success) + { + return (ResultCode)resultCode.ErrorCode; + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_systemUpdateEventHandle); + + Logger.Stub?.PrintStub(LogClass.Service); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecord.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecord.cs new file mode 100644 index 000000000..95a0c2565 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecord.cs @@ -0,0 +1,21 @@ +using LibHac; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ns.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct ApplicationRecord + { + public ApplicationId AppId; + public ApplicationRecordType Type; + public byte Unknown; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] Padding1; + + public byte Unknown2; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] Padding2; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecordType.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecordType.cs new file mode 100644 index 000000000..cbfd02b2c --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecordType.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Ns.Types +{ + enum ApplicationRecordType : byte + { + Installing = 2, + Installed = 3, + GameCardNotInserted = 5, + Archived = 11, + GameCard = 16, + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationView.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationView.cs new file mode 100644 index 000000000..d22f7b0f3 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationView.cs @@ -0,0 +1,15 @@ +using LibHac; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ns.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x50)] + struct ApplicationView + { + public ApplicationId AppId; + public uint Unknown1; + public uint Flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x40)] + public byte[] Unknown2; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationViewWithPromotionInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationViewWithPromotionInfo.cs new file mode 100644 index 000000000..11d3d7078 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationViewWithPromotionInfo.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ns.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x70)] + struct ApplicationViewWithPromotionInfo + { + public ApplicationView AppView; + public PromotionInfo Promotion; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Types/BackgroundNetworkUpdateState.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Types/BackgroundNetworkUpdateState.cs new file mode 100644 index 000000000..0c30c5283 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Types/BackgroundNetworkUpdateState.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Ns.Types +{ + public enum BackgroundNetworkUpdateState + { + None, + InProgress, + Ready + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Types/PromotionInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Types/PromotionInfo.cs new file mode 100644 index 000000000..3d0314b12 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Types/PromotionInfo.cs @@ -0,0 +1,26 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ns.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct PromotionInfo + { + [MarshalAs(UnmanagedType.I8)] + public long StartTimestamp; + + [MarshalAs(UnmanagedType.I8)] + public long EndTimestamp; + + [MarshalAs(UnmanagedType.I8)] + public long RemainingTime; + + [MarshalAs(UnmanagedType.U4)] + public uint Reserved; + + [MarshalAs(UnmanagedType.U1)] + public byte Flags; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public byte[] Padding; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Types/RyuApplicationData.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Types/RyuApplicationData.cs new file mode 100644 index 000000000..5af7140ee --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Types/RyuApplicationData.cs @@ -0,0 +1,22 @@ +using LibHac; +using LibHac.Ns; + +namespace Ryujinx.HLE.HOS.Services.Ns.Types +{ + // Used to store some internal data about applications. + public struct RyuApplicationData + { + public ApplicationId AppId; + public ApplicationControlProperty Nacp; + public string Path; + public byte[] Icon; + + public RyuApplicationData(ApplicationId appId, ApplicationControlProperty nacp, string path, byte[] icon) + { + AppId = appId; + Nacp = nacp; + Path = path; + Icon = icon; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Olsc/IDaemonController.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/IDaemonController.cs new file mode 100644 index 000000000..782a94dfd --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Olsc/IDaemonController.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Olsc +{ + class IDaemonController : IpcService + { + + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs index c6e372052..d8fc39e72 100644 --- a/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs @@ -1,8 +1,46 @@ +using Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService; + namespace Ryujinx.HLE.HOS.Services.Olsc { [Service("olsc:s")] // 4.0.0+ class IOlscServiceForSystemService : IpcService { public IOlscServiceForSystemService(ServiceCtx context) { } + + [CommandCmif(0)] + // GetTransferTaskListController() -> object + public ResultCode GetTransferTaskListController(ServiceCtx context) + { + MakeObject(context, new ITransferTaskListController(context)); + + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetDaemonController() -> object + public ResultCode GetDaemonController(ServiceCtx context) + { + MakeObject(context, new IDaemonController()); + + return ResultCode.Success; + } + + [CommandCmif(200)] + // GetDataTransferPolicy(u64) -> (u8, u8) + public ResultCode GetDataTransferPolicy(ServiceCtx context) + { + context.ResponseData.Write(0); + context.ResponseData.Write(0); + return ResultCode.Success; + } + + [CommandCmif(10000)] + // GetOlscServiceForSystemService() -> object + public ResultCode GetOlscServiceForSystemService(ServiceCtx context) + { + MakeObject(context, new IOlscServiceForSystemService(context)); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Olsc/OlscServiceForSystemService/INativeHandleHolder.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/OlscServiceForSystemService/INativeHandleHolder.cs new file mode 100644 index 000000000..e034046ff --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Olsc/OlscServiceForSystemService/INativeHandleHolder.cs @@ -0,0 +1,35 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService +{ + class INativeHandleHolder : IpcService + { + private KEvent _event; + private int _eventHandle; + + public INativeHandleHolder(ServiceCtx context) + { + _event = new KEvent(context.Device.System.KernelContext); + } + + [CommandCmif(0)] + // GetNativeHandle() -> handle + public ResultCode GetNativeHandle(ServiceCtx context) + { + if (_eventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Olsc/OlscServiceForSystemService/ITransferTaskListController.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/OlscServiceForSystemService/ITransferTaskListController.cs new file mode 100644 index 000000000..11bc03668 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Olsc/OlscServiceForSystemService/ITransferTaskListController.cs @@ -0,0 +1,31 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService +{ + class ITransferTaskListController : IpcService + { + public ITransferTaskListController(ServiceCtx context) { } + + [CommandCmif(5)] + // GetNativeHandleHolder() -> object + public ResultCode GetNativeHandleHolder(ServiceCtx context) + { + MakeObject(context, new INativeHandleHolder(context)); + + Logger.Stub?.PrintStub(LogClass.ServiceOlsc); + + return ResultCode.Success; + } + + [CommandCmif(9)] + // GetNativeHandleHolderEx() -> object + public ResultCode GetNativeHandleHolderEx(ServiceCtx context) + { + MakeObject(context, new INativeHandleHolder(context)); + + Logger.Stub?.PrintStub(LogClass.ServiceOlsc); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs b/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs index 9b026a1c3..0cbfa562e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs @@ -1,5 +1,8 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Arp; +using Ryujinx.Horizon.Common; using System; using static LibHac.Ns.ApplicationControlProperty; @@ -22,11 +25,24 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory private bool _stereoVisionRestriction = false; #pragma warning restore IDE0052, CS0414 + private KEvent _synchronizationEvent; + private int _synchronizationEventHandle; + + private KEvent _unlinkedEvent; + private int _unlinkedEventHandle; + + private KEvent _playTimerRequestSuspensionEvent; + private int _playTimerRequestSuspensionEventHandle; + public IParentalControlService(ServiceCtx context, ulong pid, bool withInitialize, int permissionFlag) { _pid = pid; _permissionFlag = permissionFlag; + _synchronizationEvent = new KEvent(context.Device.System.KernelContext); + _unlinkedEvent = new KEvent(context.Device.System.KernelContext); + _playTimerRequestSuspensionEvent = new KEvent(context.Device.System.KernelContext); + if (withInitialize) { Initialize(context); @@ -97,6 +113,28 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.Success; } + [CommandCmif(1006)] + // IsRestrictionTemporaryUnlocked() -> b8 + public ResultCode IsRestrictionTemporaryUnlocked(ServiceCtx context) + { + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1010)] + // IsRestrictedSystemSettingsEntered() -> b8 + public ResultCode IsRestrictedSystemSettingsEntered(ServiceCtx context) + { + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + [CommandCmif(1017)] // 10.0.0+ // EndFreeCommunication() public ResultCode EndFreeCommunication(ServiceCtx context) @@ -106,6 +144,37 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return ResultCode.Success; } + [CommandCmif(1032)] + // GetSafetyLevel() -> u32 + public ResultCode GetSafetyLevel(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1035)] + // GetCurrentSettings() -> nn::pctl::RestrictionSettings + public ResultCode GetCurrentSettings(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1039)] + // GetFreeCommunicationApplicationListCount() -> u32 + public ResultCode GetFreeCommunicationApplicationListCount(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + [CommandCmif(1013)] // 4.0.0+ // ConfirmStereoVisionPermission() public ResultCode ConfirmStereoVisionPermission(ServiceCtx context) @@ -235,6 +304,125 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory return resultCode; } + [CommandCmif(1403)] + // IsPairingActive() -> b8 + public ResultCode IsPairingActive(ServiceCtx context) + { + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1432)] + // GetSynchronizationEvent() -> handle + public ResultCode GetSynchronizationEvent(ServiceCtx context) + { + if (_synchronizationEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_synchronizationEvent.ReadableEvent, out _synchronizationEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_synchronizationEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(1451)] + // StartPlayTimer() + public ResultCode StartPlayTimer(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1452)] + // StopPlayTimer() + public ResultCode StopPlayTimer(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1455)] + // IsRestrictedByPlayTimer() -> b8 + public ResultCode IsRestrictedByPlayTimer(ServiceCtx context) + { + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1456)] + // GetPlayTimerSettings() -> nn::pctl::PlayTimerSettings + public ResultCode GetPlayTimerSettings(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1457)] + // GetPlayTimerEventToRequestSuspension() -> handle + public ResultCode GetPlayTimerEventToRequestSuspension(ServiceCtx context) + { + if (_playTimerRequestSuspensionEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_playTimerRequestSuspensionEvent.ReadableEvent, out _playTimerRequestSuspensionEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_playTimerRequestSuspensionEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(1458)] // 4.0.0+ + // IsPlayTimerAlarmDisabled() -> b8 + public ResultCode IsPlayTimerAlarmDisabled(ServiceCtx context) + { + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServicePctl); + + return ResultCode.Success; + } + + [CommandCmif(1473)] + // GetUnlinkedEvent() -> handle + public ResultCode GetUnlinkedEvent(ServiceCtx context) + { + if (_unlinkedEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_unlinkedEvent.ReadableEvent, out _unlinkedEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_unlinkedEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(145601)] + // GetPlayTimerSettings() + public ResultCode GetPlayTimerSettingsEx(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServicePctl); + return ResultCode.Success; + } + private ResultCode IsStereoVisionPermittedImpl() { /* diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs index 4ad57bedc..df27bda67 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs @@ -1,4 +1,5 @@ using LibHac; +using LibHac.Account; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; @@ -7,9 +8,15 @@ using LibHac.Ncm; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Services.Settings.Types; +using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.SystemState; using System; using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; namespace Ryujinx.HLE.HOS.Services.Settings @@ -85,12 +92,68 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } + + [CommandCmif(7)] + // GetLockScreenFlag() -> bool + public ResultCode GetLockScreenFlag(ServiceCtx context) + { + context.ResponseData.Write(false); + Logger.Stub?.PrintStub(LogClass.ServiceSet); + return ResultCode.Success; + } + [CommandCmif(17)] + // GetAccountSettings() -> nn::settings::system::AccountSettings + public ResultCode GetAccountSettings(ServiceCtx context) + { + AccountSettings accountSettings = new AccountSettings + { + UserSelectorSettings = new UserSelectorSettings() + }; + context.ResponseData.WriteStruct(accountSettings); + Logger.Stub?.PrintStub(LogClass.ServiceSet); + return ResultCode.Success; + } + + [CommandCmif(21)] + // GetEulaVersions() -> (u32, buffer) + public ResultCode GetEulaVersions(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; + + if ((ulong)Unsafe.SizeOf() > bufferLen) + { + return ResultCode.NullEULAVersionBuffer; + } + + var eulaVersion = new EulaVersion + { + Version = 0x10000, + RegionCode = 1, + ClockType = 1, + NetworkSystemClock = 0, + SteadyClock = new Time.Clock.SteadyClockTimePoint { + TimePoint = 0xc, + ClockSourceId = new UInt128(0x36a0328708bc18c1, 0x1608ea2b023284) + } + }; + + context.Memory.Write(bufferPosition, eulaVersion); + + context.ResponseData.Write(1); + + return ResultCode.Success; + } + [CommandCmif(23)] // GetColorSetId() -> i32 public ResultCode GetColorSetId(ServiceCtx context) { - context.ResponseData.Write((int)context.Device.System.State.ThemeColor); + bool isDarkMode = context.Device.UIHandler.IsDarkMode(); + context.ResponseData.Write(isDarkMode ? 1 : 0); return ResultCode.Success; } @@ -99,12 +162,51 @@ namespace Ryujinx.HLE.HOS.Services.Settings // GetColorSetId() -> i32 public ResultCode SetColorSetId(ServiceCtx context) { - int colorSetId = context.RequestData.ReadInt32(); - - context.Device.System.State.ThemeColor = (ColorSet)colorSetId; + Logger.Stub?.PrintStub(LogClass.ServiceSet); return ResultCode.Success; } + + [CommandCmif(29)] + // GetNotificationSettings() -> nn::settings::system::NotificationSettings + public ResultCode GetNotificationSettings(ServiceCtx context) + { + NotificationSettings notificationSettings = new NotificationSettings + { + Flags = NotificationFlag.None, + Volume = NotificationVolume.Low, + HeadTime = new NotificationTime(), + TailTime = new NotificationTime() + }; + + context.ResponseData.WriteStruct(notificationSettings); + + return ResultCode.Success; + } + + [CommandCmif(31)] + // GetAccountNotificationSettings() -> (u32, buffer) + public ResultCode GetAccountNotificationSettings(ServiceCtx context) + { + var buffer = context.Request.ReceiveBuff[0]; + + Span elementsSpan = CreateSpanFromBuffer(context,buffer,true); + elementsSpan[0] = new AccountNotificationSettings + { + Uid = new Array16(), + Flags = 0x1F, + FriendPresenceOverlayPermission = 0x1, + FriendInvitationOverlayPermission = 0x1, + Reserved1 = 0, + Reserved2 = 0 + }; + int count = elementsSpan.Length; + Logger.Info?.PrintStub(LogClass.ServiceSet, $"AccountNotificationSettings: {count} settings found"); + context.ResponseData.Write(1); + WriteSpanToBuffer(context, buffer, elementsSpan); + return ResultCode.Success; + } + [CommandCmif(37)] // GetSettingsItemValueSize(buffer, buffer) -> u64 @@ -221,6 +323,29 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } + [CommandCmif(39)] + // GetTvSettings() -> nn::settings::system::TvSettings + public ResultCode GetTvSettings(ServiceCtx context) + { + TvSettings tvSettings = new(); + + context.ResponseData.WriteStruct(tvSettings); + + return ResultCode.Success; + } + + [CommandCmif(47)] + // GetQuestFlag() -> bool + public ResultCode GetQuestFlag(ServiceCtx context) + { + // NOTE: Gets a flag determining whether the console is a kiosk unit (codenamed "Quest"). Used by qlaunch to determine whether to launch Retail Interactive Display Menu. + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + return ResultCode.Success; + } + [CommandCmif(60)] // IsUserSystemClockAutomaticCorrectionEnabled() -> bool public ResultCode IsUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) @@ -244,15 +369,50 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } + [CommandCmif(63)] + // GetPrimaryAlbumStorage() -> s32 + public ResultCode GetPrimaryAlbumStorage(ServiceCtx context) + { + context.ResponseData.Write((byte)PrimaryAlbumStorage.Nand); + Logger.Stub?.PrintStub(LogClass.ServiceSet); + return ResultCode.Success; + } + [CommandCmif(68)] // GetSerialNumber() -> buffer public ResultCode GetSerialNumber(ServiceCtx context) { context.ResponseData.Write(Encoding.ASCII.GetBytes("RYU00000000000")); + Logger.Stub?.PrintStub(LogClass.ServiceSet); + return ResultCode.Success; + } + + [CommandCmif(71)] + // GetSleepSettings() -> SleepSettings + public ResultCode GetSleepSettings(ServiceCtx context) + { + SleepSettings sleepSettings = new(); + + context.ResponseData.WriteStruct(sleepSettings); + return ResultCode.Success; + } + + [CommandCmif(75)] + // GetInitialLaunchSettings() -> nn::settings::system::InitialLaunchSettings + public ResultCode GetInitialLaunchSettings(ServiceCtx context) + { + InitialLaunchSettings launchSettings = new InitialLaunchSettings(); + launchSettings.Flags |= InitialLaunchFlag.InitialLaunchCompletionFlag; + launchSettings.Flags |= InitialLaunchFlag.InitialLaunchUserAdditionFlag; + launchSettings.Flags |= InitialLaunchFlag.InitialLaunchTimestampFlag; + + context.ResponseData.WriteStruct(launchSettings); + + Logger.Stub?.PrintStub(LogClass.ServiceSet); return ResultCode.Success; } - + [CommandCmif(77)] // GetDeviceNickName() -> buffer public ResultCode GetDeviceNickName(ServiceCtx context) @@ -301,7 +461,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } - + [CommandCmif(90)] // GetMiiAuthorId() -> nn::util::Uuid public ResultCode GetMiiAuthorId(ServiceCtx context) @@ -313,7 +473,85 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } + + [CommandCmif(95)] + // GetAutoUpdateEnableFlag() -> bool + public ResultCode GetAutoUpdateEnableFlag(ServiceCtx context) + { + context.ResponseData.Write(false); + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + return ResultCode.Success; + } + + [CommandCmif(99)] + // GetBatteryPercentageFlag() -> u8 + public ResultCode GetBatteryPercentageFlag(ServiceCtx context) + { + context.ResponseData.Write(100); + + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + return ResultCode.Success; + } + + [CommandCmif(124)] + // GetErrorReportSharePermission() -> s32 + public ResultCode GetErrorReportSharePermission(ServiceCtx context) + { + context.ResponseData.Write(1); + + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + return ResultCode.Success; + } + + [CommandCmif(126)] + // GetAppletLaunchFlags() -> u32 + public ResultCode GetAppletLaunchFlags(ServiceCtx context) + { + // NOTE: I do not know what this is used for but it is used by qlaunch. + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + return ResultCode.Success; + } + + [CommandCmif(136)] + // GetKeyboardLayout() -> s32 + public ResultCode GetKeyboardLayout(ServiceCtx context) + { + context.ResponseData.Write((int)KeyboardLayout.Default); + + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + return ResultCode.Success; + } + + [CommandCmif(170)] + // GetChineseTraditionalInputMethod() -> s32 + public ResultCode GetChineseTraditionalInputMethod(ServiceCtx context) + { + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + return ResultCode.Success; + } + + [CommandCmif(201)] + // GetFieldTestingFlag() -> bool + public ResultCode GetFieldTestingFlag(ServiceCtx context) + { + context.ResponseData.Write(false); + + Logger.Stub?.PrintStub(LogClass.ServiceSet); + + return ResultCode.Success; + } + public byte[] GetFirmwareData(Switch device) { const ulong SystemVersionTitleId = 0x0100000000000809; diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/AccountNotificationSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/AccountNotificationSettings.cs new file mode 100644 index 000000000..b90308133 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/AccountNotificationSettings.cs @@ -0,0 +1,19 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + struct AccountNotificationSettings + { + public Array16 Uid; // 0x0 - 0x10: Unique ID (16 bytes) + + public uint Flags; // 0x10 - 0x4: Notification Flags + + public byte FriendPresenceOverlayPermission; // 0x14 - 0x1: Friend presence overlay permission + + public byte FriendInvitationOverlayPermission; // 0x15 - 0x1: Friend invitation overlay permission + + public byte Reserved1; // 0x16 - 0x2: Reserved bytes + public byte Reserved2; // 0x16 - 0x2: Reserved bytes + } + +} diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/AccountSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/AccountSettings.cs new file mode 100644 index 000000000..91a41f959 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/AccountSettings.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct AccountSettings + { + public UserSelectorSettings UserSelectorSettings; + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/CmuMode.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/CmuMode.cs new file mode 100644 index 000000000..eb84f0c1b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/CmuMode.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum CmuMode : uint + { + None = 0, + ColorInvert = 1, + HighContrast = 2, + GrayScale = 3 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/ConsoleSleepPlan.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/ConsoleSleepPlan.cs new file mode 100644 index 000000000..c692f60b0 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/ConsoleSleepPlan.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum ConsoleSleepPlan : uint + { + OneHour = 0, + TwoHour = 1, + ThreeHour = 2, + SixHour = 3, + TwelveHour = 4, + Never = 5 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/ErrorReportSharePermission.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/ErrorReportSharePermission.cs new file mode 100644 index 000000000..de021e600 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/ErrorReportSharePermission.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum ErrorReportSharePermission : uint + { + NotConfirmed = 0, + Granted = 1, + Denied = 2 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/EulaVersion.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/EulaVersion.cs new file mode 100644 index 000000000..df1ceed10 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/EulaVersion.cs @@ -0,0 +1,16 @@ +using Ryujinx.HLE.HOS.Services.Time.Clock; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct EulaVersion + { + public uint Version; + public uint RegionCode; + public uint ClockType; + public uint Reserved; + public long NetworkSystemClock; + public SteadyClockTimePoint SteadyClock; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/HandheldSleepPlan.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/HandheldSleepPlan.cs new file mode 100644 index 000000000..19548f79a --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/HandheldSleepPlan.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum HandheldSleepPlan : uint + { + OneMin = 0, + ThreeMin = 1, + FiveMin = 2, + TenMin = 3, + ThirtyMin = 4, + Never = 5 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/HdmiContentType.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/HdmiContentType.cs new file mode 100644 index 000000000..70a4b8b42 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/HdmiContentType.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum HdmiContentType : uint + { + None = 0, + Graphics = 1, + Cinema = 2, + Photo = 3, + Game = 4 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/InitialLaunchFlag.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/InitialLaunchFlag.cs new file mode 100644 index 000000000..4f6d3a739 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/InitialLaunchFlag.cs @@ -0,0 +1,13 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [Flags] + enum InitialLaunchFlag : uint + { + None = 0, + InitialLaunchCompletionFlag = 1 << 0, + InitialLaunchUserAdditionFlag = 1 << 8, + InitialLaunchTimestampFlag = 1 << 16 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/InitialLaunchSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/InitialLaunchSettings.cs new file mode 100644 index 000000000..abc0a7fdb --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/InitialLaunchSettings.cs @@ -0,0 +1,14 @@ +using Ryujinx.HLE.HOS.Services.Time.Clock; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 8)] + struct InitialLaunchSettings + { + public InitialLaunchFlag Flags; + public uint Reserved; + public SteadyClockTimePoint TimeStamp; + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationFlag.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationFlag.cs new file mode 100644 index 000000000..0d349324c --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationFlag.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [Flags] + enum NotificationFlag : uint + { + None = 0, + RingtoneFlag = 1 << 0, + DownloadCompletionFlag = 1 << 1, + EnablesNews = 1 << 8, + IncomingLampFlag = 1 << 9 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationSettings.cs new file mode 100644 index 000000000..d8e3bea96 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationSettings.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 8)] + struct NotificationSettings + { + public NotificationFlag Flags; + public NotificationVolume Volume; + public NotificationTime HeadTime; + public NotificationTime TailTime; + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationTime.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationTime.cs new file mode 100644 index 000000000..207465a7f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationTime.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + struct NotificationTime + { + public uint Hour; + public uint Minute; + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationVolume.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationVolume.cs new file mode 100644 index 000000000..94590e958 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/NotificationVolume.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum NotificationVolume : uint + { + Mute = 0, + Low = 1, + High = 2 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/PrimaryAlbumStorage.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/PrimaryAlbumStorage.cs new file mode 100644 index 000000000..49e54374f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/PrimaryAlbumStorage.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum PrimaryAlbumStorage : uint + { + Nand = 0, + SdCard = 1, + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/RgbRange.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/RgbRange.cs new file mode 100644 index 000000000..a8409f647 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/RgbRange.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum RgbRange : uint + { + Auto = 0, + Full = 1, + Limited = 2 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/SleepSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/SleepSettings.cs new file mode 100644 index 000000000..dd417d658 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/SleepSettings.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + struct SleepSettings + { + public uint Flags; + public HandheldSleepPlan HandheldSleepPlan; + public ConsoleSleepPlan ConsoleSleepPlan; + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvFlag.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvFlag.cs new file mode 100644 index 000000000..943bb4163 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvFlag.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [Flags] + enum TvFlag : uint + { + None = 0, + Allows4k = 1 << 0, + Allows3d = 1 << 1, + AllowsCec = 1 << 2, + PreventsScreenBurnIn = 1 << 3 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvResolution.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvResolution.cs new file mode 100644 index 000000000..ef852bbde --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvResolution.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum TvResolution : uint + { + Auto = 0, + Resolution1080p = 1, + Resolution720p = 2, + Resolution480p = 3 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvSettings.cs new file mode 100644 index 000000000..d781de01e --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/TvSettings.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + struct TvSettings + { + public TvFlag Flags; + public TvResolution TvResolution; + public HdmiContentType HdmiContentType; + public RgbRange RgbRange; + public CmuMode CmuMode; + public uint TvUnderscan; + public uint TvGamma; + public uint ContrastRatio; + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/UserSelectorFlag.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/UserSelectorFlag.cs new file mode 100644 index 000000000..3f7e1c966 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/UserSelectorFlag.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [Flags] + enum UserSelectorFlag : uint + { + None = 0, + SkipsIfSingleUser = 1 << 0, + Unknown = 1U << 31 + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/Types/UserSelectorSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/Types/UserSelectorSettings.cs new file mode 100644 index 000000000..d223d99c0 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Settings/Types/UserSelectorSettings.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct UserSelectorSettings + { + public UserSelectorFlag UserSelectorFlag; + } +} \ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs index fa6e77298..ba12a6885 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs @@ -49,6 +49,15 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return _applicationDisplayService.CreateStrayLayer(context); } + + [CommandCmif(3000)] + // ListDisplayModes(u64) -> (u64, buffer) + public ResultCode ListDisplayModes(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceVi); + + return ResultCode.Success; + } [CommandCmif(3200)] // GetDisplayMode(u64) -> nn::vi::DisplayModeInfo diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 56bcae1ee..3bec26386 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -126,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService ulong displayInfoBuffer = context.Request.ReceiveBuff[0].Position; // TODO: Determine when more than one display is needed. - ulong displayCount = 1; + ulong displayCount = 2; for (int i = 0; i < (int)displayCount; i++) { diff --git a/src/Ryujinx.HLE/UI/IHostUIHandler.cs b/src/Ryujinx.HLE/UI/IHostUIHandler.cs index 8ccb5cf89..54bc3ca7f 100644 --- a/src/Ryujinx.HLE/UI/IHostUIHandler.cs +++ b/src/Ryujinx.HLE/UI/IHostUIHandler.cs @@ -66,5 +66,10 @@ namespace Ryujinx.HLE.UI /// Displays the player select dialog and returns the selected profile. /// UserProfile ShowPlayerSelectDialog(); + + /// + /// Gets the UI theme and returns true if is dark mode. + /// + bool IsDarkMode(); } } diff --git a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs index 91beec20d..24482764a 100644 --- a/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs +++ b/src/Ryujinx.Horizon/Bcat/Ipc/ServiceCreator/BcatService.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Horizon.Bcat.Types; using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Sdk.Bcat; @@ -16,5 +17,13 @@ namespace Ryujinx.Horizon.Bcat.Ipc return Result.Success; } + + [CmifCommand(30300)] + // RegisterSystemApplicationDeliveryTasks + public Result RegisterSystemApplicationDeliveryTasks() + { + Logger.Stub?.PrintStub(LogClass.ServiceBcat); + return Result.Success; + } } } diff --git a/src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs b/src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs index 2c97476c8..9960f6bac 100644 --- a/src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs +++ b/src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Sdk.Lbl; using Ryujinx.Horizon.Sdk.Sf; @@ -8,6 +9,15 @@ namespace Ryujinx.Horizon.Lbl.Ipc { private bool _vrModeEnabled; private float _currentBrightnessSettingForVrMode; + + [CmifCommand(1)] + // LoadCurrentSetting() + public Result LoadCurrentSetting() + { + Logger.Stub?.PrintStub(LogClass.ServiceLbl); + + return Result.Success; + } [CmifCommand(17)] public Result SetBrightnessReflectionDelayLevel(float unknown0, float unknown1) diff --git a/src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs b/src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs new file mode 100644 index 000000000..20f07d067 --- /dev/null +++ b/src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs @@ -0,0 +1,10 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Ovln; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Ovln.Ipc +{ + partial class Sender : ISender + { + } +} diff --git a/src/Ryujinx.Horizon/Ovln/Ipc/SenderService.cs b/src/Ryujinx.Horizon/Ovln/Ipc/SenderService.cs index 04ef5d4b1..a95e7f2e5 100644 --- a/src/Ryujinx.Horizon/Ovln/Ipc/SenderService.cs +++ b/src/Ryujinx.Horizon/Ovln/Ipc/SenderService.cs @@ -1,8 +1,21 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Sdk.Ovln; +using Ryujinx.Horizon.Sdk.Sf; namespace Ryujinx.Horizon.Ovln.Ipc { partial class SenderService : ISenderService { + [CmifCommand(0)] + // OpenSender() -> object + public Result OpenSender(out ISender service) + { + service = new Sender(); + + Logger.Stub?.PrintStub(LogClass.ServiceOvln); + + return Result.Success; + } } } diff --git a/src/Ryujinx.Horizon/Sdk/Ovln/ISender.cs b/src/Ryujinx.Horizon/Sdk/Ovln/ISender.cs new file mode 100644 index 000000000..8e9ba9ebd --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Ovln/ISender.cs @@ -0,0 +1,9 @@ +using LibHac; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Ovln +{ + interface ISender : IServiceObject + { + } +} diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 25f451858..46dac3582 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -40,6 +40,7 @@ using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.Services.Ns.Types; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.Input; using Ryujinx.Input.HLE; @@ -47,6 +48,7 @@ using SkiaSharp; using SPB.Graphics.Vulkan; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; @@ -54,6 +56,7 @@ using System.Threading; using System.Threading.Tasks; using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing; +using ApplicationId = LibHac.ApplicationId; using InputManager = Ryujinx.Input.HLE.InputManager; using IRenderer = Ryujinx.Graphics.GAL.IRenderer; using Key = Ryujinx.Input.Key; @@ -124,6 +127,7 @@ namespace Ryujinx.Ava private bool _dialogShown; private readonly bool _isFirmwareTitle; + private List _titles = new(); private readonly Lock _lockObject = new(); @@ -141,6 +145,8 @@ namespace Ryujinx.Ava public string ApplicationPath { get; private set; } public ulong ApplicationId { get; private set; } public bool ScreenshotRequested { get; set; } + + public IImmutableList Titles { get => _titles.ToImmutableList(); } public AppHost( RendererHost renderer, @@ -919,12 +925,19 @@ namespace Ryujinx.Ava // Initialize Configuration. MemoryConfiguration memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value; + + _titles = new List(); + foreach (ApplicationData App in _viewModel.Applications) + { + _titles.Add(new(new ApplicationId(App.Id),App.ControlHolder.Value,App.Path,App.Icon)); + } Device = new Switch(new HLEConfiguration( VirtualFileSystem, _viewModel.LibHacHorizonManager, ContentManager, _accountManager, + Titles.ToImmutableList(), _userChannelPersistence, renderer, InitializeAudio(), diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index e89b872b5..28bebb46d 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -172,6 +172,56 @@ "zh_TW": "" } }, + { + "ID": "MenuBarFileOpenAppletOpenQLaunchApplet", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "QLaunch Applet", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "MenuBarFileOpenAppletOpenQLaunchAppletToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Open QLaunch Applet in Standalone mode", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsTabInputDirectMouseAccess", "Translations": { diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index aa8238fc6..e873f4c74 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -16,9 +16,11 @@ using Ryujinx.Graphics.Metal; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; +using Ryujinx.HLE.HOS.Services.Ns.Types; using Ryujinx.Input; using Silk.NET.Vulkan; using System; +using System.Collections.Immutable; using System.IO; using System.Text.Json; using System.Threading.Tasks; @@ -333,6 +335,7 @@ namespace Ryujinx.Headless _libHacHorizonManager, _contentManager, _accountManager, + new ImmutableArray(), _userChannelPersistence, renderer, new SDL2HardwareDeviceDriver(), diff --git a/src/Ryujinx/Headless/Windows/WindowBase.cs b/src/Ryujinx/Headless/Windows/WindowBase.cs index c9d672af4..0645f341c 100644 --- a/src/Ryujinx/Headless/Windows/WindowBase.cs +++ b/src/Ryujinx/Headless/Windows/WindowBase.cs @@ -563,5 +563,10 @@ namespace Ryujinx.Headless { return AccountSaveDataManager.GetLastUsedUser(); } + + public bool IsDarkMode() + { + return true; + } } } diff --git a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs index c75e532ec..17fd1e44f 100644 --- a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs +++ b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs @@ -313,5 +313,10 @@ namespace Ryujinx.Ava.UI.Applet } return profile; } + + public bool IsDarkMode() + { + return ConfigurationState.Instance.UI.BaseStyle.Value == "Dark"; + } } } diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml index 24d71b495..4f2119f9a 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml @@ -65,6 +65,11 @@ Header="{ext:Locale MenuBarFileOpenAppletOpenPhotoViewerApplet}" Icon="{ext:Icon fa-solid fa-photo-film}" ToolTip.Tip="{ext:Locale MenuBarFileOpenAppletOpenPhotoViewerAppletToolTip}" /> + ViewModel.AppHost?.Pause()); @@ -165,6 +166,16 @@ namespace Ryujinx.Ava.UI.Views.Main await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData); } } + + public AppletMetadata SystemApplet => new(ViewModel.ContentManager,"qlaunch", 0x0100000000001000); + + public async Task OpenSystemApplet() + { + if (SystemApplet.CanStart(ViewModel.ContentManager, out var appData, out var nacpData)) + { + await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData); + } + } public async Task OpenCheatManagerForCurrentApp() {