ITimeZoneService.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. using ChocolArm64.Memory;
  2. using Ryujinx.Common;
  3. using Ryujinx.Common.Logging;
  4. using Ryujinx.HLE.HOS.Ipc;
  5. using Ryujinx.HLE.HOS.Services.Time.TimeZone;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Text;
  9. using static Ryujinx.HLE.HOS.ErrorCode;
  10. namespace Ryujinx.HLE.HOS.Services.Time
  11. {
  12. class ITimeZoneService : IpcService
  13. {
  14. private Dictionary<int, ServiceProcessRequest> _commands;
  15. public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
  16. public ITimeZoneService()
  17. {
  18. _commands = new Dictionary<int, ServiceProcessRequest>
  19. {
  20. { 0, GetDeviceLocationName },
  21. { 1, SetDeviceLocationName },
  22. { 2, GetTotalLocationNameCount },
  23. { 3, LoadLocationNameList },
  24. { 4, LoadTimeZoneRule },
  25. //{ 5, GetTimeZoneRuleVersion }, // 2.0.0+
  26. //{ 6, GetDeviceLocationNameAndUpdatedTime }, // 5.0.0+
  27. { 100, ToCalendarTime },
  28. { 101, ToCalendarTimeWithMyRule },
  29. { 201, ToPosixTime },
  30. { 202, ToPosixTimeWithMyRule }
  31. };
  32. }
  33. // GetDeviceLocationName() -> nn::time::LocationName
  34. public long GetDeviceLocationName(ServiceCtx context)
  35. {
  36. char[] tzName = TimeZoneManager.Instance.GetDeviceLocationName().ToCharArray();
  37. int padding = 0x24 - tzName.Length;
  38. if (padding < 0)
  39. {
  40. return MakeError(ErrorModule.Time, TimeError.LocationNameTooLong);
  41. }
  42. context.ResponseData.Write(tzName);
  43. for (int index = 0; index < padding; index++)
  44. {
  45. context.ResponseData.Write((byte)0);
  46. }
  47. return 0;
  48. }
  49. // SetDeviceLocationName(nn::time::LocationName)
  50. public long SetDeviceLocationName(ServiceCtx context)
  51. {
  52. string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
  53. return TimeZoneManager.Instance.SetDeviceLocationName(locationName);
  54. }
  55. // GetTotalLocationNameCount() -> u32
  56. public long GetTotalLocationNameCount(ServiceCtx context)
  57. {
  58. context.ResponseData.Write(TimeZoneManager.Instance.GetTotalLocationNameCount());
  59. return 0;
  60. }
  61. // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
  62. public long LoadLocationNameList(ServiceCtx context)
  63. {
  64. // TODO: fix logic to use index
  65. uint index = context.RequestData.ReadUInt32();
  66. long bufferPosition = context.Request.ReceiveBuff[0].Position;
  67. long bufferSize = context.Request.ReceiveBuff[0].Size;
  68. uint errorCode = TimeZoneManager.Instance.LoadLocationNameList(index, out string[] locationNameArray, (uint)bufferSize / 0x24);
  69. if (errorCode == 0)
  70. {
  71. uint offset = 0;
  72. foreach (string locationName in locationNameArray)
  73. {
  74. int padding = 0x24 - locationName.Length;
  75. if (padding < 0)
  76. {
  77. return MakeError(ErrorModule.Time, TimeError.LocationNameTooLong);
  78. }
  79. context.Memory.WriteBytes(bufferPosition + offset, Encoding.ASCII.GetBytes(locationName));
  80. MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + locationName.Length, padding);
  81. offset += 0x24;
  82. }
  83. context.ResponseData.Write((uint)locationNameArray.Length);
  84. }
  85. return errorCode;
  86. }
  87. // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
  88. public long LoadTimeZoneRule(ServiceCtx context)
  89. {
  90. long bufferPosition = context.Request.ReceiveBuff[0].Position;
  91. long bufferSize = context.Request.ReceiveBuff[0].Size;
  92. if (bufferSize != 0x4000)
  93. {
  94. // TODO: find error code here
  95. Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)");
  96. throw new InvalidOperationException();
  97. }
  98. string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
  99. long resultCode = TimeZoneManager.Instance.LoadTimeZoneRules(out TimeZoneRule rules, locationName);
  100. // Write TimeZoneRule if success
  101. if (resultCode == 0)
  102. {
  103. MemoryHelper.Write(context.Memory, bufferPosition, rules);
  104. }
  105. return resultCode;
  106. }
  107. // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
  108. public long ToCalendarTime(ServiceCtx context)
  109. {
  110. long posixTime = context.RequestData.ReadInt64();
  111. long bufferPosition = context.Request.SendBuff[0].Position;
  112. long bufferSize = context.Request.SendBuff[0].Size;
  113. if (bufferSize != 0x4000)
  114. {
  115. // TODO: find error code here
  116. Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)");
  117. throw new InvalidOperationException();
  118. }
  119. TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition);
  120. long resultCode = TimeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar);
  121. if (resultCode == 0)
  122. {
  123. context.ResponseData.WriteStruct(calendar);
  124. }
  125. return resultCode;
  126. }
  127. // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
  128. public long ToCalendarTimeWithMyRule(ServiceCtx context)
  129. {
  130. long posixTime = context.RequestData.ReadInt64();
  131. long resultCode = TimeZoneManager.Instance.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar);
  132. if (resultCode == 0)
  133. {
  134. context.ResponseData.WriteStruct(calendar);
  135. }
  136. return resultCode;
  137. }
  138. // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
  139. public long ToPosixTime(ServiceCtx context)
  140. {
  141. long inBufferPosition = context.Request.SendBuff[0].Position;
  142. long inBufferSize = context.Request.SendBuff[0].Size;
  143. CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
  144. if (inBufferSize != 0x4000)
  145. {
  146. // TODO: find error code here
  147. Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{inBufferSize:x} (expected 0x4000)");
  148. throw new InvalidOperationException();
  149. }
  150. TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition);
  151. long resultCode = TimeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime);
  152. if (resultCode == 0)
  153. {
  154. long outBufferPosition = context.Request.RecvListBuff[0].Position;
  155. long outBufferSize = context.Request.RecvListBuff[0].Size;
  156. context.Memory.WriteInt64(outBufferPosition, posixTime);
  157. context.ResponseData.Write(1);
  158. }
  159. return resultCode;
  160. }
  161. // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
  162. public long ToPosixTimeWithMyRule(ServiceCtx context)
  163. {
  164. CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
  165. long resultCode = TimeZoneManager.Instance.ToPosixTimeWithMyRules(calendarTime, out long posixTime);
  166. if (resultCode == 0)
  167. {
  168. long outBufferPosition = context.Request.RecvListBuff[0].Position;
  169. long outBufferSize = context.Request.RecvListBuff[0].Size;
  170. context.Memory.WriteInt64(outBufferPosition, posixTime);
  171. // There could be only one result on one calendar as leap seconds aren't supported.
  172. context.ResponseData.Write(1);
  173. }
  174. return resultCode;
  175. }
  176. }
  177. }