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
This commit is contained in:
Thomas Guillemard 2019-07-25 16:44:51 +02:00 committed by gdkchan
parent d254548548
commit 54b79dffa8
13 changed files with 518 additions and 205 deletions

View file

@ -199,7 +199,7 @@ namespace Ryujinx.HLE.HOS
// TODO: use set:sys (and set external clock source id from settings) // 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. // 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)) if (Services.Set.NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
{ {

View file

@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct TimeSpanType struct TimeSpanType
{ {
private const long NanoSecondsPerSecond = 1000000000;
public long NanoSeconds; public long NanoSeconds;
public TimeSpanType(long nanoSeconds) public TimeSpanType(long nanoSeconds)
@ -16,12 +18,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public long ToSeconds() 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) 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 long Offset;
public SteadyClockTimePoint SteadyTimePoint; 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;
}
}
} }

View file

@ -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;
}
}
}

View file

@ -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 class StandardLocalSystemClockCore : SystemClockCore
{ {
private SteadyClockCore _steadyClockCore; private static StandardLocalSystemClockCore _instance;
private SystemClockContext _context;
private static StandardLocalSystemClockCore instance;
public static StandardLocalSystemClockCore Instance public static StandardLocalSystemClockCore Instance
{ {
get 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) public StandardLocalSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore) {}
{
_steadyClockCore = steadyClockCore;
_context = new SystemClockContext();
_context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
}
public override ResultCode Flush(SystemClockContext context) public override ResultCode Flush(SystemClockContext context)
{ {
@ -36,24 +25,5 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return ResultCode.Success; 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;
}
} }
} }

View file

@ -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 namespace Ryujinx.HLE.HOS.Services.Time.Clock
{ {
class StandardNetworkSystemClockCore : SystemClockCore class StandardNetworkSystemClockCore : SystemClockCore
{ {
private SteadyClockCore _steadyClockCore;
private SystemClockContext _context;
private TimeSpanType _standardNetworkClockSufficientAccuracy; private TimeSpanType _standardNetworkClockSufficientAccuracy;
private static StandardNetworkSystemClockCore instance; private static StandardNetworkSystemClockCore _instance;
public static StandardNetworkSystemClockCore Instance public static StandardNetworkSystemClockCore Instance
{ {
get 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); _standardNetworkClockSufficientAccuracy = new TimeSpanType(0);
} }
@ -40,25 +33,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return ResultCode.Success; 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) public bool IsStandardNetworkSystemClockAccuracySufficient(KThread thread)
{ {
SteadyClockCore steadyClockCore = GetSteadyClockCore(); SteadyClockCore steadyClockCore = GetSteadyClockCore();
@ -66,7 +40,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
bool isStandardNetworkClockSufficientAccuracy = false; 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; isStandardNetworkClockSufficientAccuracy = outSpan * 1000000000 < _standardNetworkClockSufficientAccuracy.NanoSeconds;
} }

View file

@ -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;
}
}
}

View file

@ -8,22 +8,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
private StandardNetworkSystemClockCore _networkSystemClockCore; private StandardNetworkSystemClockCore _networkSystemClockCore;
private bool _autoCorrectionEnabled; private bool _autoCorrectionEnabled;
private static StandardUserSystemClockCore instance; private static StandardUserSystemClockCore _instance;
public static StandardUserSystemClockCore Instance public static StandardUserSystemClockCore Instance
{ {
get 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; _localSystemClockCore = localSystemClockCore;
_networkSystemClockCore = networkSystemClockCore; _networkSystemClockCore = networkSystemClockCore;
@ -35,11 +35,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
return ResultCode.NotImplemented; return ResultCode.NotImplemented;
} }
public override SteadyClockCore GetSteadyClockCore()
{
return _localSystemClockCore.GetSteadyClockCore();
}
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context) public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
{ {
ResultCode result = ApplyAutomaticCorrection(thread, false); ResultCode result = ApplyAutomaticCorrection(thread, false);

View file

@ -1,131 +1,62 @@
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Bpc;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using System; using System;
namespace Ryujinx.HLE.HOS.Services.Time.Clock 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 SteadyClockCore()
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()); _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;
}
public UInt128 GetClockSourceId() public UInt128 GetClockSourceId()
{ {
return _clockSourceId; 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) public SteadyClockTimePoint GetCurrentTimePoint(KThread thread)
{ {
SteadyClockTimePoint result = GetTimePoint(thread); SteadyClockTimePoint result = GetTimePoint(thread);
result.TimePoint += _testOffset.ToSeconds(); result.TimePoint += GetTestOffset().ToSeconds();
result.TimePoint += _internalOffset.ToSeconds(); result.TimePoint += GetInternalOffset().ToSeconds();
return result; 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;
}
} }
} }

View file

@ -4,11 +4,35 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
{ {
abstract class SystemClockCore 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); public abstract ResultCode Flush(SystemClockContext context);

View file

@ -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;
}
}
}

View file

