diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index a7d36765dc..20c8da3f81 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -47,6 +47,7 @@ namespace Ryujinx.Common.Logging ServiceNim, ServiceNs, ServiceNsd, + ServiceNtc, ServiceNv, ServiceOlsc, ServicePctl, diff --git a/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs b/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs index f5a3bc7b45..1a4100ee39 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/Ntc/IStaticService.cs @@ -1,8 +1,24 @@ -namespace Ryujinx.HLE.HOS.Services.Nim.Ntc +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService; + +namespace Ryujinx.HLE.HOS.Services.Nim.Ntc { [Service("ntc")] class IStaticService : IpcService { public IStaticService(ServiceCtx context) { } + + [CommandHipc(0)] + // OpenEnsureNetworkClockAvailabilityService(u64) -> object + public ResultCode CreateAsyncInterface(ServiceCtx context) + { + ulong unknown = context.RequestData.ReadUInt64(); + + MakeObject(context, new IEnsureNetworkClockAvailabilityService(context)); + + Logger.Stub?.PrintStub(LogClass.ServiceNtc, new { unknown }); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs b/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs new file mode 100644 index 0000000000..fb31bd1f0d --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs @@ -0,0 +1,77 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +using System; + +namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService +{ + class IEnsureNetworkClockAvailabilityService : IpcService + { + private KEvent _finishNotificationEvent; + private ResultCode _taskResultCode; + + public IEnsureNetworkClockAvailabilityService(ServiceCtx context) + { + _finishNotificationEvent = new KEvent(context.Device.System.KernelContext); + _taskResultCode = ResultCode.Success; + + // NOTE: The service starts a thread that polls Nintendo NTP server and syncs the time with it. + // Additionnally it gets and uses some settings too: + // autonomic_correction_interval_seconds, autonomic_correction_failed_retry_interval_seconds, + // autonomic_correction_immediate_try_count_max, autonomic_correction_immediate_try_interval_milliseconds + } + + [CommandHipc(0)] + // StartTask() + public ResultCode StartTask(ServiceCtx context) + { + if (!context.Device.Configuration.EnableInternetAccess) + { + return (ResultCode)Time.ResultCode.NetworkTimeNotAvailable; + } + + // NOTE: Since we don't support the Nintendo NTP server, we can signal the event now to confirm the update task is done. + _finishNotificationEvent.ReadableEvent.Signal(); + + Logger.Stub?.PrintStub(LogClass.ServiceNtc); + + return ResultCode.Success; + } + + [CommandHipc(1)] + // GetFinishNotificationEvent() -> handle + public ResultCode GetFinishNotificationEvent(ServiceCtx context) + { + if (context.Process.HandleTable.GenerateHandle(_finishNotificationEvent.ReadableEvent, out int finishNotificationEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(finishNotificationEventHandle); + + return ResultCode.Success; + } + + [CommandHipc(2)] + // GetResult() + public ResultCode GetResult(ServiceCtx context) + { + return _taskResultCode; + } + + [CommandHipc(3)] + // Cancel() + public ResultCode Cancel(ServiceCtx context) + { + // NOTE: The update task should be canceled here. + _finishNotificationEvent.ReadableEvent.Signal(); + + _taskResultCode = (ResultCode)Time.ResultCode.NetworkTimeTaskCanceled; + + Logger.Stub?.PrintStub(LogClass.ServiceNtc); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs index 57bf42060b..3b042ec036 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs @@ -7,16 +7,18 @@ Success = 0, - TimeServiceNotInitialized = (0 << ErrorCodeShift) | ModuleId, - PermissionDenied = (1 << ErrorCodeShift) | ModuleId, - TimeMismatch = (102 << ErrorCodeShift) | ModuleId, - UninitializedClock = (103 << ErrorCodeShift) | ModuleId, - TimeNotFound = (200 << ErrorCodeShift) | ModuleId, - Overflow = (201 << ErrorCodeShift) | ModuleId, - LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId, - OutOfRange = (902 << ErrorCodeShift) | ModuleId, - TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId, - TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId, - NotImplemented = (990 << ErrorCodeShift) | ModuleId, + TimeServiceNotInitialized = (0 << ErrorCodeShift) | ModuleId, + PermissionDenied = (1 << ErrorCodeShift) | ModuleId, + TimeMismatch = (102 << ErrorCodeShift) | ModuleId, + UninitializedClock = (103 << ErrorCodeShift) | ModuleId, + TimeNotFound = (200 << ErrorCodeShift) | ModuleId, + Overflow = (201 << ErrorCodeShift) | ModuleId, + LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId, + OutOfRange = (902 << ErrorCodeShift) | ModuleId, + TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId, + TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId, + NotImplemented = (990 << ErrorCodeShift) | ModuleId, + NetworkTimeNotAvailable = (1000 << ErrorCodeShift) | ModuleId, + NetworkTimeTaskCanceled = (1003 << ErrorCodeShift) | ModuleId, } }