using Ryujinx.Common; using Ryujinx.Cpu; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.Utilities; using Ryujinx.Horizon.Common; using System; using System.IO; namespace Ryujinx.HLE.HOS.Services.Time { [Service("time:m")] // 9.0.0+ class ITimeServiceManager : IpcService { private TimeManager _timeManager; private int _automaticCorrectionEvent; public ITimeServiceManager(ServiceCtx context) { _timeManager = TimeManager.Instance; _automaticCorrectionEvent = 0; } [CommandHipc(0)] // GetUserStaticService() -> object<nn::timesrv::detail::service::IStaticService> public ResultCode GetUserStaticService(ServiceCtx context) { MakeObject(context, new IStaticServiceForPsc(_timeManager, TimePermissions.User)); return ResultCode.Success; } [CommandHipc(5)] // GetAdminStaticService() -> object<nn::timesrv::detail::service::IStaticService> public ResultCode GetAdminStaticService(ServiceCtx context) { MakeObject(context, new IStaticServiceForPsc(_timeManager, TimePermissions.Admin)); return ResultCode.Success; } [CommandHipc(6)] // GetRepairStaticService() -> object<nn::timesrv::detail::service::IStaticService> public ResultCode GetRepairStaticService(ServiceCtx context) { MakeObject(context, new IStaticServiceForPsc(_timeManager, TimePermissions.Repair)); return ResultCode.Success; } [CommandHipc(9)] // GetManufactureStaticService() -> object<nn::timesrv::detail::service::IStaticService> public ResultCode GetManufactureStaticService(ServiceCtx context) { MakeObject(context, new IStaticServiceForPsc(_timeManager, TimePermissions.Manufacture)); return ResultCode.Success; } [CommandHipc(10)] // SetupStandardSteadyClock(nn::util::Uuid clock_source_id, nn::TimeSpanType setup_value, nn::TimeSpanType internal_offset, nn::TimeSpanType test_offset, bool is_rtc_reset_detected) public ResultCode SetupStandardSteadyClock(ServiceCtx context) { UInt128 clockSourceId = context.RequestData.ReadStruct<UInt128>(); TimeSpanType setupValue = context.RequestData.ReadStruct<TimeSpanType>(); TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>(); TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>(); bool isRtcResetDetected = context.RequestData.ReadBoolean(); ITickSource tickSource = context.Device.System.TickSource; _timeManager.SetupStandardSteadyClock(tickSource, clockSourceId, setupValue, internalOffset, testOffset, isRtcResetDetected); return ResultCode.Success; } [CommandHipc(11)] // SetupStandardLocalSystemClock(nn::time::SystemClockContext context, nn::time::PosixTime posix_time) public ResultCode SetupStandardLocalSystemClock(ServiceCtx context) { SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>(); long posixTime = context.RequestData.ReadInt64(); ITickSource tickSource = context.Device.System.TickSource; _timeManager.SetupStandardLocalSystemClock(tickSource, clockContext, posixTime); return ResultCode.Success; } [CommandHipc(12)] // SetupStandardNetworkSystemClock(nn::time::SystemClockContext context, nn::TimeSpanType sufficient_accuracy) public ResultCode SetupStandardNetworkSystemClock(ServiceCtx context) { SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>(); TimeSpanType sufficientAccuracy = context.RequestData.ReadStruct<TimeSpanType>(); _timeManager.SetupStandardNetworkSystemClock(clockContext, sufficientAccuracy); return ResultCode.Success; } [CommandHipc(13)] // SetupStandardUserSystemClock(bool automatic_correction_enabled, nn::time::SteadyClockTimePoint steady_clock_timepoint) public ResultCode SetupStandardUserSystemClock(ServiceCtx context) { bool isAutomaticCorrectionEnabled = context.RequestData.ReadBoolean(); context.RequestData.BaseStream.Position += 7; SteadyClockTimePoint steadyClockTimePoint = context.RequestData.ReadStruct<SteadyClockTimePoint>(); ITickSource tickSource = context.Device.System.TickSource; _timeManager.SetupStandardUserSystemClock(tickSource, isAutomaticCorrectionEnabled, steadyClockTimePoint); return ResultCode.Success; } [CommandHipc(14)] // SetupTimeZoneManager(nn::time::LocationName location_name, nn::time::SteadyClockTimePoint timezone_update_timepoint, u32 total_location_name_count, nn::time::TimeZoneRuleVersion timezone_rule_version, buffer<nn::time::TimeZoneBinary, 0x21> timezone_binary) public ResultCode SetupTimeZoneManager(ServiceCtx context) { string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24); SteadyClockTimePoint timeZoneUpdateTimePoint = context.RequestData.ReadStruct<SteadyClockTimePoint>(); uint totalLocationNameCount = context.RequestData.ReadUInt32(); UInt128 timeZoneRuleVersion = context.RequestData.ReadStruct<UInt128>(); (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); byte[] temp = new byte[bufferSize]; context.Memory.Read(bufferPosition, temp); using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) { _timeManager.SetupTimeZoneManager(locationName, timeZoneUpdateTimePoint, totalLocationNameCount, timeZoneRuleVersion, timeZoneBinaryStream); } return ResultCode.Success; } [CommandHipc(15)] // SetupEphemeralNetworkSystemClock() public ResultCode SetupEphemeralNetworkSystemClock(ServiceCtx context) { _timeManager.SetupEphemeralNetworkSystemClock(); return ResultCode.Success; } [CommandHipc(50)] // Unknown50() -> handle<copy> public ResultCode Unknown50(ServiceCtx context) { // TODO: figure out the usage of this event throw new ServiceNotImplementedException(this, context); } [CommandHipc(51)] // Unknown51() -> handle<copy> public ResultCode Unknown51(ServiceCtx context) { // TODO: figure out the usage of this event throw new ServiceNotImplementedException(this, context); } [CommandHipc(52)] // Unknown52() -> handle<copy> public ResultCode Unknown52(ServiceCtx context) { // TODO: figure out the usage of this event throw new ServiceNotImplementedException(this, context); } [CommandHipc(60)] // GetStandardUserSystemClockAutomaticCorrectionEvent() -> handle<copy> public ResultCode GetStandardUserSystemClockAutomaticCorrectionEvent(ServiceCtx context) { if (_automaticCorrectionEvent == 0) { if (context.Process.HandleTable.GenerateHandle(_timeManager.StandardUserSystemClock.GetAutomaticCorrectionReadableEvent(), out _automaticCorrectionEvent) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } } context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_automaticCorrectionEvent); return ResultCode.Success; } [CommandHipc(100)] // SetStandardSteadyClockRtcOffset(nn::TimeSpanType rtc_offset) public ResultCode SetStandardSteadyClockRtcOffset(ServiceCtx context) { TimeSpanType rtcOffset = context.RequestData.ReadStruct<TimeSpanType>(); ITickSource tickSource = context.Device.System.TickSource; _timeManager.SetStandardSteadyClockRtcOffset(tickSource, rtcOffset); return ResultCode.Success; } [CommandHipc(200)] // GetAlarmRegistrationEvent() -> handle<copy> public ResultCode GetAlarmRegistrationEvent(ServiceCtx context) { // TODO throw new ServiceNotImplementedException(this, context); } [CommandHipc(201)] // UpdateSteadyAlarms() public ResultCode UpdateSteadyAlarms(ServiceCtx context) { // TODO throw new ServiceNotImplementedException(this, context); } [CommandHipc(202)] // TryGetNextSteadyClockAlarmSnapshot() -> (bool, nn::time::SteadyClockAlarmSnapshot) public ResultCode TryGetNextSteadyClockAlarmSnapshot(ServiceCtx context) { // TODO throw new ServiceNotImplementedException(this, context); } } }