diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 7523d2bee5..d81ea6f5e2 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -197,10 +197,10 @@ namespace Ryujinx.HLE.HOS 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)); + // TODO: use set:sys (and set external clock source id from settings) + // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. + SteadyClockCore.Instance.ConfigureSetupValue(); + } public void LoadCart(string exeFsDir, string romFsFile = null) diff --git a/Ryujinx.HLE/HOS/Services/Bpc/IRtcManager.cs b/Ryujinx.HLE/HOS/Services/Bpc/IRtcManager.cs new file mode 100644 index 0000000000..75d8540467 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Bpc/IRtcManager.cs @@ -0,0 +1,34 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Bpc +{ + [Service("bpc:r")] + class IRtcManager : IpcService + { + public IRtcManager(ServiceCtx context) { } + + [Command(0)] + // GetRtcTime() -> u64 + public static ResultCode GetRtcTime(ServiceCtx context) + { + ResultCode result = GetExternalRtcValue(out ulong rtcValue); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(rtcValue); + } + + return result; + } + + public static ResultCode GetExternalRtcValue(out ulong rtcValue) + { + // TODO: emulate MAX77620/MAX77812 + DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + rtcValue = (ulong)(DateTime.Now.ToUniversalTime() - UnixEpoch).TotalSeconds; + + return ResultCode.Success; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs index b69b7d3cbf..b16ffb97a5 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Bpc; using Ryujinx.HLE.Utilities; using System; @@ -6,6 +7,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock { class SteadyClockCore { + private ulong _setupValue; + private ResultCode _setupResultCode; + private bool _isRtcResetDetected; private TimeSpanType _testOffset; private TimeSpanType _internalOffset; private UInt128 _clockSourceId; @@ -42,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0); - result.TimePoint = _internalOffset.ToSeconds() + ticksTimeSpan.ToSeconds(); + result.TimePoint = _setupValue + ticksTimeSpan.ToSeconds(); return result; } @@ -57,6 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock SteadyClockTimePoint result = GetTimePoint(thread); result.TimePoint += _testOffset.ToSeconds(); + result.TimePoint += _internalOffset.ToSeconds(); return result; } @@ -71,16 +76,56 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock _testOffset = testOffset; } - // TODO: check if this is accurate + 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; } - // TODO: check if this is accurate 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 = rtcValue; + break; + } + + retry++; + } + + _setupResultCode = result; + } } } diff --git a/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs b/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs index d9f05f29cc..2772b45d66 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ISteadyClock.cs @@ -36,6 +36,38 @@ namespace Ryujinx.HLE.HOS.Services.Time return 0; } + [Command(100)] // 2.0.0+ + // GetRtcValue() -> u64 + public ResultCode GetRtcValue(ServiceCtx context) + { + ResultCode result = SteadyClockCore.Instance.GetRtcValue(out ulong rtcValue); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(rtcValue); + } + + return result; + } + + [Command(101)] // 2.0.0+ + // IsRtcResetDetected() -> bool + public ResultCode IsRtcResetDetected(ServiceCtx context) + { + context.ResponseData.Write(SteadyClockCore.Instance.IsRtcResetDetected()); + + return ResultCode.Success; + } + + [Command(102)] // 2.0.0+ + // GetSetupResultValue() -> u32 + public ResultCode GetSetupResultValue(ServiceCtx context) + { + context.ResponseData.Write((uint)SteadyClockCore.Instance.GetSetupResultCode()); + + return ResultCode.Success; + } + [Command(200)] // 3.0.0+ // GetInternalOffset() -> nn::TimeSpanType public ResultCode GetInternalOffset(ServiceCtx context)