Implement psc's ITimeZoneService
This commit is contained in:
parent
9fba0659d6
commit
dc95f7535f
4 changed files with 560 additions and 2 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
263
Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManagerForPsc.cs
Normal file
263
Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManagerForPsc.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue