Clean up ITimeZoneService

Add error codes and simplify parsing
This commit is contained in:
Thog 2019-07-01 21:42:27 +02:00
commit 9aa4d2ba6b
No known key found for this signature in database
GPG key ID: 0CD291558FAFDBC6
3 changed files with 206 additions and 53 deletions

View file

@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Ipc;
using System; using System;
@ -27,6 +28,8 @@ namespace Ryujinx.HLE.HOS.Services.Time
{ 2, GetTotalLocationNameCount }, { 2, GetTotalLocationNameCount },
{ 3, LoadLocationNameList }, { 3, LoadLocationNameList },
{ 4, LoadTimeZoneRule }, { 4, LoadTimeZoneRule },
//{ 5, GetTimeZoneRuleVersion }, // 2.0.0+
//{ 6, GetDeviceLocationNameAndUpdatedTime }, // 5.0.0+
{ 100, ToCalendarTime }, { 100, ToCalendarTime },
{ 101, ToCalendarTimeWithMyRule }, { 101, ToCalendarTimeWithMyRule },
{ 201, ToPosixTime }, { 201, ToPosixTime },
@ -34,6 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
}; };
} }
// GetDeviceLocationName() -> nn::time::LocationName
public long GetDeviceLocationName(ServiceCtx context) public long GetDeviceLocationName(ServiceCtx context)
{ {
char[] tzName = _timeZone.Id.ToCharArray(); char[] tzName = _timeZone.Id.ToCharArray();
@ -50,6 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
return 0; return 0;
} }
// SetDeviceLocationName(nn::time::LocationName)
public long SetDeviceLocationName(ServiceCtx context) public long SetDeviceLocationName(ServiceCtx context)
{ {
byte[] locationName = context.RequestData.ReadBytes(0x24); byte[] locationName = context.RequestData.ReadBytes(0x24);
@ -64,12 +69,13 @@ namespace Ryujinx.HLE.HOS.Services.Time
} }
catch (TimeZoneNotFoundException) catch (TimeZoneNotFoundException)
{ {
resultCode = MakeError(ErrorModule.Time, 0x3dd); resultCode = MakeError(ErrorModule.Time, TimeError.TimeZoneNotFound);
} }
return resultCode; return resultCode;
} }
// GetTotalLocationNameCount() -> u32
public long GetTotalLocationNameCount(ServiceCtx context) public long GetTotalLocationNameCount(ServiceCtx context)
{ {
context.ResponseData.Write(TimeZoneInfo.GetSystemTimeZones().Count); context.ResponseData.Write(TimeZoneInfo.GetSystemTimeZones().Count);
@ -77,8 +83,11 @@ namespace Ryujinx.HLE.HOS.Services.Time
return 0; return 0;
} }
// LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
public long LoadLocationNameList(ServiceCtx context) public long LoadLocationNameList(ServiceCtx context)
{ {
// TODO: fix logic to use index
uint index = context.RequestData.ReadUInt32();
long bufferPosition = context.Response.SendBuff[0].Position; long bufferPosition = context.Response.SendBuff[0].Position;
long bufferSize = context.Response.SendBuff[0].Size; long bufferSize = context.Response.SendBuff[0].Size;
@ -92,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
int padding = 0x24 - tzData.Length; int padding = 0x24 - tzData.Length;
for (int index = 0; index < padding; index++) for (int i = 0; i < padding; i++)
{ {
context.ResponseData.Write((byte)0); context.ResponseData.Write((byte)0);
} }
@ -103,6 +112,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
return 0; return 0;
} }
// LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
public long LoadTimeZoneRule(ServiceCtx context) public long LoadTimeZoneRule(ServiceCtx context)
{ {
long bufferPosition = context.Request.ReceiveBuff[0].Position; long bufferPosition = context.Request.ReceiveBuff[0].Position;
@ -134,34 +144,13 @@ namespace Ryujinx.HLE.HOS.Services.Time
{ {
Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {tzId} (len: {tzId.Length})"); Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {tzId} (len: {tzId.Length})");
resultCode = MakeError(ErrorModule.Time, 0x3dd); resultCode = MakeError(ErrorModule.Time, TimeError.TimeZoneNotFound);
} }
return resultCode; return resultCode;
} }
private long ToCalendarTimeWithTz(ServiceCtx context, long posixTime, TimeZoneInfo info) // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
{
DateTime currentTime = Epoch.AddSeconds(posixTime);
currentTime = TimeZoneInfo.ConvertTimeFromUtc(currentTime, info);
context.ResponseData.Write((ushort)currentTime.Year);
context.ResponseData.Write((byte)currentTime.Month);
context.ResponseData.Write((byte)currentTime.Day);
context.ResponseData.Write((byte)currentTime.Hour);
context.ResponseData.Write((byte)currentTime.Minute);
context.ResponseData.Write((byte)currentTime.Second);
context.ResponseData.Write((byte)0); //MilliSecond ?
context.ResponseData.Write((int)currentTime.DayOfWeek);
context.ResponseData.Write(currentTime.DayOfYear - 1);
context.ResponseData.Write(new byte[8]); //TODO: Find out the names used.
context.ResponseData.Write((byte)(currentTime.IsDaylightSavingTime() ? 1 : 0));
context.ResponseData.Write((int)info.GetUtcOffset(currentTime).TotalSeconds);
return 0;
}
public long ToCalendarTime(ServiceCtx context) public long ToCalendarTime(ServiceCtx context)
{ {
long posixTime = context.RequestData.ReadInt64(); long posixTime = context.RequestData.ReadInt64();
@ -178,7 +167,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
string tzId = Encoding.ASCII.GetString(tzData).TrimEnd('\0'); string tzId = Encoding.ASCII.GetString(tzData).TrimEnd('\0');
long resultCode = 0; long resultCode;
// Check if the Time Zone exists, otherwise error out. // Check if the Time Zone exists, otherwise error out.
try try
@ -191,12 +180,13 @@ namespace Ryujinx.HLE.HOS.Services.Time
{ {
Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {tzId} (len: {tzId.Length})"); Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {tzId} (len: {tzId.Length})");
resultCode = MakeError(ErrorModule.Time, 0x3dd); resultCode = MakeError(ErrorModule.Time, TimeError.TimeZoneNotFound);
} }
return resultCode; return resultCode;
} }
// ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
public long ToCalendarTimeWithMyRule(ServiceCtx context) public long ToCalendarTimeWithMyRule(ServiceCtx context)
{ {
long posixTime = context.RequestData.ReadInt64(); long posixTime = context.RequestData.ReadInt64();
@ -204,19 +194,15 @@ namespace Ryujinx.HLE.HOS.Services.Time
return ToCalendarTimeWithTz(context, posixTime, _timeZone); return ToCalendarTimeWithTz(context, posixTime, _timeZone);
} }
// ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
public long ToPosixTime(ServiceCtx context) public long ToPosixTime(ServiceCtx context)
{ {
long bufferPosition = context.Request.SendBuff[0].Position; long bufferPosition = context.Request.SendBuff[0].Position;
long bufferSize = context.Request.SendBuff[0].Size; long bufferSize = context.Request.SendBuff[0].Size;
ushort year = context.RequestData.ReadUInt16(); CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
byte month = context.RequestData.ReadByte();
byte day = context.RequestData.ReadByte();
byte hour = context.RequestData.ReadByte();
byte minute = context.RequestData.ReadByte();
byte second = context.RequestData.ReadByte();
DateTime calendarTime = new DateTime(year, month, day, hour, minute, second); DateTime dateTime = new DateTime(calendarTime.year, calendarTime.month, calendarTime.day, calendarTime.hour, calendarTime.minute, calendarTime.second, DateTimeKind.Local);
if (bufferSize != 0x4000) if (bufferSize != 0x4000)
{ {
@ -235,30 +221,26 @@ namespace Ryujinx.HLE.HOS.Services.Time
{ {
TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(tzId); TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(tzId);
return ToPosixTimeWithTz(context, calendarTime, info); return ToPosixTimeWithTz(context, dateTime, info);
} }
catch (TimeZoneNotFoundException) catch (TimeZoneNotFoundException)
{ {
Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {tzId} (len: {tzId.Length})"); Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {tzId} (len: {tzId.Length})");
resultCode = MakeError(ErrorModule.Time, 0x3dd); resultCode = MakeError(ErrorModule.Time, TimeError.TimeZoneNotFound);
} }
return resultCode; return resultCode;
} }
// ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
public long ToPosixTimeWithMyRule(ServiceCtx context) public long ToPosixTimeWithMyRule(ServiceCtx context)
{ {
ushort year = context.RequestData.ReadUInt16(); CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
byte month = context.RequestData.ReadByte();
byte day = context.RequestData.ReadByte();
byte hour = context.RequestData.ReadByte();
byte minute = context.RequestData.ReadByte();
byte second = context.RequestData.ReadByte();
DateTime calendarTime = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Local); DateTime dateTime = new DateTime(calendarTime.year, calendarTime.month, calendarTime.day, calendarTime.hour, calendarTime.minute, calendarTime.second, DateTimeKind.Local);
return ToPosixTimeWithTz(context, calendarTime, _timeZone); return ToPosixTimeWithTz(context, dateTime, _timeZone);
} }
private long ToPosixTimeWithTz(ServiceCtx context, DateTime calendarTime, TimeZoneInfo info) private long ToPosixTimeWithTz(ServiceCtx context, DateTime calendarTime, TimeZoneInfo info)
@ -276,5 +258,37 @@ namespace Ryujinx.HLE.HOS.Services.Time
return 0; return 0;
} }
private long ToCalendarTimeWithTz(ServiceCtx context, long posixTime, TimeZoneInfo info)
{
DateTime currentTime = Epoch.AddSeconds(posixTime);
currentTime = TimeZoneInfo.ConvertTimeFromUtc(currentTime, info);
CalendarInfo calendar = new CalendarInfo()
{
time = new CalendarTime()
{
year = (short)currentTime.Year,
month = (sbyte)currentTime.Month,
day = (sbyte)currentTime.Day,
hour = (sbyte)currentTime.Hour,
minute = (sbyte)currentTime.Minute,
second = (sbyte)currentTime.Second,
},
additionalInfo = new CalendarAdditionalInfo()
{
dayOfWeek = (uint)currentTime.DayOfWeek,
dayOfYear = (uint)(currentTime.DayOfYear - 1),
isDaySavingTime = currentTime.IsDaylightSavingTime(),
gmtOffset = info.GetUtcOffset(currentTime).Seconds,
}
};
context.ResponseData.WriteStruct(calendar);
return 0;
}
} }
} }

