diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.HLE/HOS/Services/Time/StaticService | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Services/Time/StaticService')
4 files changed, 731 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs new file mode 100644 index 00000000..97d7884e --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs @@ -0,0 +1,155 @@ +using Ryujinx.Common; +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Services.Time.Clock; + +namespace Ryujinx.HLE.HOS.Services.Time.StaticService +{ + class ISteadyClock : IpcService + { + private SteadyClockCore _steadyClock; + private bool _writePermission; + private bool _bypassUninitializedClock; + + public ISteadyClock(SteadyClockCore steadyClock, bool writePermission, bool bypassUninitializedClock) + { + _steadyClock = steadyClock; + _writePermission = writePermission; + _bypassUninitializedClock = bypassUninitializedClock; + } + + [CommandCmif(0)] + // GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint + public ResultCode GetCurrentTimePoint(ServiceCtx context) + { + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + ITickSource tickSource = context.Device.System.TickSource; + + SteadyClockTimePoint currentTimePoint = _steadyClock.GetCurrentTimePoint(tickSource); + + context.ResponseData.WriteStruct(currentTimePoint); + + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetTestOffset() -> nn::TimeSpanType + public ResultCode GetTestOffset(ServiceCtx context) + { + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + context.ResponseData.WriteStruct(_steadyClock.GetTestOffset()); + + return ResultCode.Success; + } + + [CommandCmif(3)] + // SetTestOffset(nn::TimeSpanType) + public ResultCode SetTestOffset(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>(); + + _steadyClock.SetTestOffset(testOffset); + + return ResultCode.Success; + } + + [CommandCmif(100)] // 2.0.0+ + // GetRtcValue() -> u64 + public ResultCode GetRtcValue(ServiceCtx context) + { + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + ResultCode result = _steadyClock.GetRtcValue(out ulong rtcValue); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(rtcValue); + } + + return result; + } + + [CommandCmif(101)] // 2.0.0+ + // IsRtcResetDetected() -> bool + public ResultCode IsRtcResetDetected(ServiceCtx context) + { + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + context.ResponseData.Write(_steadyClock.IsRtcResetDetected()); + + return ResultCode.Success; + } + + [CommandCmif(102)] // 2.0.0+ + // GetSetupResultValue() -> u32 + public ResultCode GetSetupResultValue(ServiceCtx context) + { + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + context.ResponseData.Write((uint)_steadyClock.GetSetupResultValue()); + + return ResultCode.Success; + } + + [CommandCmif(200)] // 3.0.0+ + // GetInternalOffset() -> nn::TimeSpanType + public ResultCode GetInternalOffset(ServiceCtx context) + { + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + context.ResponseData.WriteStruct(_steadyClock.GetInternalOffset()); + + return ResultCode.Success; + } + + [CommandCmif(201)] // 3.0.0-3.0.2 + // SetInternalOffset(nn::TimeSpanType) + public ResultCode SetInternalOffset(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>(); + + _steadyClock.SetInternalOffset(internalOffset); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs new file mode 100644 index 00000000..3cd0a4a6 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs @@ -0,0 +1,131 @@ +using Ryujinx.Common; +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Time.Clock; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Time.StaticService +{ + class ISystemClock : IpcService + { + private SystemClockCore _clockCore; + private bool _writePermission; + private bool _bypassUninitializedClock; + private int _operationEventReadableHandle; + + public ISystemClock(SystemClockCore clockCore, bool writePermission, bool bypassUninitializedClock) + { + _clockCore = clockCore; + _writePermission = writePermission; + _bypassUninitializedClock = bypassUninitializedClock; + _operationEventReadableHandle = 0; + } + + [CommandCmif(0)] + // GetCurrentTime() -> nn::time::PosixTime + public ResultCode GetCurrentTime(ServiceCtx context) + { + if (!_bypassUninitializedClock && !_clockCore.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + ITickSource tickSource = context.Device.System.TickSource; + + ResultCode result = _clockCore.GetCurrentTime(tickSource, out long posixTime); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(posixTime); + } + + return result; + } + + [CommandCmif(1)] + // SetCurrentTime(nn::time::PosixTime) + public ResultCode SetCurrentTime(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + if (!_bypassUninitializedClock && !_clockCore.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + long posixTime = context.RequestData.ReadInt64(); + + ITickSource tickSource = context.Device.System.TickSource; + + return _clockCore.SetCurrentTime(tickSource, posixTime); + } + + [CommandCmif(2)] + // GetClockContext() -> nn::time::SystemClockContext + public ResultCode GetSystemClockContext(ServiceCtx context) + { + if (!_bypassUninitializedClock && !_clockCore.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + ITickSource tickSource = context.Device.System.TickSource; + + ResultCode result = _clockCore.GetClockContext(tickSource, out SystemClockContext clockContext); + + if (result == ResultCode.Success) + { + context.ResponseData.WriteStruct(clockContext); + } + + return result; + } + + [CommandCmif(3)] + // SetClockContext(nn::time::SystemClockContext) + public ResultCode SetSystemClockContext(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + if (!_bypassUninitializedClock && !_clockCore.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>(); + + ResultCode result = _clockCore.SetSystemClockContext(clockContext); + + return result; + } + + [CommandCmif(4)] // 9.0.0+ + // GetOperationEventReadableHandle() -> handle<copy> + public ResultCode GetOperationEventReadableHandle(ServiceCtx context) + { + if (_operationEventReadableHandle == 0) + { + KEvent kEvent = new KEvent(context.Device.System.KernelContext); + + _clockCore.RegisterOperationEvent(kEvent.WritableEvent); + + if (context.Process.HandleTable.GenerateHandle(kEvent.ReadableEvent, out _operationEventReadableHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_operationEventReadableHandle); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs new file mode 100644 index 00000000..96a7e604 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs @@ -0,0 +1,142 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Services.Time.TimeZone; +using Ryujinx.HLE.Utilities; +using Ryujinx.Memory; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Time.StaticService +{ + class ITimeZoneServiceForGlue : IpcService + { + private TimeZoneContentManager _timeZoneContentManager; + private ITimeZoneServiceForPsc _inner; + private bool _writePermission; + + public ITimeZoneServiceForGlue(TimeZoneContentManager timeZoneContentManager, bool writePermission) + { + _timeZoneContentManager = timeZoneContentManager; + _writePermission = writePermission; + _inner = new ITimeZoneServiceForPsc(timeZoneContentManager.Manager, writePermission); + } + + [CommandCmif(0)] + // GetDeviceLocationName() -> nn::time::LocationName + public ResultCode GetDeviceLocationName(ServiceCtx context) + { + return _inner.GetDeviceLocationName(context); + } + + [CommandCmif(1)] + // SetDeviceLocationName(nn::time::LocationName) + public ResultCode SetDeviceLocationName(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24); + + return _timeZoneContentManager.SetDeviceLocationName(locationName); + } + + [CommandCmif(2)] + // GetTotalLocationNameCount() -> u32 + public ResultCode GetTotalLocationNameCount(ServiceCtx context) + { + return _inner.GetTotalLocationNameCount(context); + } + + [CommandCmif(3)] + // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) + public ResultCode LoadLocationNameList(ServiceCtx context) + { + uint index = context.RequestData.ReadUInt32(); + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; + + ResultCode errorCode = _timeZoneContentManager.LoadLocationNameList(index, out string[] locationNameArray, (uint)bufferSize / 0x24); + + if (errorCode == 0) + { + uint offset = 0; + + foreach (string locationName in locationNameArray) + { + int padding = 0x24 - locationName.Length; + + if (padding < 0) + { + return ResultCode.LocationNameTooLong; + } + + context.Memory.Write(bufferPosition + offset, Encoding.ASCII.GetBytes(locationName)); + MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + (ulong)locationName.Length, padding); + + offset += 0x24; + } + + context.ResponseData.Write((uint)locationNameArray.Length); + } + + return errorCode; + } + + [CommandCmif(4)] + // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16> + public ResultCode LoadTimeZoneRule(ServiceCtx context) + { + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; + + if (bufferSize != 0x4000) + { + // TODO: find error code here + Logger.Error?.Print(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)"); + + throw new InvalidOperationException(); + } + + string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24); + + using (WritableRegion region = context.Memory.GetWritableRegion(bufferPosition, Unsafe.SizeOf<TimeZoneRule>())) + { + ref TimeZoneRule rules = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0]; + + return _timeZoneContentManager.LoadTimeZoneRule(ref rules, locationName); + } + } + + [CommandCmif(100)] + // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) + public ResultCode ToCalendarTime(ServiceCtx context) + { + return _inner.ToCalendarTime(context); + } + + [CommandCmif(101)] + // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) + public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context) + { + return _inner.ToCalendarTimeWithMyRule(context); + } + + [CommandCmif(201)] + // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) + public ResultCode ToPosixTime(ServiceCtx context) + { + return _inner.ToPosixTime(context); + } + + [CommandCmif(202)] + // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) + public ResultCode ToPosixTimeWithMyRule(ServiceCtx context) + { + return _inner.ToPosixTimeWithMyRule(context); + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs new file mode 100644 index 00000000..3c9ac71f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs @@ -0,0 +1,303 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Time.Clock; +using Ryujinx.HLE.HOS.Services.Time.TimeZone; +using Ryujinx.HLE.Utilities; +using Ryujinx.Memory; +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Time.StaticService +{ + class ITimeZoneServiceForPsc : IpcService + { + private TimeZoneManager _timeZoneManager; + private bool _writePermission; + + public ITimeZoneServiceForPsc(TimeZoneManager timeZoneManager, bool writePermission) + { + _timeZoneManager = timeZoneManager; + _writePermission = writePermission; + } + + [CommandCmif(0)] + // GetDeviceLocationName() -> nn::time::LocationName + public ResultCode GetDeviceLocationName(ServiceCtx context) + { + ResultCode result = _timeZoneManager.GetDeviceLocationName(out string deviceLocationName); + + if (result == ResultCode.Success) + { + WriteLocationName(context, deviceLocationName); + } + + return result; + } + + [CommandCmif(1)] + // SetDeviceLocationName(nn::time::LocationName) + public ResultCode SetDeviceLocationName(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + return ResultCode.NotImplemented; + } + + [CommandCmif(2)] + // GetTotalLocationNameCount() -> u32 + public ResultCode GetTotalLocationNameCount(ServiceCtx context) + { + ResultCode result = _timeZoneManager.GetTotalLocationNameCount(out uint totalLocationNameCount); + + if (result == ResultCode.Success) + { + context.ResponseData.Write(totalLocationNameCount); + } + + return ResultCode.Success; + } + + [CommandCmif(3)] + // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) + public ResultCode LoadLocationNameList(ServiceCtx context) + { + return ResultCode.NotImplemented; + } + + [CommandCmif(4)] + // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16> + public ResultCode LoadTimeZoneRule(ServiceCtx context) + { + return ResultCode.NotImplemented; + } + + [CommandCmif(5)] // 2.0.0+ + // GetTimeZoneRuleVersion() -> nn::time::TimeZoneRuleVersion + public ResultCode GetTimeZoneRuleVersion(ServiceCtx context) + { + ResultCode result = _timeZoneManager.GetTimeZoneRuleVersion(out UInt128 timeZoneRuleVersion); + + if (result == ResultCode.Success) + { + context.ResponseData.WriteStruct(timeZoneRuleVersion); + } + + return result; + } + + [CommandCmif(6)] // 5.0.0+ + // GetDeviceLocationNameAndUpdatedTime() -> (nn::time::LocationName, nn::time::SteadyClockTimePoint) + public ResultCode GetDeviceLocationNameAndUpdatedTime(ServiceCtx context) + { + ResultCode result = _timeZoneManager.GetDeviceLocationName(out string deviceLocationName); + + if (result == ResultCode.Success) + { + result = _timeZoneManager.GetUpdatedTime(out SteadyClockTimePoint timeZoneUpdateTimePoint); + + if (result == ResultCode.Success) + { + WriteLocationName(context, deviceLocationName); + + // Skip padding + context.ResponseData.BaseStream.Position += 0x4; + + context.ResponseData.WriteStruct(timeZoneUpdateTimePoint); + } + } + + return result; + } + + [CommandCmif(7)] // 9.0.0+ + // SetDeviceLocationNameWithTimeZoneRule(nn::time::LocationName locationName, buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) + public ResultCode SetDeviceLocationNameWithTimeZoneRule(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); + + string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24); + + ResultCode result; + + byte[] temp = new byte[bufferSize]; + + context.Memory.Read(bufferPosition, temp); + + using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) + { + result = _timeZoneManager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream); + } + + return result; + } + + [CommandCmif(8)] // 9.0.0+ + // ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16> + public ResultCode ParseTimeZoneBinary(ServiceCtx context) + { + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); + + ulong timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position; + ulong timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size; + + if (timeZoneRuleBufferSize != 0x4000) + { + // TODO: find error code here + Logger.Error?.Print(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{timeZoneRuleBufferSize:x} (expected 0x4000)"); + + throw new InvalidOperationException(); + } + + ResultCode result; + + byte[] temp = new byte[bufferSize]; + + context.Memory.Read(bufferPosition, temp); + + using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) + { + using (WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf<TimeZoneRule>())) + { + ref TimeZoneRule rule = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0]; + + result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream); + } + } + + return result; + } + + [CommandCmif(20)] // 9.0.0+ + // GetDeviceLocationNameOperationEventReadableHandle() -> handle<copy> + public ResultCode GetDeviceLocationNameOperationEventReadableHandle(ServiceCtx context) + { + return ResultCode.NotImplemented; + } + + [CommandCmif(100)] + // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) + public ResultCode ToCalendarTime(ServiceCtx context) + { + long posixTime = context.RequestData.ReadInt64(); + ulong bufferPosition = context.Request.SendBuff[0].Position; + ulong bufferSize = context.Request.SendBuff[0].Size; + + if (bufferSize != 0x4000) + { + // TODO: find error code here + Logger.Error?.Print(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)"); + + throw new InvalidOperationException(); + } + + ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(bufferPosition, (int)bufferSize)); + + ResultCode resultCode = _timeZoneManager.ToCalendarTime(in rules[0], posixTime, out CalendarInfo calendar); + + if (resultCode == 0) + { + context.ResponseData.WriteStruct(calendar); + } + + return resultCode; + } + + [CommandCmif(101)] + // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) + public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context) + { + long posixTime = context.RequestData.ReadInt64(); + + ResultCode resultCode = _timeZoneManager.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar); + + if (resultCode == ResultCode.Success) + { + context.ResponseData.WriteStruct(calendar); + } + + return resultCode; + } + + [CommandCmif(201)] + // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) + public ResultCode ToPosixTime(ServiceCtx context) + { + ulong inBufferPosition = context.Request.SendBuff[0].Position; + ulong inBufferSize = context.Request.SendBuff[0].Size; + + CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>(); + + if (inBufferSize != 0x4000) + { + // TODO: find error code here + Logger.Error?.Print(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{inBufferSize:x} (expected 0x4000)"); + + throw new InvalidOperationException(); + } + + ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(inBufferPosition, (int)inBufferSize)); + + ResultCode resultCode = _timeZoneManager.ToPosixTime(in rules[0], calendarTime, out long posixTime); + + if (resultCode == ResultCode.Success) + { + ulong outBufferPosition = context.Request.RecvListBuff[0].Position; + ulong outBufferSize = context.Request.RecvListBuff[0].Size; + + context.Memory.Write(outBufferPosition, posixTime); + context.ResponseData.Write(1); + } + + return resultCode; + } + + [CommandCmif(202)] + // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) + public ResultCode ToPosixTimeWithMyRule(ServiceCtx context) + { + CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>(); + + ResultCode resultCode = _timeZoneManager.ToPosixTimeWithMyRules(calendarTime, out long posixTime); + + if (resultCode == ResultCode.Success) + { + ulong outBufferPosition = context.Request.RecvListBuff[0].Position; + ulong outBufferSize = context.Request.RecvListBuff[0].Size; + + context.Memory.Write(outBufferPosition, posixTime); + + // There could be only one result on one calendar as leap seconds aren't supported. + context.ResponseData.Write(1); + } + + return resultCode; + } + + private void WriteLocationName(ServiceCtx context, string locationName) + { + char[] locationNameArray = locationName.ToCharArray(); + + int padding = 0x24 - locationNameArray.Length; + + Debug.Assert(padding >= 0, "LocationName exceeded limit (0x24 bytes)"); + + context.ResponseData.Write(locationNameArray); + + for (int index = 0; index < padding; index++) + { + context.ResponseData.Write((byte)0); + } + } + } +} |
