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:
parent
d254548548
commit
54b79dffa8
13 changed files with 518 additions and 205 deletions
|
@ -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))
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
107
Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs
Normal file
107
Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Reference in a new issue