ITimeZoneServiceForPsc.cs 11 KB

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