ITimeZoneService.cs 7.9 KB

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