ITimeZoneServiceForPsc.cs 11 KB

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