ITimeZoneService.cs 7.9 KB

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