From 97d0c6242368f443c50395b2fa9d99a59f1df1e8 Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Sun, 14 Jul 2019 22:50:11 +0200 Subject: [PATCH] Accurately implement steady & system clocks (#732) * Improve SteadyClock implementation accuracy * Rewrite system clocks to be accurate * Implement IStaticService 100 & 101 * Add time:* permissions * Address comments * Realign TimePermissions definitions * Address gdk's comments * Fix after rebase --- Ryujinx.HLE/HOS/Horizon.cs | 6 + .../HOS/Services/Time/Clock/ClockTypes.cs | 40 +++++++ .../Clock/StandardLocalSystemClockCore.cs | 59 ++++++++++ .../Clock/StandardNetworkSystemClockCore.cs | 59 ++++++++++ .../Time/Clock/StandardUserSystemClockCore.cs | 96 ++++++++++++++++ .../Services/Time/Clock/SteadyClockCore.cs | 86 ++++++++++++++ .../Services/Time/Clock/SystemClockCore.cs | 31 +++++ .../HOS/Services/Time/IStaticService.cs | 44 +++++-- Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs | 43 ++++--- Ryujinx.HLE/HOS/Services/Time/ISystemClock.cs | 108 ++++++++++-------- Ryujinx.HLE/HOS/Services/Time/ResultCode.cs | 5 +- .../HOS/Services/Time/SystemClockType.cs | 10 -- .../HOS/Services/Time/TimePermissions.cs | 17 +++ 13 files changed, 522 insertions(+), 82 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs create mode 100644 Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Time/SystemClockType.cs create mode 100644 Ryujinx.HLE/HOS/Services/Time/TimePermissions.cs diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 496fbdbe4..7523d2bee 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Sm; +using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; @@ -195,6 +196,11 @@ namespace Ryujinx.HLE.HOS LoadKeySet(); ContentManager = new ContentManager(device); + + // NOTE: Now we set the default internal offset of the steady clock like Nintendo does... even if it's strange this is accurate. + // TODO: use bpc:r and set:sys (and set external clock source id from settings) + DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + SteadyClockCore.Instance.SetInternalOffset(new TimeSpanType(((ulong)(DateTime.Now.ToUniversalTime() - UnixEpoch).TotalSeconds) * 1000000000)); } public void LoadCart(string exeFsDir, string romFsFile = null) diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs new file mode 100644 index 000000000..1e527c002 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs @@ -0,0 +1,40 @@ +using Ryujinx.HLE.Utilities; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + [StructLayout(LayoutKind.Sequential)] + struct TimeSpanType + { + public ulong NanoSeconds; + + public TimeSpanType(ulong nanoSeconds) + { + NanoSeconds = nanoSeconds; + } + + public ulong ToSeconds() + { + return NanoSeconds / 1000000000; + } + + public static TimeSpanType FromTicks(ulong ticks, ulong frequency) + { + return new TimeSpanType(ticks * 1000000000 / frequency); + } + } + + [StructLayout(LayoutKind.Sequential)] + struct SteadyClockTimePoint + { + public ulong TimePoint; + public UInt128 ClockSourceId; + } + + [StructLayout(LayoutKind.Sequential)] + struct SystemClockContext + { + public ulong Offset; + public SteadyClockTimePoint SteadyTimePoint; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs new file mode 100644 index 000000000..16550199b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs @@ -0,0 +1,59 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + class StandardLocalSystemClockCore : SystemClockCore + { + private SteadyClockCore _steadyClockCore; + private SystemClockContext _context; + + private static StandardLocalSystemClockCore instance; + + public static StandardLocalSystemClockCore Instance + { + get + { + if (instance == null) + { + instance = new StandardLocalSystemClockCore(SteadyClockCore.Instance); + } + + return instance; + } + } + + public StandardLocalSystemClockCore(SteadyClockCore steadyClockCore) + { + _steadyClockCore = steadyClockCore; + _context = new SystemClockContext(); + + _context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId(); + } + + public override ResultCode Flush(SystemClockContext context) + { + // TODO: set:sys SetUserSystemClockContext + + 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 new file mode 100644 index 000000000..59bc822ce --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs @@ -0,0 +1,59 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + class StandardNetworkSystemClockCore : SystemClockCore + { + private SteadyClockCore _steadyClockCore; + private SystemClockContext _context; + + private static StandardNetworkSystemClockCore instance; + + public static StandardNetworkSystemClockCore Instance + { + get + { + if (instance == null) + { + instance = new StandardNetworkSystemClockCore(SteadyClockCore.Instance); + } + + return instance; + } + } + + public StandardNetworkSystemClockCore(SteadyClockCore steadyClockCore) + { + _steadyClockCore = steadyClockCore; + _context = new SystemClockContext(); + + _context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId(); + } + + public override ResultCode Flush(SystemClockContext context) + { + // TODO: set:sys SetNetworkSystemClockContext + + 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/StandardUserSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs new file mode 100644 index 000000000..00f296ad0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs @@ -0,0 +1,96 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + class StandardUserSystemClockCore : SystemClockCore + { + private StandardLocalSystemClockCore _localSystemClockCore; + private StandardNetworkSystemClockCore _networkSystemClockCore; + private bool _autoCorrectionEnabled; + + private static StandardUserSystemClockCore instance; + + public static StandardUserSystemClockCore Instance + { + get + { + if (instance == null) + { + instance = new StandardUserSystemClockCore(StandardLocalSystemClockCore.Instance, StandardNetworkSystemClockCore.Instance); + } + + return instance; + } + } + + public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore) + { + _localSystemClockCore = localSystemClockCore; + _networkSystemClockCore = networkSystemClockCore; + _autoCorrectionEnabled = false; + } + + public override ResultCode Flush(SystemClockContext context) + { + return ResultCode.NotImplemented; + } + + public override SteadyClockCore GetSteadyClockCore() + { + return _localSystemClockCore.GetSteadyClockCore(); + } + + public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context) + { + ResultCode result = ApplyAutomaticCorrection(thread, false); + + context = new SystemClockContext(); + + if (result == ResultCode.Success) + { + return _localSystemClockCore.GetSystemClockContext(thread, out context); + } + + return result; + } + + public override ResultCode SetSystemClockContext(SystemClockContext context) + { + return ResultCode.NotImplemented; + } + + private ResultCode ApplyAutomaticCorrection(KThread thread, bool autoCorrectionEnabled) + { + ResultCode result = ResultCode.Success; + + if (_autoCorrectionEnabled != autoCorrectionEnabled && _networkSystemClockCore.IsClockSetup(thread)) + { + result = _networkSystemClockCore.GetSystemClockContext(thread, out SystemClockContext context); + + if (result == ResultCode.Success) + { + _localSystemClockCore.SetSystemClockContext(context); + } + } + + return result; + } + + public ResultCode SetAutomaticCorrectionEnabled(KThread thread, bool autoCorrectionEnabled) + { + ResultCode result = ApplyAutomaticCorrection(thread, autoCorrectionEnabled); + + if (result == ResultCode.Success) + { + _autoCorrectionEnabled = autoCorrectionEnabled; + } + + return result; + } + + public bool IsAutomaticCorrectionEnabled() + { + return _autoCorrectionEnabled; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs new file mode 100644 index 000000000..b69b7d3cb --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs @@ -0,0 +1,86 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.Utilities; +using System; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + class SteadyClockCore + { + private TimeSpanType _testOffset; + private TimeSpanType _internalOffset; + private UInt128 _clockSourceId; + + private static SteadyClockCore instance; + + public static SteadyClockCore Instance + { + 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 = _internalOffset.ToSeconds() + ticksTimeSpan.ToSeconds(); + + return result; + } + + public UInt128 GetClockSourceId() + { + return _clockSourceId; + } + + public SteadyClockTimePoint GetCurrentTimePoint(KThread thread) + { + SteadyClockTimePoint result = GetTimePoint(thread); + + result.TimePoint += _testOffset.ToSeconds(); + + return result; + } + + public TimeSpanType GetTestOffset() + { + return _testOffset; + } + + public void SetTestOffset(TimeSpanType testOffset) + { + _testOffset = testOffset; + } + + // TODO: check if this is accurate + public TimeSpanType GetInternalOffset() + { + return _internalOffset; + } + + // TODO: check if this is accurate + public void SetInternalOffset(TimeSpanType internalOffset) + { + _internalOffset = internalOffset; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs new file mode 100644 index 000000000..d3a056e43 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs @@ -0,0 +1,31 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock +{ + abstract class SystemClockCore + { + public abstract SteadyClockCore GetSteadyClockCore(); + + public abstract ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context); + + public abstract ResultCode SetSystemClockContext(SystemClockContext context); + + public abstract ResultCode Flush(SystemClockContext context); + + public bool IsClockSetup(KThread thread) + { + ResultCode result = GetSystemClockContext(thread, out SystemClockContext context); + + if (result == ResultCode.Success) + { + SteadyClockCore steadyClockCore = GetSteadyClockCore(); + + SteadyClockTimePoint steadyClockTimePoint = steadyClockCore.GetCurrentTimePoint(thread); + + return steadyClockTimePoint.ClockSourceId == context.SteadyTimePoint.ClockSourceId; + } + + return false; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs index 420e69128..10c659c5c 100644 --- a/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs +++ b/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs @@ -1,25 +1,31 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Services.Time.Clock; using System; namespace Ryujinx.HLE.HOS.Services.Time { - [Service("time:a")] - [Service("time:s")] - [Service("time:u")] + [Service("time:a", TimePermissions.Applet)] + [Service("time:s", TimePermissions.System)] + [Service("time:u", TimePermissions.User)] class IStaticService : IpcService { + private TimePermissions _permissions; + private int _timeSharedMemoryNativeHandle = 0; private static readonly DateTime StartupDate = DateTime.UtcNow; - public IStaticService(ServiceCtx context) { } + public IStaticService(ServiceCtx context, TimePermissions permissions) + { + _permissions = permissions; + } [Command(0)] // GetStandardUserSystemClock() -> object public ResultCode GetStandardUserSystemClock(ServiceCtx context) { - MakeObject(context, new ISystemClock(SystemClockType.User)); + MakeObject(context, new ISystemClock(StandardUserSystemClockCore.Instance, (_permissions & TimePermissions.UserSystemClockWritableMask) != 0)); return ResultCode.Success; } @@ -28,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // GetStandardNetworkSystemClock() -> object public ResultCode GetStandardNetworkSystemClock(ServiceCtx context) { - MakeObject(context, new ISystemClock(SystemClockType.Network)); + MakeObject(context, new ISystemClock(StandardNetworkSystemClockCore.Instance, (_permissions & TimePermissions.NetworkSystemClockWritableMask) != 0)); return ResultCode.Success; } @@ -55,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // GetStandardLocalSystemClock() -> object public ResultCode GetStandardLocalSystemClock(ServiceCtx context) { - MakeObject(context, new ISystemClock(SystemClockType.Local)); + MakeObject(context, new ISystemClock(StandardLocalSystemClockCore.Instance, (_permissions & TimePermissions.LocalSystemClockWritableMask) != 0)); return ResultCode.Success; } @@ -77,10 +83,34 @@ namespace Ryujinx.HLE.HOS.Services.Time return ResultCode.Success; } + [Command(100)] + // IsStandardUserSystemClockAutomaticCorrectionEnabled() -> bool + public ResultCode IsStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) + { + context.ResponseData.Write(StandardUserSystemClockCore.Instance.IsAutomaticCorrectionEnabled()); + + return ResultCode.Success; + } + + [Command(101)] + // SetStandardUserSystemClockAutomaticCorrectionEnabled(b8) + public ResultCode SetStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) + { + if ((_permissions & TimePermissions.UserSystemClockWritableMask) == 0) + { + return ResultCode.PermissionDenied; + } + + bool autoCorrectionEnabled = context.RequestData.ReadBoolean(); + + return StandardUserSystemClockCore.Instance.SetAutomaticCorrectionEnabled(context.Thread, autoCorrectionEnabled); + } + [Command(300)] // 4.0.0+ // CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> u64 public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context) { + // TODO: reimplement this long timeOffset = (long)(DateTime.UtcNow - StartupDate).TotalSeconds; long systemClockContextEpoch = context.RequestData.ReadInt64(); diff --git a/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs b/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs index ccf19d7d5..d9f05f29c 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs @@ -1,26 +1,17 @@ -using System; +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Services.Time.Clock; namespace Ryujinx.HLE.HOS.Services.Time { class ISteadyClock : IpcService { - private ulong _testOffset; - - public ISteadyClock() - { - _testOffset = 0; - } - [Command(0)] // GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint public ResultCode GetCurrentTimePoint(ServiceCtx context) { - context.ResponseData.Write((long)(System.Diagnostics.Process.GetCurrentProcess().StartTime - DateTime.Now).TotalSeconds); + SteadyClockTimePoint currentTimePoint = SteadyClockCore.Instance.GetCurrentTimePoint(context.Thread); - for (int i = 0; i < 0x10; i++) - { - context.ResponseData.Write((byte)0); - } + context.ResponseData.WriteStruct(currentTimePoint); return ResultCode.Success; } @@ -29,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Time // GetTestOffset() -> nn::TimeSpanType public ResultCode GetTestOffset(ServiceCtx context) { - context.ResponseData.Write(_testOffset); + context.ResponseData.WriteStruct(SteadyClockCore.Instance.GetTestOffset()); return ResultCode.Success; } @@ -38,7 +29,29 @@ namespace Ryujinx.HLE.HOS.Services.Time // SetTestOffset(nn::TimeSpanType) public ResultCode SetTestOffset(ServiceCtx context) { - _testOffset = context.RequestData.ReadUInt64(); + TimeSpanType testOffset = context.RequestData.ReadStruct(); + + SteadyClockCore.Instance.SetTestOffset(testOffset); + + return 0; + } + + [Command(200)] // 3.0.0+ + // GetInternalOffset() -> nn::TimeSpanType + public ResultCode GetInternalOffset(ServiceCtx context) + { + context.ResponseData.WriteStruct(SteadyClockCore.Instance.GetInternalOffset()); + + return ResultCode.Success; + } + + [Command(201)] // 3.0.0-3.0.2 + // SetInternalOffset(nn::TimeSpanType) + public ResultCode SetInternalOffset(ServiceCtx context) + { + TimeSpanType internalOffset = context.RequestData.ReadStruct(); + + SteadyClockCore.Instance.SetInternalOffset(internalOffset); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Time/ISystemClock.cs b/Ryujinx.HLE/HOS/Services/Time/ISystemClock.cs index 0ed34d179..ddd67a421 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ISystemClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ISystemClock.cs @@ -1,97 +1,107 @@ -using System; +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Services.Time.Clock; namespace Ryujinx.HLE.HOS.Services.Time { class ISystemClock : IpcService { - private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private SystemClockCore _clockCore; + private bool _writePermission; - private SystemClockType _clockType; - private DateTime _systemClockContextEpoch; - private long _systemClockTimePoint; - private byte[] _systemClockContextEnding; - private long _timeOffset; - - public ISystemClock(SystemClockType clockType) + public ISystemClock(SystemClockCore clockCore, bool writePermission) { - _clockType = clockType; - _systemClockContextEpoch = System.Diagnostics.Process.GetCurrentProcess().StartTime; - _systemClockContextEnding = new byte[0x10]; - _timeOffset = 0; - - if (clockType == SystemClockType.User || - clockType == SystemClockType.Network) - { - _systemClockContextEpoch = _systemClockContextEpoch.ToUniversalTime(); - } - - _systemClockTimePoint = (long)(_systemClockContextEpoch - Epoch).TotalSeconds; + _clockCore = clockCore; + _writePermission = writePermission; } [Command(0)] // GetCurrentTime() -> nn::time::PosixTime public ResultCode GetCurrentTime(ServiceCtx context) { - DateTime currentTime = DateTime.Now; + SteadyClockCore steadyClockCore = _clockCore.GetSteadyClockCore(); + SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(context.Thread); - if (_clockType == SystemClockType.User || - _clockType == SystemClockType.Network) + ResultCode result = _clockCore.GetSystemClockContext(context.Thread, out SystemClockContext clockContext); + + if (result == ResultCode.Success) { - currentTime = currentTime.ToUniversalTime(); + result = ResultCode.TimeMismatch; + + if (currentTimePoint.ClockSourceId == clockContext.SteadyTimePoint.ClockSourceId) + { + ulong posixTime = clockContext.Offset + currentTimePoint.TimePoint; + + context.ResponseData.Write(posixTime); + + result = 0; + } } - context.ResponseData.Write((long)((currentTime - Epoch).TotalSeconds) + _timeOffset); - - return ResultCode.Success; + return result; } [Command(1)] // SetCurrentTime(nn::time::PosixTime) public ResultCode SetCurrentTime(ServiceCtx context) { - DateTime currentTime = DateTime.Now; - - if (_clockType == SystemClockType.User || - _clockType == SystemClockType.Network) + if (!_writePermission) { - currentTime = currentTime.ToUniversalTime(); + return ResultCode.PermissionDenied; } - _timeOffset = (context.RequestData.ReadInt64() - (long)(currentTime - Epoch).TotalSeconds); + ulong posixTime = context.RequestData.ReadUInt64(); + SteadyClockCore steadyClockCore = _clockCore.GetSteadyClockCore(); + SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(context.Thread); - return ResultCode.Success; + SystemClockContext clockContext = new SystemClockContext() + { + Offset = posixTime - currentTimePoint.TimePoint, + SteadyTimePoint = currentTimePoint + }; + + ResultCode result = _clockCore.SetSystemClockContext(clockContext); + + if (result == ResultCode.Success) + { + result = _clockCore.Flush(clockContext); + } + + return result; } [Command(2)] // GetSystemClockContext() -> nn::time::SystemClockContext public ResultCode GetSystemClockContext(ServiceCtx context) { - context.ResponseData.Write((long)(_systemClockContextEpoch - Epoch).TotalSeconds); + ResultCode result = _clockCore.GetSystemClockContext(context.Thread, out SystemClockContext clockContext); - // The point in time, TODO: is there a link between epoch and this? - context.ResponseData.Write(_systemClockTimePoint); - - // This seems to be some kind of identifier? - for (int i = 0; i < 0x10; i++) + if (result == ResultCode.Success) { - context.ResponseData.Write(_systemClockContextEnding[i]); + context.ResponseData.WriteStruct(clockContext); } - return ResultCode.Success; + return result; } [Command(3)] // SetSystemClockContext(nn::time::SystemClockContext) public ResultCode SetSystemClockContext(ServiceCtx context) { - long newSystemClockEpoch = context.RequestData.ReadInt64(); - long newSystemClockTimePoint = context.RequestData.ReadInt64(); + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } - _systemClockContextEpoch = Epoch.Add(TimeSpan.FromSeconds(newSystemClockEpoch)); - _systemClockTimePoint = newSystemClockTimePoint; - _systemClockContextEnding = context.RequestData.ReadBytes(0x10); + SystemClockContext clockContext = context.RequestData.ReadStruct(); - return ResultCode.Success; + ResultCode result = _clockCore.SetSystemClockContext(clockContext); + + if (result == ResultCode.Success) + { + result = _clockCore.Flush(clockContext); + } + + return result; } } } \ 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 5868a458f..ed7130f36 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs @@ -7,11 +7,14 @@ Success = 0, + PermissionDenied = (1 << ErrorCodeShift) | ModuleId, + TimeMismatch = (102 << 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 + TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId, + NotImplemented = (990 << ErrorCodeShift) | ModuleId, } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Time/SystemClockType.cs b/Ryujinx.HLE/HOS/Services/Time/SystemClockType.cs deleted file mode 100644 index 54b7df3f8..000000000 --- a/Ryujinx.HLE/HOS/Services/Time/SystemClockType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Time -{ - enum SystemClockType - { - User, - Network, - Local, - EphemeralNetwork - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Time/TimePermissions.cs b/Ryujinx.HLE/HOS/Services/Time/TimePermissions.cs new file mode 100644 index 000000000..823c82888 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/TimePermissions.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Time +{ + [Flags] + enum TimePermissions + { + LocalSystemClockWritableMask = 0x1, + UserSystemClockWritableMask = 0x2, + NetworkSystemClockWritableMask = 0x4, + UnknownPermissionMask = 0x8, + + User = 0, + Applet = LocalSystemClockWritableMask | UserSystemClockWritableMask | UnknownPermissionMask, + System = NetworkSystemClockWritableMask + } +}