forked from Mirror/Ryujinx
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
This commit is contained in:
parent
4ad3936afd
commit
97d0c62423
13 changed files with 522 additions and 82 deletions
|
@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Sm;
|
using Ryujinx.HLE.HOS.Services.Sm;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
|
@ -195,6 +196,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
LoadKeySet();
|
LoadKeySet();
|
||||||
|
|
||||||
ContentManager = new ContentManager(device);
|
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)
|
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||||
|
|
40
Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs
Normal file
40
Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
Normal file
86
Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs
Normal file
31
Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,31 @@
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time
|
namespace Ryujinx.HLE.HOS.Services.Time
|
||||||
{
|
{
|
||||||
[Service("time:a")]
|
[Service("time:a", TimePermissions.Applet)]
|
||||||
[Service("time:s")]
|
[Service("time:s", TimePermissions.System)]
|
||||||
[Service("time:u")]
|
[Service("time:u", TimePermissions.User)]
|
||||||
class IStaticService : IpcService
|
class IStaticService : IpcService
|
||||||
{
|
{
|
||||||
|
private TimePermissions _permissions;
|
||||||
|
|
||||||
private int _timeSharedMemoryNativeHandle = 0;
|
private int _timeSharedMemoryNativeHandle = 0;
|
||||||
|
|
||||||
private static readonly DateTime StartupDate = DateTime.UtcNow;
|
private static readonly DateTime StartupDate = DateTime.UtcNow;
|
||||||
|
|
||||||
public IStaticService(ServiceCtx context) { }
|
public IStaticService(ServiceCtx context, TimePermissions permissions)
|
||||||
|
{
|
||||||
|
_permissions = permissions;
|
||||||
|
}
|
||||||
|
|
||||||
[Command(0)]
|
[Command(0)]
|
||||||
// GetStandardUserSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
|
// GetStandardUserSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
|
||||||
public ResultCode GetStandardUserSystemClock(ServiceCtx context)
|
public ResultCode GetStandardUserSystemClock(ServiceCtx context)
|
||||||
{
|
{
|
||||||
MakeObject(context, new ISystemClock(SystemClockType.User));
|
MakeObject(context, new ISystemClock(StandardUserSystemClockCore.Instance, (_permissions & TimePermissions.UserSystemClockWritableMask) != 0));
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
||||||
// GetStandardNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
|
// GetStandardNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
|
||||||
public ResultCode GetStandardNetworkSystemClock(ServiceCtx context)
|
public ResultCode GetStandardNetworkSystemClock(ServiceCtx context)
|
||||||
{
|
{
|
||||||
MakeObject(context, new ISystemClock(SystemClockType.Network));
|
MakeObject(context, new ISystemClock(StandardNetworkSystemClockCore.Instance, (_permissions & TimePermissions.NetworkSystemClockWritableMask) != 0));
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
||||||
// GetStandardLocalSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
|
// GetStandardLocalSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
|
||||||
public ResultCode GetStandardLocalSystemClock(ServiceCtx context)
|
public ResultCode GetStandardLocalSystemClock(ServiceCtx context)
|
||||||
{
|
{
|
||||||
MakeObject(context, new ISystemClock(SystemClockType.Local));
|
MakeObject(context, new ISystemClock(StandardLocalSystemClockCore.Instance, (_permissions & TimePermissions.LocalSystemClockWritableMask) != 0));
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -77,10 +83,34 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
||||||
return ResultCode.Success;
|
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+
|
[Command(300)] // 4.0.0+
|
||||||
// CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> u64
|
// CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> u64
|
||||||
public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context)
|
public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context)
|
||||||
{
|
{
|
||||||
|
// TODO: reimplement this
|
||||||
long timeOffset = (long)(DateTime.UtcNow - StartupDate).TotalSeconds;
|
long timeOffset = (long)(DateTime.UtcNow - StartupDate).TotalSeconds;
|
||||||
long systemClockContextEpoch = context.RequestData.ReadInt64();
|
long systemClockContextEpoch = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,17 @@
|
||||||
using System;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time
|
namespace Ryujinx.HLE.HOS.Services.Time
|
||||||
{
|
{
|
||||||
class ISteadyClock : IpcService
|
class ISteadyClock : IpcService
|
||||||
{
|
{
|
||||||
private ulong _testOffset;
|
|
||||||
|
|
||||||
public ISteadyClock()
|
|
||||||
{
|
|
||||||
_testOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command(0)]
|
[Command(0)]
|
||||||
// GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint
|
// GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint
|
||||||
public ResultCode GetCurrentTimePoint(ServiceCtx context)
|
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.WriteStruct(currentTimePoint);
|
||||||
{
|
|
||||||
context.ResponseData.Write((byte)0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
||||||
// GetTestOffset() -> nn::TimeSpanType
|
// GetTestOffset() -> nn::TimeSpanType
|
||||||
public ResultCode GetTestOffset(ServiceCtx context)
|
public ResultCode GetTestOffset(ServiceCtx context)
|
||||||
{
|
{
|
||||||
context.ResponseData.Write(_testOffset);
|
context.ResponseData.WriteStruct(SteadyClockCore.Instance.GetTestOffset());
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +29,29 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
||||||
// SetTestOffset(nn::TimeSpanType)
|
// SetTestOffset(nn::TimeSpanType)
|
||||||
public ResultCode SetTestOffset(ServiceCtx context)
|
public ResultCode SetTestOffset(ServiceCtx context)
|
||||||
{
|
{
|
||||||
_testOffset = context.RequestData.ReadUInt64();
|
TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>();
|
||||||
|
|
||||||
|
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<TimeSpanType>();
|
||||||
|
|
||||||
|
SteadyClockCore.Instance.SetInternalOffset(internalOffset);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,97 +1,107 @@
|
||||||
using System;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time
|
namespace Ryujinx.HLE.HOS.Services.Time
|
||||||
{
|
{
|
||||||
class ISystemClock : IpcService
|
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;
|
public ISystemClock(SystemClockCore clockCore, bool writePermission)
|
||||||
private DateTime _systemClockContextEpoch;
|
|
||||||
private long _systemClockTimePoint;
|
|
||||||
private byte[] _systemClockContextEnding;
|
|
||||||
private long _timeOffset;
|
|
||||||
|
|
||||||
public ISystemClock(SystemClockType clockType)
|
|
||||||
{
|
{
|
||||||
_clockType = clockType;
|
_clockCore = clockCore;
|
||||||
_systemClockContextEpoch = System.Diagnostics.Process.GetCurrentProcess().StartTime;
|
_writePermission = writePermission;
|
||||||
_systemClockContextEnding = new byte[0x10];
|
|
||||||
_timeOffset = 0;
|
|
||||||
|
|
||||||
if (clockType == SystemClockType.User ||
|
|
||||||
clockType == SystemClockType.Network)
|
|
||||||
{
|
|
||||||
_systemClockContextEpoch = _systemClockContextEpoch.ToUniversalTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
_systemClockTimePoint = (long)(_systemClockContextEpoch - Epoch).TotalSeconds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(0)]
|
[Command(0)]
|
||||||
// GetCurrentTime() -> nn::time::PosixTime
|
// GetCurrentTime() -> nn::time::PosixTime
|
||||||
public ResultCode GetCurrentTime(ServiceCtx context)
|
public ResultCode GetCurrentTime(ServiceCtx context)
|
||||||
{
|
{
|
||||||
DateTime currentTime = DateTime.Now;
|
SteadyClockCore steadyClockCore = _clockCore.GetSteadyClockCore();
|
||||||
|
SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(context.Thread);
|
||||||
|
|
||||||
if (_clockType == SystemClockType.User ||
|
ResultCode result = _clockCore.GetSystemClockContext(context.Thread, out SystemClockContext clockContext);
|
||||||
_clockType == SystemClockType.Network)
|
|
||||||
|
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 result;
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(1)]
|
[Command(1)]
|
||||||
// SetCurrentTime(nn::time::PosixTime)
|
// SetCurrentTime(nn::time::PosixTime)
|
||||||
public ResultCode SetCurrentTime(ServiceCtx context)
|
public ResultCode SetCurrentTime(ServiceCtx context)
|
||||||
{
|
{
|
||||||
DateTime currentTime = DateTime.Now;
|
if (!_writePermission)
|
||||||
|
|
||||||
if (_clockType == SystemClockType.User ||
|
|
||||||
_clockType == SystemClockType.Network)
|
|
||||||
{
|
{
|
||||||
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)]
|
[Command(2)]
|
||||||
// GetSystemClockContext() -> nn::time::SystemClockContext
|
// GetSystemClockContext() -> nn::time::SystemClockContext
|
||||||
public ResultCode GetSystemClockContext(ServiceCtx context)
|
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?
|
if (result == ResultCode.Success)
|
||||||
context.ResponseData.Write(_systemClockTimePoint);
|
|
||||||
|
|
||||||
// This seems to be some kind of identifier?
|
|
||||||
for (int i = 0; i < 0x10; i++)
|
|
||||||
{
|
{
|
||||||
context.ResponseData.Write(_systemClockContextEnding[i]);
|
context.ResponseData.WriteStruct(clockContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(3)]
|
[Command(3)]
|
||||||
// SetSystemClockContext(nn::time::SystemClockContext)
|
// SetSystemClockContext(nn::time::SystemClockContext)
|
||||||
public ResultCode SetSystemClockContext(ServiceCtx context)
|
public ResultCode SetSystemClockContext(ServiceCtx context)
|
||||||
{
|
{
|
||||||
long newSystemClockEpoch = context.RequestData.ReadInt64();
|
if (!_writePermission)
|
||||||
long newSystemClockTimePoint = context.RequestData.ReadInt64();
|
{
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
_systemClockContextEpoch = Epoch.Add(TimeSpan.FromSeconds(newSystemClockEpoch));
|
SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>();
|
||||||
_systemClockTimePoint = newSystemClockTimePoint;
|
|
||||||
_systemClockContextEnding = context.RequestData.ReadBytes(0x10);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
ResultCode result = _clockCore.SetSystemClockContext(clockContext);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
result = _clockCore.Flush(clockContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,11 +7,14 @@
|
||||||
|
|
||||||
Success = 0,
|
Success = 0,
|
||||||
|
|
||||||
|
PermissionDenied = (1 << ErrorCodeShift) | ModuleId,
|
||||||
|
TimeMismatch = (102 << ErrorCodeShift) | ModuleId,
|
||||||
TimeNotFound = (200 << ErrorCodeShift) | ModuleId,
|
TimeNotFound = (200 << ErrorCodeShift) | ModuleId,
|
||||||
Overflow = (201 << ErrorCodeShift) | ModuleId,
|
Overflow = (201 << ErrorCodeShift) | ModuleId,
|
||||||
LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId,
|
LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId,
|
||||||
OutOfRange = (902 << ErrorCodeShift) | ModuleId,
|
OutOfRange = (902 << ErrorCodeShift) | ModuleId,
|
||||||
TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId,
|
TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId,
|
||||||
TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId
|
TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId,
|
||||||
|
NotImplemented = (990 << ErrorCodeShift) | ModuleId,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time
|
|
||||||
{
|
|
||||||
enum SystemClockType
|
|
||||||
{
|
|
||||||
User,
|
|
||||||
Network,
|
|
||||||
Local,
|
|
||||||
EphemeralNetwork
|
|
||||||
}
|
|
||||||
}
|
|
17
Ryujinx.HLE/HOS/Services/Time/TimePermissions.cs
Normal file
17
Ryujinx.HLE/HOS/Services/Time/TimePermissions.cs
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue