From 54b79dffa8eafe9da9765329930e8832ae90ae83 Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Thu, 25 Jul 2019 16:44:51 +0200 Subject: [PATCH] Implement time:* 4.0.0 commands (#736) * Abstract SteadyClockCore to follow Nintendo changes in 4.x This is the ground work for 4.0.0 support * Implement TickBasedSteadyClockCore Preparation for the ephemeral clock. * Refactor SystemClockCore to follow 4.0.0 changes * Implement EphemeralNetworkSystemClock * Implement GetSnapshotClock & GetSnapshotClockFromSystemClockContext * Implement CalculateStandardUserSystemClockDifferenceByUser & CalculateSpanBetween * Remove an outdated comment & unused import * Fix a nit and GetClockSnapshot * Address comment --- Ryujinx.HLE/HOS/Horizon.cs | 2 +- .../HOS/Services/Time/Clock/ClockTypes.cs | 47 +++- .../Clock/EphemeralNetworkSystemClockCore.cs | 27 +++ .../Clock/StandardLocalSystemClockCore.cs | 42 +--- .../Clock/StandardNetworkSystemClockCore.cs | 42 +--- .../Time/Clock/StandardSteadyClockCore.cs | 107 +++++++++ .../Time/Clock/StandardUserSystemClockCore.cs | 15 +- .../Services/Time/Clock/SteadyClockCore.cs | 143 +++--------- .../Services/Time/Clock/SystemClockCore.cs | 30 ++- .../Time/Clock/TickBasedSteadyClockCore.cs | 40 ++++ .../HOS/Services/Time/IStaticService.cs | 211 +++++++++++++++++- Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs | 16 +- .../HOS/Services/Time/ITimeZoneService.cs | 1 - 13 files changed, 518 insertions(+), 205 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 01b87b42..f8bb345f 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -199,7 +199,7 @@ namespace Ryujinx.HLE.HOS // TODO: use set:sys (and set external clock source id from settings) // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. - SteadyClockCore.Instance.ConfigureSetupValue(); + StandardSteadyClockCore.Instance.ConfigureSetupValue(); if (Services.Set.NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes)) { diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs index 860f5ad2..c70819c0 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs @@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock [StructLayout(LayoutKind.Sequential)] struct TimeSpanType { + private const long NanoSecondsPerSecond = 1000000000; + public long NanoSeconds; public TimeSpanType(long nanoSeconds) @@ -16,12 +18,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public long ToSeconds() { - return NanoSeconds / 1000000000; + return NanoSeconds / NanoSecondsPerSecond; + } + + public static TimeSpanType FromSeconds(long seconds) + { + return new TimeSpanType(seconds * NanoSecondsPerSecond); } public static TimeSpanType FromTicks(ulong ticks, ulong frequency) { - return new TimeSpanType((long)ticks * 1000000000 / (long)frequency); + return FromSeconds((long)ticks / (long)frequency); } } @@ -59,4 +66,40 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public long Offset; public SteadyClockTimePoint SteadyTimePoint; } + + [StructLayout(LayoutKind.Sequential, Size = 0xD0)] + struct ClockSnapshot + { + public SystemClockContext UserContext; + public SystemClockContext NetworkContext; + public long UserTime; + public long NetworkTime; + public CalendarTime UserCalendarTime; + public CalendarTime NetworkCalendarTime; + public CalendarAdditionalInfo UserCalendarAdditionalTime; + public CalendarAdditionalInfo NetworkCalendarAdditionalTime; + public SteadyClockTimePoint SteadyClockTimePoint; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x24)] + public char[] LocationName; + + [MarshalAs(UnmanagedType.I1)] + public bool IsAutomaticCorrectionEnabled; + public byte Type; + public ushort Unknown; + + public static ResultCode GetCurrentTime(out long currentTime, SteadyClockTimePoint steadyClockTimePoint, SystemClockContext context) + { + currentTime = 0; + + if (steadyClockTimePoint.ClockSourceId == context.SteadyTimePoint.ClockSourceId) + { + currentTime = steadyClockTimePoint.TimePoint + context.Offset; + + return ResultCode.Success; + } + + return ResultCode.TimeMismatch; + } + } } diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs new file mode 100644 index 00000000..8e9073a4 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + class EphemeralNetworkSystemClockCore : SystemClockCore + { + private static EphemeralNetworkSystemClockCore _instance; + + public static EphemeralNetworkSystemClockCore Instance + { + get + { + if (_instance == null) + { + _instance = new EphemeralNetworkSystemClockCore(TickBasedSteadyClockCore.Instance); + } + + return _instance; + } + } + + public EphemeralNetworkSystemClockCore(SteadyClockCore steadyClockCore) : base(steadyClockCore) { } + + public override ResultCode Flush(SystemClockContext context) + { + return ResultCode.Success; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs index 16550199..a1727976 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs @@ -1,34 +1,23 @@ -using Ryujinx.HLE.HOS.Kernel.Threading; - -namespace Ryujinx.HLE.HOS.Services.Time.Clock +namespace Ryujinx.HLE.HOS.Services.Time.Clock { class StandardLocalSystemClockCore : SystemClockCore { - private SteadyClockCore _steadyClockCore; - private SystemClockContext _context; - - private static StandardLocalSystemClockCore instance; + private static StandardLocalSystemClockCore _instance; public static StandardLocalSystemClockCore Instance { get { - if (instance == null) + if (_instance == null) { - instance = new StandardLocalSystemClockCore(SteadyClockCore.Instance); + _instance = new StandardLocalSystemClockCore(StandardSteadyClockCore.Instance); } - return instance; + return _instance; } } - public StandardLocalSystemClockCore(SteadyClockCore steadyClockCore) - { - _steadyClockCore = steadyClockCore; - _context = new SystemClockContext(); - - _context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId(); - } + public StandardLocalSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore) {} public override ResultCode Flush(SystemClockContext context) { @@ -36,24 +25,5 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return ResultCode.Success; } - - public override SteadyClockCore GetSteadyClockCore() - { - return _steadyClockCore; - } - - public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context) - { - context = _context; - - return ResultCode.Success; - } - - public override ResultCode SetSystemClockContext(SystemClockContext context) - { - _context = context; - - return ResultCode.Success; - } } } diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs index c00f460e..5037fb60 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs @@ -1,35 +1,28 @@ -using System; -using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Kernel.Threading; namespace Ryujinx.HLE.HOS.Services.Time.Clock { class StandardNetworkSystemClockCore : SystemClockCore { - private SteadyClockCore _steadyClockCore; - private SystemClockContext _context; private TimeSpanType _standardNetworkClockSufficientAccuracy; - private static StandardNetworkSystemClockCore instance; + private static StandardNetworkSystemClockCore _instance; public static StandardNetworkSystemClockCore Instance { get { - if (instance == null) + if (_instance == null) { - instance = new StandardNetworkSystemClockCore(SteadyClockCore.Instance); + _instance = new StandardNetworkSystemClockCore(StandardSteadyClockCore.Instance); } - return instance; + return _instance; } } - public StandardNetworkSystemClockCore(SteadyClockCore steadyClockCore) + public StandardNetworkSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore) { - _steadyClockCore = steadyClockCore; - _context = new SystemClockContext(); - - _context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId(); _standardNetworkClockSufficientAccuracy = new TimeSpanType(0); } @@ -40,25 +33,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return ResultCode.Success; } - public override SteadyClockCore GetSteadyClockCore() - { - return _steadyClockCore; - } - - public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context) - { - context = _context; - - return ResultCode.Success; - } - - public override ResultCode SetSystemClockContext(SystemClockContext context) - { - _context = context; - - return ResultCode.Success; - } - public bool IsStandardNetworkSystemClockAccuracySufficient(KThread thread) { SteadyClockCore steadyClockCore = GetSteadyClockCore(); @@ -66,7 +40,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock bool isStandardNetworkClockSufficientAccuracy = false; - if (_context.SteadyTimePoint.GetSpanBetween(currentTimePoint, out long outSpan) == ResultCode.Success) + ResultCode result = GetSystemClockContext(thread, out SystemClockContext context); + + if (result == ResultCode.Success && context.SteadyTimePoint.GetSpanBetween(currentTimePoint, out long outSpan) == ResultCode.Success) { isStandardNetworkClockSufficientAccuracy = outSpan * 1000000000 < _standardNetworkClockSufficientAccuracy.NanoSeconds; } diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs new file mode 100644 index 00000000..fea5bf2f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs @@ -0,0 +1,107 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Bpc; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + class StandardSteadyClockCore : SteadyClockCore + { + private long _setupValue; + private ResultCode _setupResultCode; + private bool _isRtcResetDetected; + private TimeSpanType _testOffset; + private TimeSpanType _internalOffset; + + private static StandardSteadyClockCore _instance; + + public static StandardSteadyClockCore Instance + { + get + { + if (_instance == null) + { + _instance = new StandardSteadyClockCore(); + } + + return _instance; + } + } + + private StandardSteadyClockCore() + { + _testOffset = new TimeSpanType(0); + _internalOffset = new TimeSpanType(0); + } + + public override SteadyClockTimePoint GetTimePoint(KThread thread) + { + SteadyClockTimePoint result = new SteadyClockTimePoint + { + TimePoint = 0, + ClockSourceId = GetClockSourceId() + }; + + TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0); + + result.TimePoint = _setupValue + ticksTimeSpan.ToSeconds(); + + return result; + } + + public override TimeSpanType GetTestOffset() + { + return _testOffset; + } + + public override void SetTestOffset(TimeSpanType testOffset) + { + _testOffset = testOffset; + } + + public override ResultCode GetRtcValue(out ulong rtcValue) + { + return (ResultCode)IRtcManager.GetExternalRtcValue(out rtcValue); + } + + public bool IsRtcResetDetected() + { + return _isRtcResetDetected; + } + + public override TimeSpanType GetInternalOffset() + { + return _internalOffset; + } + + public override void SetInternalOffset(TimeSpanType internalOffset) + { + _internalOffset = internalOffset; + } + + public override ResultCode GetSetupResultValue() + { + return _setupResultCode; + } + + public void ConfigureSetupValue() + { + int retry = 0; + + ResultCode result = ResultCode.Success; + + while (retry < 20) + { + result = (ResultCode)IRtcManager.GetExternalRtcValue(out ulong rtcValue); + + if (result == ResultCode.Success) + { + _setupValue = (long)rtcValue; + break; + } + + retry++; + } + + _setupResultCode = result; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs index 00f296ad..c98b0064 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs @@ -8,22 +8,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock private StandardNetworkSystemClockCore _networkSystemClockCore; private bool _autoCorrectionEnabled; - private static StandardUserSystemClockCore instance; + private static StandardUserSystemClockCore _instance; public static StandardUserSystemClockCore Instance { get { - if (instance == null) + if (_instance == null) { - instance = new StandardUserSystemClockCore(StandardLocalSystemClockCore.Instance, StandardNetworkSystemClockCore.Instance); + _instance = new StandardUserSystemClockCore(StandardLocalSystemClockCore.Instance, StandardNetworkSystemClockCore.Instance); } - return instance; + return _instance; } } - public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore) + public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore) : base(localSystemClockCore.GetSteadyClockCore()) { _localSystemClockCore = localSystemClockCore; _networkSystemClockCore = networkSystemClockCore; @@ -35,11 +35,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return ResultCode.NotImplemented; } - public override SteadyClockCore GetSteadyClockCore() - { - return _localSystemClockCore.GetSteadyClockCore(); - } - public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context) { ResultCode result = ApplyAutomaticCorrection(thread, false); diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs index e661ab53..54d9accf 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs @@ -1,54 +1,16 @@ using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Bpc; using Ryujinx.HLE.Utilities; using System; namespace Ryujinx.HLE.HOS.Services.Time.Clock { - class SteadyClockCore + abstract class SteadyClockCore { - private long _setupValue; - private ResultCode _setupResultCode; - private bool _isRtcResetDetected; - private TimeSpanType _testOffset; - private TimeSpanType _internalOffset; - private UInt128 _clockSourceId; + private UInt128 _clockSourceId; - private static SteadyClockCore instance; - - public static SteadyClockCore Instance + public SteadyClockCore() { - get - { - if (instance == null) - { - instance = new SteadyClockCore(); - } - - return instance; - } - } - - private SteadyClockCore() - { - _testOffset = new TimeSpanType(0); - _internalOffset = new TimeSpanType(0); - _clockSourceId = new UInt128(Guid.NewGuid().ToByteArray()); - } - - private SteadyClockTimePoint GetTimePoint(KThread thread) - { - SteadyClockTimePoint result = new SteadyClockTimePoint - { - TimePoint = 0, - ClockSourceId = _clockSourceId - }; - - TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0); - - result.TimePoint = _setupValue + ticksTimeSpan.ToSeconds(); - - return result; + _clockSourceId = new UInt128(Guid.NewGuid().ToByteArray()); } public UInt128 GetClockSourceId() @@ -56,76 +18,45 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return _clockSourceId; } + public virtual TimeSpanType GetTestOffset() + { + return new TimeSpanType(0); + } + + public virtual void SetTestOffset(TimeSpanType testOffset) {} + + public virtual ResultCode GetRtcValue(out ulong rtcValue) + { + rtcValue = 0; + + return ResultCode.NotImplemented; + } + + public virtual ResultCode GetSetupResultValue() + { + return ResultCode.NotImplemented; + } + + public virtual TimeSpanType GetInternalOffset() + { + return new TimeSpanType(0); + } + + public virtual void SetInternalOffset(TimeSpanType internalOffset) {} + + public virtual SteadyClockTimePoint GetTimePoint(KThread thread) + { + throw new NotImplementedException(); + } + public SteadyClockTimePoint GetCurrentTimePoint(KThread thread) { SteadyClockTimePoint result = GetTimePoint(thread); - result.TimePoint += _testOffset.ToSeconds(); - result.TimePoint += _internalOffset.ToSeconds(); + result.TimePoint += GetTestOffset().ToSeconds(); + result.TimePoint += GetInternalOffset().ToSeconds(); return result; } - - public TimeSpanType GetTestOffset() - { - return _testOffset; - } - - public void SetTestOffset(TimeSpanType testOffset) - { - _testOffset = testOffset; - } - - public ResultCode GetRtcValue(out ulong rtcValue) - { - return (ResultCode)IRtcManager.GetExternalRtcValue(out rtcValue); - } - - public bool IsRtcResetDetected() - { - return _isRtcResetDetected; - } - - public ResultCode GetSetupResultCode() - { - return _setupResultCode; - } - - public TimeSpanType GetInternalOffset() - { - return _internalOffset; - } - - public void SetInternalOffset(TimeSpanType internalOffset) - { - _internalOffset = internalOffset; - } - - public ResultCode GetSetupResultValue() - { - return _setupResultCode; - } - - public void ConfigureSetupValue() - { - int retry = 0; - - ResultCode result = ResultCode.Success; - - while (retry < 20) - { - result = (ResultCode)IRtcManager.GetExternalRtcValue(out ulong rtcValue); - - if (result == ResultCode.Success) - { - _setupValue = (long)rtcValue; - break; - } - - retry++; - } - - _setupResultCode = result; - } } } diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs index d3a056e4..52f3c908 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs @@ -4,11 +4,35 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { abstract class SystemClockCore { - public abstract SteadyClockCore GetSteadyClockCore(); + private SteadyClockCore _steadyClockCore; + private SystemClockContext _context; - public abstract ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context); + public SystemClockCore(SteadyClockCore steadyClockCore) + { + _steadyClockCore = steadyClockCore; + _context = new SystemClockContext(); - public abstract ResultCode SetSystemClockContext(SystemClockContext context); + _context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId(); + } + + public virtual SteadyClockCore GetSteadyClockCore() + { + return _steadyClockCore; + } + + public virtual ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context) + { + context = _context; + + return ResultCode.Success; + } + + public virtual ResultCode SetSystemClockContext(SystemClockContext context) + { + _context = context; + + return ResultCode.Success; + } public abstract ResultCode Flush(SystemClockContext context); diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs new file mode 100644 index 00000000..7a69b014 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs @@ -0,0 +1,40 @@ +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + class TickBasedSteadyClockCore : SteadyClockCore + { + private static TickBasedSteadyClockCore _instance; + + public static TickBasedSteadyClockCore Instance + { + get + { + if (_instance == null) + { + _instance = new TickBasedSteadyClockCore(); + } + + return _instance; + } + } + + private TickBasedSteadyClockCore() {} + + public override SteadyClockTimePoint GetTimePoint(KThread thread) + { + SteadyClockTimePoint result = new SteadyClockTimePoint + { + TimePoint = 0, + ClockSourceId = GetClockSourceId() + }; + + TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0); + + result.TimePoint = ticksTimeSpan.ToSeconds(); + + return result; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs index 8c5ae65c..9ee038d5 100644 --- a/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs +++ b/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs @@ -1,7 +1,13 @@ +using Ryujinx.Common; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Time.Clock; +using Ryujinx.HLE.HOS.Services.Time.TimeZone; using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Time { @@ -66,6 +72,15 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } + [Command(5)] // 4.0.0+ + // GetEphemeralNetworkSystemClock() -> object + public ResultCode GetEphemeralNetworkSystemClock(ServiceCtx context) + { + MakeObject(context, new ISystemClock(StandardNetworkSystemClockCore.Instance, false)); + + return ResultCode.Success; + } + [Command(20)] // 6.0.0+ // GetSharedMemoryNativeHandle() -> handle public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context) @@ -116,16 +131,202 @@ namespace Ryujinx.HLE.HOS.Services.Time } [Command(300)] // 4.0.0+ - // CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> u64 + // CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> s64 public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context) { - // TODO: reimplement this - long timeOffset = (long)(DateTime.UtcNow - StartupDate).TotalSeconds; - long systemClockContextEpoch = context.RequestData.ReadInt64(); + SystemClockContext otherContext = context.RequestData.ReadStruct(); + SteadyClockTimePoint currentTimePoint = StandardSteadyClockCore.Instance.GetCurrentTimePoint(context.Thread); - context.ResponseData.Write(timeOffset + systemClockContextEpoch); + ResultCode result = ResultCode.TimeMismatch; + + if (currentTimePoint.ClockSourceId == otherContext.SteadyTimePoint.ClockSourceId) + { + TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(context.Thread.Context.ThreadState.CntpctEl0, context.Thread.Context.ThreadState.CntfrqEl0); + long baseTimePoint = otherContext.Offset + currentTimePoint.TimePoint - ticksTimeSpan.ToSeconds(); + + context.ResponseData.Write(baseTimePoint); + + result = 0; + } + + return result; + } + + [Command(400)] // 4.0.0+ + // GetClockSnapshot(u8) -> buffer + public ResultCode GetClockSnapshot(ServiceCtx context) + { + byte type = context.RequestData.ReadByte(); + + ResultCode result = StandardUserSystemClockCore.Instance.GetSystemClockContext(context.Thread, out SystemClockContext userContext); + + if (result == ResultCode.Success) + { + result = StandardNetworkSystemClockCore.Instance.GetSystemClockContext(context.Thread, out SystemClockContext networkContext); + + if (result == ResultCode.Success) + { + result = GetClockSnapshotFromSystemClockContextInternal(context.Thread, userContext, networkContext, type, out ClockSnapshot clockSnapshot); + + if (result == ResultCode.Success) + { + WriteClockSnapshotFromBuffer(context, context.Request.RecvListBuff[0], clockSnapshot); + } + } + } + + return result; + } + + [Command(401)] // 4.0.0+ + // GetClockSnapshotFromSystemClockContext(u8, nn::time::SystemClockContext, nn::time::SystemClockContext) -> buffer + public ResultCode GetClockSnapshotFromSystemClockContext(ServiceCtx context) + { + byte type = context.RequestData.ReadByte(); + + context.RequestData.BaseStream.Position += 7; + + SystemClockContext userContext = context.RequestData.ReadStruct(); + SystemClockContext networkContext = context.RequestData.ReadStruct(); + + ResultCode result = GetClockSnapshotFromSystemClockContextInternal(context.Thread, userContext, networkContext, type, out ClockSnapshot clockSnapshot); + + if (result == ResultCode.Success) + { + WriteClockSnapshotFromBuffer(context, context.Request.RecvListBuff[0], clockSnapshot); + } + + return result; + } + + [Command(500)] // 4.0.0+ + // CalculateStandardUserSystemClockDifferenceByUser(buffer, buffer) -> nn::TimeSpanType + public ResultCode CalculateStandardUserSystemClockDifferenceByUser(ServiceCtx context) + { + + ClockSnapshot clockSnapshotA = ReadClockSnapshotFromBuffer(context, context.Request.ExchangeBuff[0]); + ClockSnapshot clockSnapshotB = ReadClockSnapshotFromBuffer(context, context.Request.ExchangeBuff[1]); + TimeSpanType difference = TimeSpanType.FromSeconds(clockSnapshotB.UserContext.Offset - clockSnapshotA.UserContext.Offset); + + if (clockSnapshotB.UserContext.SteadyTimePoint.ClockSourceId != clockSnapshotA.UserContext.SteadyTimePoint.ClockSourceId || (clockSnapshotB.IsAutomaticCorrectionEnabled && clockSnapshotA.IsAutomaticCorrectionEnabled)) + { + difference = new TimeSpanType(0); + } + + context.ResponseData.Write(difference.NanoSeconds); return ResultCode.Success; } + + [Command(501)] // 4.0.0+ + // CalculateSpanBetween(buffer, buffer) -> nn::TimeSpanType + public ResultCode CalculateSpanBetween(ServiceCtx context) + { + ClockSnapshot clockSnapshotA = ReadClockSnapshotFromBuffer(context, context.Request.ExchangeBuff[0]); + ClockSnapshot clockSnapshotB = ReadClockSnapshotFromBuffer(context, context.Request.ExchangeBuff[1]); + + TimeSpanType result; + + ResultCode resultCode = clockSnapshotA.SteadyClockTimePoint.GetSpanBetween(clockSnapshotB.SteadyClockTimePoint, out long timeSpan); + + if (resultCode != ResultCode.Success) + { + resultCode = ResultCode.TimeNotFound; + + if (clockSnapshotA.NetworkTime != 0 && clockSnapshotB.NetworkTime != 0) + { + result = TimeSpanType.FromSeconds(clockSnapshotB.NetworkTime - clockSnapshotA.NetworkTime); + resultCode = ResultCode.Success; + } + else + { + return resultCode; + } + } + else + { + result = TimeSpanType.FromSeconds(timeSpan); + } + + context.ResponseData.Write(result.NanoSeconds); + + return resultCode; + } + + private ResultCode GetClockSnapshotFromSystemClockContextInternal(KThread thread, SystemClockContext userContext, SystemClockContext networkContext, byte type, out ClockSnapshot clockSnapshot) + { + clockSnapshot = new ClockSnapshot(); + + SteadyClockCore steadyClockCore = StandardSteadyClockCore.Instance; + SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(thread); + + clockSnapshot.IsAutomaticCorrectionEnabled = StandardUserSystemClockCore.Instance.IsAutomaticCorrectionEnabled(); + clockSnapshot.UserContext = userContext; + clockSnapshot.NetworkContext = networkContext; + + char[] tzName = TimeZoneManager.Instance.GetDeviceLocationName().ToCharArray(); + char[] locationName = new char[0x24]; + + Array.Copy(tzName, locationName, tzName.Length); + + clockSnapshot.LocationName = locationName; + + ResultCode result = ClockSnapshot.GetCurrentTime(out clockSnapshot.UserTime, currentTimePoint, clockSnapshot.UserContext); + + if (result == ResultCode.Success) + { + result = TimeZoneManager.Instance.ToCalendarTimeWithMyRules(clockSnapshot.UserTime, out CalendarInfo userCalendarInfo); + + if (result == ResultCode.Success) + { + clockSnapshot.UserCalendarTime = userCalendarInfo.Time; + clockSnapshot.UserCalendarAdditionalTime = userCalendarInfo.AdditionalInfo; + + if (ClockSnapshot.GetCurrentTime(out clockSnapshot.NetworkTime, currentTimePoint, clockSnapshot.NetworkContext) != ResultCode.Success) + { + clockSnapshot.NetworkTime = 0; + } + + result = TimeZoneManager.Instance.ToCalendarTimeWithMyRules(clockSnapshot.NetworkTime, out CalendarInfo networkCalendarInfo); + + if (result == ResultCode.Success) + { + clockSnapshot.NetworkCalendarTime = networkCalendarInfo.Time; + clockSnapshot.NetworkCalendarAdditionalTime = networkCalendarInfo.AdditionalInfo; + clockSnapshot.Type = type; + + // Probably a version field? + clockSnapshot.Unknown = 0; + } + } + } + + return result; + } + + private ClockSnapshot ReadClockSnapshotFromBuffer(ServiceCtx context, IpcBuffDesc ipcDesc) + { + Debug.Assert(ipcDesc.Size == Marshal.SizeOf()); + + using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(context.Memory.ReadBytes(ipcDesc.Position, ipcDesc.Size)))) + { + return bufferReader.ReadStruct(); + } + } + + private void WriteClockSnapshotFromBuffer(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, ClockSnapshot clockSnapshot) + { + Debug.Assert(ipcDesc.Size == Marshal.SizeOf()); + + MemoryStream memory = new MemoryStream((int)ipcDesc.Size); + + using (BinaryWriter bufferWriter = new BinaryWriter(memory)) + { + bufferWriter.WriteStruct(clockSnapshot); + } + + context.Memory.WriteBytes(ipcDesc.Position, memory.ToArray()); + memory.Dispose(); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs b/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs index 2772b45d..7e3edcef 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs @@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint public ResultCode GetCurrentTimePoint(ServiceCtx context) { - SteadyClockTimePoint currentTimePoint = SteadyClockCore.Instance.GetCurrentTimePoint(context.Thread); + SteadyClockTimePoint currentTimePoint = StandardSteadyClockCore.Instance.GetCurrentTimePoint(context.Thread); context.ResponseData.WriteStruct(currentTimePoint); @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // GetTestOffset() -> nn::TimeSpanType public ResultCode GetTestOffset(ServiceCtx context) { - context.ResponseData.WriteStruct(SteadyClockCore.Instance.GetTestOffset()); + context.ResponseData.WriteStruct(StandardSteadyClockCore.Instance.GetTestOffset()); return ResultCode.Success; } @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { TimeSpanType testOffset = context.RequestData.ReadStruct(); - SteadyClockCore.Instance.SetTestOffset(testOffset); + StandardSteadyClockCore.Instance.SetTestOffset(testOffset); return 0; } @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // GetRtcValue() -> u64 public ResultCode GetRtcValue(ServiceCtx context) { - ResultCode result = SteadyClockCore.Instance.GetRtcValue(out ulong rtcValue); + ResultCode result = StandardSteadyClockCore.Instance.GetRtcValue(out ulong rtcValue); if (result == ResultCode.Success) { @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // IsRtcResetDetected() -> bool public ResultCode IsRtcResetDetected(ServiceCtx context) { - context.ResponseData.Write(SteadyClockCore.Instance.IsRtcResetDetected()); + context.ResponseData.Write(StandardSteadyClockCore.Instance.IsRtcResetDetected()); return ResultCode.Success; } @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // GetSetupResultValue() -> u32 public ResultCode GetSetupResultValue(ServiceCtx context) { - context.ResponseData.Write((uint)SteadyClockCore.Instance.GetSetupResultCode()); + context.ResponseData.Write((uint)StandardSteadyClockCore.Instance.GetSetupResultValue()); return ResultCode.Success; } @@ -72,7 +72,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // GetInternalOffset() -> nn::TimeSpanType public ResultCode GetInternalOffset(ServiceCtx context) { - context.ResponseData.WriteStruct(SteadyClockCore.Instance.GetInternalOffset()); + context.ResponseData.WriteStruct(StandardSteadyClockCore.Instance.GetInternalOffset()); return ResultCode.Success; } @@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { TimeSpanType internalOffset = context.RequestData.ReadStruct(); - SteadyClockCore.Instance.SetInternalOffset(internalOffset); + StandardSteadyClockCore.Instance.SetInternalOffset(internalOffset); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs index 74182428..895bb1f3 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs @@ -56,7 +56,6 @@ namespace Ryujinx.HLE.HOS.Services.Time // LoadLocationNameList(u32 index) -> (u32 outCount, buffer) public ResultCode LoadLocationNameList(ServiceCtx context) { - // TODO: fix logic to use index uint index = context.RequestData.ReadUInt32(); long bufferPosition = context.Request.ReceiveBuff[0].Position; long bufferSize = context.Request.ReceiveBuff[0].Size;