@ -1,7 +1,13 @@
using Ryujinx.Common;
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.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using System; using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time namespace Ryujinx.HLE.HOS.Services.Time
{ {
@ -66,6 +72,15 @@ namespace Ryujinx.HLE.HOS.Services.Time
return ResultCode.Success; return ResultCode.Success;
} }
[Command(5)] // 4.0.0+
// GetEphemeralNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
public ResultCode GetEphemeralNetworkSystemClock(ServiceCtx context)
{
MakeObject(context, new ISystemClock(StandardNetworkSystemClockCore.Instance, false));
return ResultCode.Success;
}
[Command(20)] // 6.0.0+ [Command(20)] // 6.0.0+
// GetSharedMemoryNativeHandle() -> handle<copy> // GetSharedMemoryNativeHandle() -> handle<copy>
public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context) public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context)
@ -116,16 +131,202 @@ namespace Ryujinx.HLE.HOS.Services.Time
} }
[Command(300)] // 4.0.0+ [Command(300)] // 4.0.0+
// CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> u64 // CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> s64
public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context) public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context)
{ {
// TODO: reimplement this SystemClockContext otherContext = context.RequestData.ReadStruct<SystemClockContext>();
long timeOffset = (long)(DateTime.UtcNow - StartupDate).TotalSeconds; SteadyClockTimePoint currentTimePoint = StandardSteadyClockCore.Instance.GetCurrentTimePoint(context.Thread);
long systemClockContextEpoch = context.RequestData.ReadInt64();
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<nn::time::sf::ClockSnapshot, 0x1a>
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<nn::time::sf::ClockSnapshot, 0x1a>
public ResultCode GetClockSnapshotFromSystemClockContext(ServiceCtx context)
{
byte type = context.RequestData.ReadByte();
context.RequestData.BaseStream.Position += 7;
SystemClockContext userContext = context.RequestData.ReadStruct<SystemClockContext>();
SystemClockContext networkContext = context.RequestData.ReadStruct<SystemClockContext>();
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<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> 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; return ResultCode.Success;
} }
[Command(501)] // 4.0.0+
// CalculateSpanBetween(buffer<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> 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<ClockSnapshot>());
using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(context.Memory.ReadBytes(ipcDesc.Position, ipcDesc.Size))))
{
return bufferReader.ReadStruct<ClockSnapshot>();
}
}
private void WriteClockSnapshotFromBuffer(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, ClockSnapshot clockSnapshot)
{
Debug.Assert(ipcDesc.Size == Marshal.SizeOf<ClockSnapshot>());
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();
}
} }
} }

View file

@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
// GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint // GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint
public ResultCode GetCurrentTimePoint(ServiceCtx context) public ResultCode GetCurrentTimePoint(ServiceCtx context)
{ {
SteadyClockTimePoint currentTimePoint = SteadyClockCore.Instance.GetCurrentTimePoint(context.Thread); SteadyClockTimePoint currentTimePoint = StandardSteadyClockCore.Instance.GetCurrentTimePoint(context.Thread);
context.ResponseData.WriteStruct(currentTimePoint); context.ResponseData.WriteStruct(currentTimePoint);
@ -20,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.WriteStruct(SteadyClockCore.Instance.GetTestOffset()); context.ResponseData.WriteStruct(StandardSteadyClockCore.Instance.GetTestOffset());
return ResultCode.Success; return ResultCode.Success;
} }
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
{ {
TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>(); TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>();
SteadyClockCore.Instance.SetTestOffset(testOffset); StandardSteadyClockCore.Instance.SetTestOffset(testOffset);
return 0; return 0;
} }
@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
// GetRtcValue() -> u64 // GetRtcValue() -> u64
public ResultCode GetRtcValue(ServiceCtx context) 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) if (result == ResultCode.Success)
{ {
@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
// IsRtcResetDetected() -> bool // IsRtcResetDetected() -> bool
public ResultCode IsRtcResetDetected(ServiceCtx context) public ResultCode IsRtcResetDetected(ServiceCtx context)
{ {
context.ResponseData.Write(SteadyClockCore.Instance.IsRtcResetDetected()); context.ResponseData.Write(StandardSteadyClockCore.Instance.IsRtcResetDetected());
return ResultCode.Success; return ResultCode.Success;
} }
@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
// GetSetupResultValue() -> u32 // GetSetupResultValue() -> u32
public ResultCode GetSetupResultValue(ServiceCtx context) public ResultCode GetSetupResultValue(ServiceCtx context)
{ {
context.ResponseData.Write((uint)SteadyClockCore.Instance.GetSetupResultCode()); context.ResponseData.Write((uint)StandardSteadyClockCore.Instance.GetSetupResultValue());
return ResultCode.Success; return ResultCode.Success;
} }
@ -72,7 +72,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
// GetInternalOffset() -> nn::TimeSpanType // GetInternalOffset() -> nn::TimeSpanType
public ResultCode GetInternalOffset(ServiceCtx context) public ResultCode GetInternalOffset(ServiceCtx context)
{ {
context.ResponseData.WriteStruct(SteadyClockCore.Instance.GetInternalOffset()); context.ResponseData.WriteStruct(StandardSteadyClockCore.Instance.GetInternalOffset());
return ResultCode.Success; return ResultCode.Success;
} }
@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
{ {
TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>(); TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>();
SteadyClockCore.Instance.SetInternalOffset(internalOffset); StandardSteadyClockCore.Instance.SetInternalOffset(internalOffset);
return ResultCode.Success; return ResultCode.Success;
} }

View file

@ -56,7 +56,6 @@ namespace Ryujinx.HLE.HOS.Services.Time
// LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
public ResultCode LoadLocationNameList(ServiceCtx context) public ResultCode LoadLocationNameList(ServiceCtx context)
{ {
// TODO: fix logic to use index
uint index = context.RequestData.ReadUInt32(); uint index = context.RequestData.ReadUInt32();
long bufferPosition = context.Request.ReceiveBuff[0].Position; long bufferPosition = context.Request.ReceiveBuff[0].Position;
long bufferSize = context.Request.ReceiveBuff[0].Size; long bufferSize = context.Request.ReceiveBuff[0].Size;