View file

@ -0,0 +1,128 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time
{
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
public struct TimeTypeInfo
{
public int gmtOffset;
[MarshalAs(UnmanagedType.I1)]
public bool isDaySavingTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
char[] padding1;
public int abbreviationListIndex;
[MarshalAs(UnmanagedType.I1)]
public bool isStandardTimeDaylight;
[MarshalAs(UnmanagedType.I1)]
public bool isGMT;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
char[] padding2;
}
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)]
public struct TimeZoneRule
{
public const int TZ_MAX_TYPES = 128;
public const int TZ_MAX_CHARS = 50;
public const int TZ_MAX_LEAPS = 50;
public const int TZ_MAX_TIMES = 1000;
public const int TZNAME_MAX = 255;
public const int TZ_NAME_MAX = 2 * (TZNAME_MAX + 1);
public int timeCount;
public int typeCount;
public int charCount;
[MarshalAs(UnmanagedType.I1)]
public bool goBack;
[MarshalAs(UnmanagedType.I1)]
public bool goAhead;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TZ_MAX_TIMES)]
public long[] ats;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TZ_MAX_TIMES)]
public byte[] types;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TZ_MAX_TYPES)]
public TimeTypeInfo[] ttis;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TZ_NAME_MAX)]
public char[] chars;
public int defaultType;
}
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x2C)]
public struct TzifHeader
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public char[] magic;
public char version;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)]
public byte[] reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] ttisGMTCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] ttisSTDCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] leapCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] timeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] typeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] charCount;
}
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x8)]
public struct CalendarTime
{
public short year;
public sbyte month;
public sbyte day;
public sbyte hour;
public sbyte minute;
public sbyte second;
}
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x18, CharSet = CharSet.Ansi)]
public struct CalendarAdditionalInfo
{
public uint dayOfWeek;
public uint dayOfYear;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public char[] timezoneName;
[MarshalAs(UnmanagedType.I1)]
public bool isDaySavingTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
char[] padding;
public int gmtOffset;
}
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x20, CharSet = CharSet.Ansi)]
public struct CalendarInfo
{
public CalendarTime time;
public CalendarAdditionalInfo additionalInfo;
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.HLE.HOS.Services.Time
{
static class TimeError
{
public const int TimeNotFound = 200;
public const int Overflow = 201;
public const int OutOfRange = 902;
public const int TimeZoneConversionFailed = 903;
public const int TimeZoneNotFound = 989;
}
}