diff options
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Time/StaticService')
5 files changed, 562 insertions, 266 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs index 31f119df..bf6a4fd1 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs @@ -5,42 +5,78 @@ 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; + } + [Command(0)] // GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint public ResultCode GetCurrentTimePoint(ServiceCtx context) { - SteadyClockTimePoint currentTimePoint = StandardSteadyClockCore.Instance.GetCurrentTimePoint(context.Thread); + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + SteadyClockTimePoint currentTimePoint = _steadyClock.GetCurrentTimePoint(context.Thread); context.ResponseData.WriteStruct(currentTimePoint); return ResultCode.Success; } - [Command(1)] + [Command(2)] // GetTestOffset() -> nn::TimeSpanType public ResultCode GetTestOffset(ServiceCtx context) { - context.ResponseData.WriteStruct(StandardSteadyClockCore.Instance.GetTestOffset()); + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + context.ResponseData.WriteStruct(_steadyClock.GetTestOffset()); return ResultCode.Success; } - [Command(2)] + [Command(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>(); - StandardSteadyClockCore.Instance.SetTestOffset(testOffset); + _steadyClock.SetTestOffset(testOffset); - return 0; + return ResultCode.Success; } [Command(100)] // 2.0.0+ // GetRtcValue() -> u64 public ResultCode GetRtcValue(ServiceCtx context) { - ResultCode result = StandardSteadyClockCore.Instance.GetRtcValue(out ulong rtcValue); + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + ResultCode result = _steadyClock.GetRtcValue(out ulong rtcValue); if (result == ResultCode.Success) { @@ -54,7 +90,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // IsRtcResetDetected() -> bool public ResultCode IsRtcResetDetected(ServiceCtx context) { - context.ResponseData.Write(StandardSteadyClockCore.Instance.IsRtcResetDetected()); + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + context.ResponseData.Write(_steadyClock.IsRtcResetDetected()); return ResultCode.Success; } @@ -63,7 +104,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // GetSetupResultValue() -> u32 public ResultCode GetSetupResultValue(ServiceCtx context) { - context.ResponseData.Write((uint)StandardSteadyClockCore.Instance.GetSetupResultValue()); + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + context.ResponseData.Write((uint)_steadyClock.GetSetupResultValue()); return ResultCode.Success; } @@ -72,7 +118,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // GetInternalOffset() -> nn::TimeSpanType public ResultCode GetInternalOffset(ServiceCtx context) { - context.ResponseData.WriteStruct(StandardSteadyClockCore.Instance.GetInternalOffset()); + if (!_bypassUninitializedClock && !_steadyClock.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + context.ResponseData.WriteStruct(_steadyClock.GetInternalOffset()); return ResultCode.Success; } @@ -81,9 +132,19 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // 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>(); - StandardSteadyClockCore.Instance.SetInternalOffset(internalOffset); + _steadyClock.SetInternalOffset(internalOffset); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs index 0d866177..d5b21f8c 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs @@ -1,5 +1,9 @@ using Ryujinx.Common; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Time.Clock; +using System; namespace Ryujinx.HLE.HOS.Services.Time.StaticService { @@ -7,34 +11,31 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService { private SystemClockCore _clockCore; private bool _writePermission; + private bool _bypassUninitializedClock; + private int _operationEventReadableHandle; - public ISystemClock(SystemClockCore clockCore, bool writePermission) + public ISystemClock(SystemClockCore clockCore, bool writePermission, bool bypassUninitializedClock) { - _clockCore = clockCore; - _writePermission = writePermission; + _clockCore = clockCore; + _writePermission = writePermission; + _bypassUninitializedClock = bypassUninitializedClock; + _operationEventReadableHandle = 0; } [Command(0)] // GetCurrentTime() -> nn::time::PosixTime public ResultCode GetCurrentTime(ServiceCtx context) { - SteadyClockCore steadyClockCore = _clockCore.GetSteadyClockCore(); - SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(context.Thread); + if (!_bypassUninitializedClock && !_clockCore.IsInitialized()) + { + return ResultCode.UninitializedClock; + } - ResultCode result = _clockCore.GetSystemClockContext(context.Thread, out SystemClockContext clockContext); + ResultCode result = _clockCore.GetCurrentTime(context.Thread, out long posixTime); if (result == ResultCode.Success) { - result = ResultCode.TimeMismatch; - - if (currentTimePoint.ClockSourceId == clockContext.SteadyTimePoint.ClockSourceId) - { - long posixTime = clockContext.Offset + currentTimePoint.TimePoint; - - context.ResponseData.Write(posixTime); - - result = 0; - } + context.ResponseData.Write(posixTime); } return result; @@ -49,31 +50,26 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.PermissionDenied; } - long posixTime = context.RequestData.ReadInt64(); - SteadyClockCore steadyClockCore = _clockCore.GetSteadyClockCore(); - SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(context.Thread); - - SystemClockContext clockContext = new SystemClockContext() + if (!_bypassUninitializedClock && !_clockCore.IsInitialized()) { - Offset = posixTime - currentTimePoint.TimePoint, - SteadyTimePoint = currentTimePoint - }; - - ResultCode result = _clockCore.SetSystemClockContext(clockContext); - - if (result == ResultCode.Success) - { - result = _clockCore.Flush(clockContext); + return ResultCode.UninitializedClock; } - return result; + long posixTime = context.RequestData.ReadInt64(); + + return _clockCore.SetCurrentTime(context.Thread, posixTime); } [Command(2)] - // GetSystemClockContext() -> nn::time::SystemClockContext + // GetClockContext() -> nn::time::SystemClockContext public ResultCode GetSystemClockContext(ServiceCtx context) { - ResultCode result = _clockCore.GetSystemClockContext(context.Thread, out SystemClockContext clockContext); + if (!_bypassUninitializedClock && !_clockCore.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + + ResultCode result = _clockCore.GetClockContext(context.Thread, out SystemClockContext clockContext); if (result == ResultCode.Success) { @@ -84,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService } [Command(3)] - // SetSystemClockContext(nn::time::SystemClockContext) + // SetClockContext(nn::time::SystemClockContext) public ResultCode SetSystemClockContext(ServiceCtx context) { if (!_writePermission) @@ -92,16 +88,37 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.PermissionDenied; } + if (!_bypassUninitializedClock && !_clockCore.IsInitialized()) + { + return ResultCode.UninitializedClock; + } + SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>(); ResultCode result = _clockCore.SetSystemClockContext(clockContext); - if (result == ResultCode.Success) + return result; + } + + [Command(4)] // 9.0.0+ + // GetOperationEventReadableHandle() -> handle<copy> + public ResultCode GetOperationEventReadableHandle(ServiceCtx context) + { + if (_operationEventReadableHandle == 0) { - result = _clockCore.Flush(clockContext); + KEvent kEvent = new KEvent(context.Device.System); + + _clockCore.RegisterOperationEvent(kEvent.WritableEvent); + + if (context.Process.HandleTable.GenerateHandle(kEvent.ReadableEvent, out _operationEventReadableHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } } - return result; + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_operationEventReadableHandle); + + return ResultCode.Success; } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneService.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneService.cs deleted file mode 100644 index c65107df..00000000 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneService.cs +++ /dev/null @@ -1,218 +0,0 @@ -using ARMeilleure.Memory; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.HLE.HOS.Services.Time.TimeZone; -using System; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Time.StaticService -{ - class ITimeZoneService : IpcService - { - public ITimeZoneService() { } - - [Command(0)] - // GetDeviceLocationName() -> nn::time::LocationName - public ResultCode GetDeviceLocationName(ServiceCtx context) - { - char[] tzName = TimeZoneManager.Instance.GetDeviceLocationName().ToCharArray(); - - int padding = 0x24 - tzName.Length; - - if (padding < 0) - { - return ResultCode.LocationNameTooLong; - } - - context.ResponseData.Write(tzName); - - for (int index = 0; index < padding; index++) - { - context.ResponseData.Write((byte)0); - } - - return ResultCode.Success; - } - - [Command(1)] - // SetDeviceLocationName(nn::time::LocationName) - public ResultCode SetDeviceLocationName(ServiceCtx context) - { - string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0'); - - return TimeZoneManager.Instance.SetDeviceLocationName(locationName); - } - - [Command(2)] - // GetTotalLocationNameCount() -> u32 - public ResultCode GetTotalLocationNameCount(ServiceCtx context) - { - context.ResponseData.Write(TimeZoneManager.Instance.GetTotalLocationNameCount()); - - return ResultCode.Success; - } - - [Command(3)] - // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) - public ResultCode LoadLocationNameList(ServiceCtx context) - { - uint index = context.RequestData.ReadUInt32(); - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferSize = context.Request.ReceiveBuff[0].Size; - - ResultCode errorCode = TimeZoneManager.Instance.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.WriteBytes(bufferPosition + offset, Encoding.ASCII.GetBytes(locationName)); - MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + locationName.Length, padding); - - offset += 0x24; - } - - context.ResponseData.Write((uint)locationNameArray.Length); - } - - return errorCode; - } - - [Command(4)] - // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16> - public ResultCode LoadTimeZoneRule(ServiceCtx context) - { - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferSize = context.Request.ReceiveBuff[0].Size; - - if (bufferSize != 0x4000) - { - // TODO: find error code here - Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)"); - - throw new InvalidOperationException(); - } - - - string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0'); - - ResultCode resultCode = TimeZoneManager.Instance.LoadTimeZoneRules(out TimeZoneRule rules, locationName); - - // Write TimeZoneRule if success - if (resultCode == 0) - { - MemoryHelper.Write(context.Memory, bufferPosition, rules); - } - - return resultCode; - } - - [Command(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(); - long bufferPosition = context.Request.SendBuff[0].Position; - long bufferSize = context.Request.SendBuff[0].Size; - - if (bufferSize != 0x4000) - { - // TODO: find error code here - Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)"); - - throw new InvalidOperationException(); - } - - TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition); - - ResultCode resultCode = TimeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar); - - if (resultCode == 0) - { - context.ResponseData.WriteStruct(calendar); - } - - return resultCode; - } - - [Command(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.Instance.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar); - - if (resultCode == 0) - { - context.ResponseData.WriteStruct(calendar); - } - - return resultCode; - } - - [Command(201)] - // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) - public ResultCode ToPosixTime(ServiceCtx context) - { - long inBufferPosition = context.Request.SendBuff[0].Position; - long inBufferSize = context.Request.SendBuff[0].Size; - - CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>(); - - if (inBufferSize != 0x4000) - { - // TODO: find error code here - Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{inBufferSize:x} (expected 0x4000)"); - - throw new InvalidOperationException(); - } - - TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition); - - ResultCode resultCode = TimeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime); - - if (resultCode == 0) - { - long outBufferPosition = context.Request.RecvListBuff[0].Position; - long outBufferSize = context.Request.RecvListBuff[0].Size; - - context.Memory.WriteInt64(outBufferPosition, posixTime); - context.ResponseData.Write(1); - } - - return resultCode; - } - - [Command(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.Instance.ToPosixTimeWithMyRules(calendarTime, out long posixTime); - - if (resultCode == 0) - { - long outBufferPosition = context.Request.RecvListBuff[0].Position; - long outBufferSize = context.Request.RecvListBuff[0].Size; - - context.Memory.WriteInt64(outBufferPosition, posixTime); - - // There could be only one result on one calendar as leap seconds aren't supported. - context.ResponseData.Write(1); - } - - return resultCode; - } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs new file mode 100644 index 00000000..7acb0257 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs @@ -0,0 +1,142 @@ +using ARMeilleure.Memory; +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Time.TimeZone; +using System; +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); + } + + [Command(0)] + // GetDeviceLocationName() -> nn::time::LocationName + public ResultCode GetDeviceLocationName(ServiceCtx context) + { + return _inner.GetDeviceLocationName(context); + } + + [Command(1)] + // SetDeviceLocationName(nn::time::LocationName) + public ResultCode SetDeviceLocationName(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0'); + + return _timeZoneContentManager.SetDeviceLocationName(locationName); + } + + [Command(2)] + // GetTotalLocationNameCount() -> u32 + public ResultCode GetTotalLocationNameCount(ServiceCtx context) + { + return _inner.GetTotalLocationNameCount(context); + } + + [Command(3)] + // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) + public ResultCode LoadLocationNameList(ServiceCtx context) + { + uint index = context.RequestData.ReadUInt32(); + long bufferPosition = context.Request.ReceiveBuff[0].Position; + long 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.WriteBytes(bufferPosition + offset, Encoding.ASCII.GetBytes(locationName)); + MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + locationName.Length, padding); + + offset += 0x24; + } + + context.ResponseData.Write((uint)locationNameArray.Length); + } + + return errorCode; + } + + [Command(4)] + // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16> + public ResultCode LoadTimeZoneRule(ServiceCtx context) + { + long bufferPosition = context.Request.ReceiveBuff[0].Position; + long bufferSize = context.Request.ReceiveBuff[0].Size; + + if (bufferSize != 0x4000) + { + // TODO: find error code here + Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)"); + + throw new InvalidOperationException(); + } + + string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0'); + + ResultCode resultCode = _timeZoneContentManager.LoadTimeZoneRule(out TimeZoneRule rules, locationName); + + // Write TimeZoneRule if success + if (resultCode == ResultCode.Success) + { + MemoryHelper.Write(context.Memory, bufferPosition, rules); + } + + return resultCode; + } + + [Command(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); + } + + [Command(101)] + // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) + public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context) + { + return _inner.ToCalendarTimeWithMyRule(context); + } + + [Command(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); + } + + [Command(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/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs new file mode 100644 index 00000000..ed31fe7c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs @@ -0,0 +1,294 @@ +using ARMeilleure.Memory; +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 System; +using System.Diagnostics; +using System.IO; +using System.Text; + +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; + } + + [Command(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; + } + + [Command(1)] + // SetDeviceLocationName(nn::time::LocationName) + public ResultCode SetDeviceLocationName(ServiceCtx context) + { + if (!_writePermission) + { + return ResultCode.PermissionDenied; + } + + return ResultCode.NotImplemented; + } + + [Command(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; + } + + [Command(3)] + // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) + public ResultCode LoadLocationNameList(ServiceCtx context) + { + return ResultCode.NotImplemented; + } + + [Command(4)] + // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16> + public ResultCode LoadTimeZoneRule(ServiceCtx context) + { + return ResultCode.NotImplemented; + } + + [Command(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; + } + + [Command(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; + } + + [Command(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; + } + + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + + string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0'); + + ResultCode result; + + using (MemoryStream timeZoneBinaryStream = new MemoryStream(context.Memory.ReadBytes(bufferPosition, bufferSize))) + { + result = _timeZoneManager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream); + } + + return result; + } + + [Command(8)] // 9.0.0+ + // ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16> + public ResultCode ParseTimeZoneBinary(ServiceCtx context) + { + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + + long timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position; + long timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size; + + if (timeZoneRuleBufferSize != 0x4000) + { + // TODO: find error code here + Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{timeZoneRuleBufferSize:x} (expected 0x4000)"); + + throw new InvalidOperationException(); + } + + ResultCode result; + + using (MemoryStream timeZoneBinaryStream = new MemoryStream(context.Memory.ReadBytes(bufferPosition, bufferSize))) + { + result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream); + + if (result == ResultCode.Success) + { + MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule); + } + } + + return result; + } + + [Command(20)] // 9.0.0+ + // GetDeviceLocationNameOperationEventReadableHandle() -> handle<copy> + public ResultCode GetDeviceLocationNameOperationEventReadableHandle(ServiceCtx context) + { + return ResultCode.NotImplemented; + } + + [Command(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(); + long bufferPosition = context.Request.SendBuff[0].Position; + long bufferSize = context.Request.SendBuff[0].Size; + + if (bufferSize != 0x4000) + { + // TODO: find error code here + Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)"); + + throw new InvalidOperationException(); + } + + TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition); + + ResultCode resultCode = _timeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar); + + if (resultCode == 0) + { + context.ResponseData.WriteStruct(calendar); + } + + return resultCode; + } + + [Command(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 == 0) + { + context.ResponseData.WriteStruct(calendar); + } + + return resultCode; + } + + [Command(201)] + // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) + public ResultCode ToPosixTime(ServiceCtx context) + { + long inBufferPosition = context.Request.SendBuff[0].Position; + long inBufferSize = context.Request.SendBuff[0].Size; + + CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>(); + + if (inBufferSize != 0x4000) + { + // TODO: find error code here + Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{inBufferSize:x} (expected 0x4000)"); + + throw new InvalidOperationException(); + } + + TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition); + + ResultCode resultCode = _timeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime); + + if (resultCode == 0) + { + long outBufferPosition = context.Request.RecvListBuff[0].Position; + long outBufferSize = context.Request.RecvListBuff[0].Size; + + context.Memory.WriteInt64(outBufferPosition, posixTime); + context.ResponseData.Write(1); + } + + return resultCode; + } + + [Command(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 == 0) + { + long outBufferPosition = context.Request.RecvListBuff[0].Position; + long outBufferSize = context.Request.RecvListBuff[0].Size; + + context.Memory.WriteInt64(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); + } + } + } +} |
