Implement psc's ITimeZoneService

This commit is contained in:
Thog 2019-09-29 23:06:16 +02:00
parent 9fba0659d6
commit dc95f7535f
No known key found for this signature in database
GPG key ID: 0CD291558FAFDBC6
4 changed files with 560 additions and 2 deletions

View file

@ -0,0 +1,295 @@
using ARMeilleure.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.HLE.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
{
class ITimeZoneServiceForPsc : IpcService
{
private TimeZoneManagerForPsc _timeZoneManager;
private bool _writePermission;
public ITimeZoneServiceForPsc(TimeZoneManagerForPsc timeZoneManager, bool writePermission)
{
_timeZoneManager = timeZoneManager;
_writePermission = writePermission;
}
[Command(0)]
// GetDeviceLocationName() -> nn::time::LocationName
public ResultCode GetDeviceLocationName(ServiceCtx context)
{
ResultCode result = _timeZoneManager.GetDeviceLocationName(out string deviceLocationName);
if (result == ResultCode.Success)
{
WriteLocationName(context, deviceLocationName);
}
return result;
}
[Command(1)]
// SetDeviceLocationName(nn::time::LocationName)
public ResultCode SetDeviceLocationName(ServiceCtx context)
{
if (!_writePermission)
{
return ResultCode.PermissionDenied;
}
return ResultCode.NotImplemented;
}
[Command(2)]
// GetTotalLocationNameCount() -> u32
public ResultCode GetTotalLocationNameCount(ServiceCtx context)
{
ResultCode result = _timeZoneManager.GetTotalLocationNameCount(out uint totalLocationNameCount);
if (result == ResultCode.Success)
{
context.ResponseData.Write(totalLocationNameCount);
}
return ResultCode.Success;
}
[Command(3)]
// LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
public ResultCode LoadLocationNameList(ServiceCtx context)
{
return ResultCode.NotImplemented;
}
[Command(4)]
// LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
public ResultCode LoadTimeZoneRule(ServiceCtx context)
{
return ResultCode.NotImplemented;
}
[Command(5)] // 2.0.0+
// GetTimeZoneRuleVersion() -> nn::time::TimeZoneRuleVersion
public ResultCode GetTimeZoneRuleVersion(ServiceCtx context)
{
ResultCode result = _timeZoneManager.GetTimeZoneRuleVersion(out UInt128 timeZoneRuleVersion);
if (result == ResultCode.Success)
{
context.ResponseData.WriteStruct(timeZoneRuleVersion);
}
return result;
}
[Command(6)] // 5.0.0+
// GetDeviceLocationNameAndUpdatedTime() -> (nn::time::LocationName, nn::time::SteadyClockTimePoint)
public ResultCode GetDeviceLocationNameAndUpdatedTime(ServiceCtx context)
{
ResultCode result = _timeZoneManager.GetDeviceLocationName(out string deviceLocationName);
if (result == ResultCode.Success)
{
result = _timeZoneManager.GetUpdatedTime(out SteadyClockTimePoint timeZoneUpdateTimePoint);
if (result == ResultCode.Success)
{
WriteLocationName(context, deviceLocationName);
// Skip padding
context.ResponseData.BaseStream.Position += 0x4;
context.ResponseData.WriteStruct(timeZoneUpdateTimePoint);
}
}
return result;
}
[Command(7)] // 9.0.0+
// SetDeviceLocationNameWithTimeZoneRule(nn::time::LocationName locationName, buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary)
public ResultCode SetDeviceLocationNameWithTimeZoneRule(ServiceCtx context)
{
if (!_writePermission)
{
return ResultCode.PermissionDenied;
}
(long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
ResultCode result;
using (MemoryStream timeZoneBinaryStream = new MemoryStream(context.Memory.ReadBytes(bufferPosition, bufferSize)))
{
result = _timeZoneManager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream);
}
return result;
}
[Command(8)] // 9.0.0+
// ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16>
public ResultCode ParseTimeZoneBinary(ServiceCtx context)
{
(long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
long timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position;
long timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size;
if (timeZoneRuleBufferSize != 0x4000)
{
// TODO: find error code here
Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{timeZoneRuleBufferSize:x} (expected 0x4000)");
throw new InvalidOperationException();
}
ResultCode result;
using (MemoryStream timeZoneBinaryStream = new MemoryStream(context.Memory.ReadBytes(bufferPosition, bufferSize)))
{
result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream);
if (result == ResultCode.Success)
{
MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule);
}
}
return result;
}
[Command(20)] // 9.0.0+
// GetDeviceLocationNameOperationEventReadableHandle() -> handle<copy>
public ResultCode GetDeviceLocationNameOperationEventReadableHandle(ServiceCtx context)
{
return ResultCode.NotImplemented;
}
[Command(100)]
// ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
public ResultCode ToCalendarTime(ServiceCtx context)
{
long posixTime = context.RequestData.ReadInt64();
long bufferPosition = context.Request.SendBuff[0].Position;
long bufferSize = context.Request.SendBuff[0].Size;
if (bufferSize != 0x4000)
{
// TODO: find error code here
Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)");
throw new InvalidOperationException();
}
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition);
ResultCode resultCode = TimeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar);
if (resultCode == 0)
{
context.ResponseData.WriteStruct(calendar);
}
return resultCode;
}
[Command(101)]
// ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context)
{
long posixTime = context.RequestData.ReadInt64();
ResultCode resultCode = _timeZoneManager.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar);
if (resultCode == 0)
{
context.ResponseData.WriteStruct(calendar);
}
return resultCode;
}
[Command(201)]
// ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
public ResultCode ToPosixTime(ServiceCtx context)
{
long inBufferPosition = context.Request.SendBuff[0].Position;
long inBufferSize = context.Request.SendBuff[0].Size;
CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
if (inBufferSize != 0x4000)
{
// TODO: find error code here
Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{inBufferSize:x} (expected 0x4000)");
throw new InvalidOperationException();
}
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition);
ResultCode resultCode = TimeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime);
if (resultCode == 0)
{
long outBufferPosition = context.Request.RecvListBuff[0].Position;
long outBufferSize = context.Request.RecvListBuff[0].Size;
context.Memory.WriteInt64(outBufferPosition, posixTime);
context.ResponseData.Write(1);
}
return resultCode;
}
[Command(202)]
// ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
public ResultCode ToPosixTimeWithMyRule(ServiceCtx context)
{
CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
ResultCode resultCode = _timeZoneManager.ToPosixTimeWithMyRules(calendarTime, out long posixTime);
if (resultCode == 0)
{
long outBufferPosition = context.Request.RecvListBuff[0].Position;
long outBufferSize = context.Request.RecvListBuff[0].Size;
context.Memory.WriteInt64(outBufferPosition, posixTime);
// There could be only one result on one calendar as leap seconds aren't supported.
context.ResponseData.Write(1);
}
return resultCode;
}
private void WriteLocationName(ServiceCtx context, string locationName)
{
char[] locationNameArray = locationName.ToCharArray();
int padding = 0x24 - locationNameArray.Length;
Debug.Assert(padding < 0, "LocationName exceeded limit (0x24 bytes)");
context.ResponseData.Write(locationNameArray);
for (int index = 0; index < padding; index++)
{
context.ResponseData.Write((byte)0);
}
}
}
}

