ITimeZoneServiceForPsc.cs 11 KB

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