ITimeZoneServiceForPsc.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. using Ryujinx.Common;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.HLE.HOS.Services.Time.Clock;
  4. using Ryujinx.HLE.HOS.Services.Time.TimeZone;
  5. using Ryujinx.HLE.Utilities;
  6. using Ryujinx.Memory;
  7. using System;
  8. using System.Diagnostics;
  9. using System.IO;
  10. using System.Runtime.CompilerServices;
  11. using System.Runtime.InteropServices;
  12. namespace Ryujinx.HLE.HOS.Services.Time.StaticService
  13. {
  14. class ITimeZoneServiceForPsc : IpcService
  15. {
  16. private TimeZoneManager _timeZoneManager;
  17. private bool _writePermission;
  18. public ITimeZoneServiceForPsc(TimeZoneManager timeZoneManager, bool writePermission)
  19. {
  20. _timeZoneManager = timeZoneManager;
  21. _writePermission = writePermission;
  22. }
  23. [CommandHipc(0)]
  24. // GetDeviceLocationName() -> nn::time::LocationName
  25. public ResultCode GetDeviceLocationName(ServiceCtx context)
  26. {
  27. ResultCode result = _timeZoneManager.GetDeviceLocationName(out string deviceLocationName);
  28. if (result == ResultCode.Success)
  29. {
  30. WriteLocationName(context, deviceLocationName);
  31. }
  32. return result;
  33. }
  34. [CommandHipc(1)]
  35. // SetDeviceLocationName(nn::time::LocationName)
  36. public ResultCode SetDeviceLocationName(ServiceCtx context)
  37. {
  38. if (!_writePermission)
  39. {
  40. return ResultCode.PermissionDenied;
  41. }
  42. return ResultCode.NotImplemented;
  43. }
  44. [CommandHipc(2)]
  45. // GetTotalLocationNameCount() -> u32
  46. public ResultCode GetTotalLocationNameCount(ServiceCtx context)
  47. {
  48. ResultCode result = _timeZoneManager.GetTotalLocationNameCount(out uint totalLocationNameCount);
  49. if (result == ResultCode.Success)
  50. {
  51. context.ResponseData.Write(totalLocationNameCount);
  52. }
  53. return ResultCode.Success;
  54. }
  55. [CommandHipc(3)]
  56. // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
  57. public ResultCode LoadLocationNameList(ServiceCtx context)
  58. {
  59. return ResultCode.NotImplemented;
  60. }
  61. [CommandHipc(4)]
  62. // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
  63. public ResultCode LoadTimeZoneRule(ServiceCtx context)
  64. {
  65. return ResultCode.NotImplemented;
  66. }
  67. [CommandHipc(5)] // 2.0.0+
  68. // GetTimeZoneRuleVersion() -> nn::time::TimeZoneRuleVersion
  69. public ResultCode GetTimeZoneRuleVersion(ServiceCtx context)
  70. {
  71. ResultCode result = _timeZoneManager.GetTimeZoneRuleVersion(out UInt128 timeZoneRuleVersion);
  72. if (result == ResultCode.Success)
  73. {
  74. context.ResponseData.WriteStruct(timeZoneRuleVersion);
  75. }
  76. return result;
  77. }
  78. [CommandHipc(6)] // 5.0.0+
  79. // GetDeviceLocationNameAndUpdatedTime() -> (nn::time::LocationName, nn::time::SteadyClockTimePoint)
  80. public ResultCode GetDeviceLocationNameAndUpdatedTime(ServiceCtx context)
  81. {
  82. ResultCode result = _timeZoneManager.GetDeviceLocationName(out string deviceLocationName);
  83. if (result == ResultCode.Success)
  84. {
  85. result = _timeZoneManager.GetUpdatedTime(out SteadyClockTimePoint timeZoneUpdateTimePoint);
  86. if (result == ResultCode.Success)
  87. {
  88. WriteLocationName(context, deviceLocationName);
  89. // Skip padding
  90. context.ResponseData.BaseStream.Position += 0x4;
  91. context.ResponseData.WriteStruct(timeZoneUpdateTimePoint);
  92. }
  93. }
  94. return result;
  95. }
  96. [CommandHipc(7)] // 9.0.0+
  97. // SetDeviceLocationNameWithTimeZoneRule(nn::time::LocationName locationName, buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary)
  98. public ResultCode SetDeviceLocationNameWithTimeZoneRule(ServiceCtx context)
  99. {
  100. if (!_writePermission)
  101. {
  102. return ResultCode.PermissionDenied;
  103. }
  104. (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
  105. string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24);
  106. ResultCode result;
  107. byte[] temp = new byte[bufferSize];
  108. context.Memory.Read(bufferPosition, temp);
  109. using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
  110. {
  111. result = _timeZoneManager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream);
  112. }
  113. return result;
  114. }
  115. [CommandHipc(8)] // 9.0.0+
  116. // ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16>
  117. public ResultCode ParseTimeZoneBinary(ServiceCtx context)
  118. {
  119. (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
  120. ulong timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position;
  121. ulong timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size;
  122. if (timeZoneRuleBufferSize != 0x4000)
  123. {
  124. // TODO: find error code here
  125. Logger.Error?.Print(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{timeZoneRuleBufferSize:x} (expected 0x4000)");
  126. throw new InvalidOperationException();
  127. }
  128. ResultCode result;
  129. byte[] temp = new byte[bufferSize];
  130. context.Memory.Read(bufferPosition, temp);
  131. using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
  132. {
  133. using (WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
  134. {
  135. ref TimeZoneRule rule = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
  136. result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream);
  137. }
  138. }
  139. return result;
  140. }
  141. [CommandHipc(20)] // 9.0.0+
  142. // GetDeviceLocationNameOperationEventReadableHandle() -> handle<copy>
  143. public ResultCode GetDeviceLocationNameOperationEventReadableHandle(ServiceCtx context)
  144. {
  145. return ResultCode.NotImplemented;
  146. }
  147. [CommandHipc(100)]
  148. // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
  149. public ResultCode ToCalendarTime(ServiceCtx context)
  150. {
  151. long posixTime = context.RequestData.ReadInt64();
  152. ulong bufferPosition = context.Request.SendBuff[0].Position;
  153. ulong bufferSize = context.Request.SendBuff[0].Size;
  154. if (bufferSize != 0x4000)
  155. {
  156. // TODO: find error code here
  157. Logger.Error?.Print(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)");
  158. throw new InvalidOperationException();
  159. }
  160. ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(bufferPosition, (int)bufferSize));
  161. ResultCode resultCode = _timeZoneManager.ToCalendarTime(in rules[0], posixTime, out CalendarInfo calendar);
  162. if (resultCode == 0)
  163. {
  164. context.ResponseData.WriteStruct(calendar);
  165. }
  166. return resultCode;
  167. }
  168. [CommandHipc(101)]
  169. // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
  170. public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context)
  171. {
  172. long posixTime = context.RequestData.ReadInt64();
  173. ResultCode resultCode = _timeZoneManager.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar);
  174. if (resultCode == ResultCode.Success)
  175. {
  176. context.ResponseData.WriteStruct(calendar);
  177. }
  178. return resultCode;
  179. }
  180. [CommandHipc(201)]
  181. // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
  182. public ResultCode ToPosixTime(ServiceCtx context)
  183. {
  184. ulong inBufferPosition = context.Request.SendBuff[0].Position;
  185. ulong inBufferSize = context.Request.SendBuff[0].Size;
  186. CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
  187. if (inBufferSize != 0x4000)
  188. {
  189. // TODO: find error code here
  190. Logger.Error?.Print(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{inBufferSize:x} (expected 0x4000)");
  191. throw new InvalidOperationException();
  192. }
  193. ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(inBufferPosition, (int)inBufferSize));
  194. ResultCode resultCode = _timeZoneManager.ToPosixTime(in rules[0], calendarTime, out long posixTime);
  195. if (resultCode == ResultCode.Success)
  196. {
  197. ulong outBufferPosition = context.Request.RecvListBuff[0].Position;
  198. ulong outBufferSize = context.Request.RecvListBuff[0].Size;
  199. context.Memory.Write(outBufferPosition, posixTime);
  200. context.ResponseData.Write(1);
  201. }
  202. return resultCode;
  203. }
  204. [CommandHipc(202)]
  205. // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
  206. public ResultCode ToPosixTimeWithMyRule(ServiceCtx context)
  207. {
  208. CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
  209. ResultCode resultCode = _timeZoneManager.ToPosixTimeWithMyRules(calendarTime, out long posixTime);
  210. if (resultCode == ResultCode.Success)
  211. {
  212. ulong outBufferPosition = context.Request.RecvListBuff[0].Position;
  213. ulong outBufferSize = context.Request.RecvListBuff[0].Size;
  214. context.Memory.Write(outBufferPosition, posixTime);
  215. // There could be only one result on one calendar as leap seconds aren't supported.
  216. context.ResponseData.Write(1);
  217. }
  218. return resultCode;
  219. }
  220. private void WriteLocationName(ServiceCtx context, string locationName)
  221. {
  222. char[] locationNameArray = locationName.ToCharArray();
  223. int padding = 0x24 - locationNameArray.Length;
  224. Debug.Assert(padding >= 0, "LocationName exceeded limit (0x24 bytes)");
  225. context.ResponseData.Write(locationNameArray);
  226. for (int index = 0; index < padding; index++)
  227. {
  228. context.ResponseData.Write((byte)0);
  229. }
  230. }
  231. }
  232. }