View file

@ -903,7 +903,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return ParsePosixName(name.ToCharArray(), out outRules, false);
}
internal static unsafe bool LoadTimeZoneRules(out TimeZoneRule outRules, Stream inputData)
internal static unsafe bool ParseTimeZoneBinary(out TimeZoneRule outRules, Stream inputData)
{
outRules = new TimeZoneRule
{

View file

@ -222,7 +222,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
Stream tzIfStream = romfs.OpenFile($"zoneinfo/{locationName}", OpenMode.Read).AsStream();
if (!TimeZone.LoadTimeZoneRules(out outRules, tzIfStream))
if (!TimeZone.ParseTimeZoneBinary(out outRules, tzIfStream))
{
return ResultCode.TimeZoneConversionFailed;
}

View file

@ -0,0 +1,263 @@
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.Utilities;
using System.IO;
using System.Threading;
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
class TimeZoneManagerForPsc
{
private bool _isInitialized;
private TimeZoneRule _myRules;
private string _deviceLocationName;
private UInt128 _timeZoneRuleVersion;
private uint _totalLocationNameCount;
private SteadyClockTimePoint _timeZoneUpdateTimePoint;
private object _lock;
public TimeZoneManagerForPsc()
{
_isInitialized = false;
_deviceLocationName = null;
_timeZoneRuleVersion = new UInt128();
_lock = new object();
// Empty rules
_myRules = new TimeZoneRule
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
_timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom();
}
public bool IsInitialized()
{
return _isInitialized;
}
public void MarkInitialized()
{
_isInitialized = true;
}
public ResultCode GetDeviceLocationName(out string deviceLocationName)
{
ResultCode result = ResultCode.UninitializedClock;
deviceLocationName = null;
Monitor.Enter(_lock);
if (_isInitialized)
{
deviceLocationName = _deviceLocationName;
result = ResultCode.Success;
}
Monitor.Exit(_lock);
return result;
}
public ResultCode SetDeviceLocationNameWithTimeZoneRule(string locationName, Stream timeZoneBinaryStream)
{
ResultCode result = ResultCode.TimeZoneConversionFailed;
Monitor.Enter(_lock);
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out TimeZoneRule rules, timeZoneBinaryStream);
if (timeZoneConversionSuccess)
{
_deviceLocationName = locationName;
_myRules = rules;
result = ResultCode.Success;
}
Monitor.Exit(_lock);
return result;
}
public void SetTotalLocationNameCount(uint totalLocationNameCount)
{
Monitor.Enter(_lock);
_totalLocationNameCount = totalLocationNameCount;
Monitor.Exit(_lock);
}
public ResultCode GetTotalLocationNameCount(out uint totalLocationNameCount)
{
ResultCode result = ResultCode.UninitializedClock;
totalLocationNameCount = 0;
Monitor.Enter(_lock);
if (_isInitialized)
{
totalLocationNameCount = _totalLocationNameCount;
result = ResultCode.Success;
}
Monitor.Exit(_lock);
return result;
}
public ResultCode SetUpdatedTime(SteadyClockTimePoint timeZoneUpdatedTimePoint)
{
ResultCode result = ResultCode.UninitializedClock;
Monitor.Enter(_lock);
if (_isInitialized)
{
_timeZoneUpdateTimePoint = timeZoneUpdatedTimePoint;
result = ResultCode.Success;
}
Monitor.Exit(_lock);
return result;
}
public ResultCode GetUpdatedTime(out SteadyClockTimePoint timeZoneUpdatedTimePoint)
{
ResultCode result;
Monitor.Enter(_lock);
if (_isInitialized)
{
timeZoneUpdatedTimePoint = _timeZoneUpdateTimePoint;
result = ResultCode.Success;
}
else
{
timeZoneUpdatedTimePoint = SteadyClockTimePoint.GetRandom();
result = ResultCode.UninitializedClock;
}
Monitor.Exit(_lock);
return result;
}
public ResultCode ParseTimeZoneRuleBinary(out TimeZoneRule outRules, Stream timeZoneBinaryStream)
{
ResultCode result = ResultCode.Success;
Monitor.Enter(_lock);
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out outRules, timeZoneBinaryStream);
if (!timeZoneConversionSuccess)
{
result = ResultCode.TimeZoneConversionFailed;
}
Monitor.Exit(_lock);
return result;
}
public void SetTimeZoneRuleVersion(UInt128 timeZoneRuleVersion)
{
Monitor.Enter(_lock);
_timeZoneRuleVersion = timeZoneRuleVersion;
Monitor.Exit(_lock);
}
public ResultCode GetTimeZoneRuleVersion(out UInt128 timeZoneRuleVersion)
{
ResultCode result;
Monitor.Enter(_lock);
if (_isInitialized)
{
timeZoneRuleVersion = _timeZoneRuleVersion;
result = ResultCode.Success;
}
else
{
timeZoneRuleVersion = new UInt128();
result = ResultCode.UninitializedClock;
}
Monitor.Exit(_lock);
return result;
}
public ResultCode ToCalendarTimeWithMyRules(long time, out CalendarInfo calendar)
{
ResultCode result;
Monitor.Enter(_lock);
if (_isInitialized)
{
result = ToCalendarTime(_myRules, time, out calendar);
}
else
{
calendar = new CalendarInfo();
result = ResultCode.UninitializedClock;
}
Monitor.Exit(_lock);
return result;
}
public ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
{
Monitor.Enter(_lock);
ResultCode result = TimeZone.ToCalendarTime(rules, time, out calendar);
Monitor.Exit(_lock);
return result;
}
public ResultCode ToPosixTimeWithMyRules(CalendarTime calendarTime, out long posixTime)
{
ResultCode result;
Monitor.Enter(_lock);
if (_isInitialized)
{
result = ToPosixTime(_myRules, calendarTime, out posixTime);
}
else
{
posixTime = 0;
result = ResultCode.UninitializedClock;
}
Monitor.Exit(_lock);
return result;
}
public ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
{
Monitor.Enter(_lock);
ResultCode result = TimeZone.ToPosixTime(rules, calendarTime, out posixTime);
Monitor.Exit(_lock);
return result;
}
}
}