1
0
Fork 0
mirror of https://github.com/Ryujinx/Ryujinx.git synced 2024-09-23 15:34:01 +01:00
Ryujinx/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs
gdkchan 0c87bf9ea4
Refactor CPU interface to allow the implementation of other CPU emulators (#3362)
* Refactor CPU interface

* Use IExecutionContext interface on SVC handler, change how CPU interrupts invokes the handlers

* Make CpuEngine take a ITickSource rather than returning one

The previous implementation had the scenario where the CPU engine had to implement the tick source in mind, like for example, when we have a hypervisor and the game can read CNTPCT on the host directly. However given that we need to do conversion due to different frequencies anyway, it's not worth it. It's better to just let the user pass the tick source and redirect any reads to CNTPCT to the user tick source

* XML docs for the public interfaces

* PPTC invalidation due to NativeInterface function name changes

* Fix build of the CPU tests

* PR feedback
2022-05-31 16:29:35 -03:00

302 lines
11 KiB
C#

using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.HLE.Utilities;
using System;
using System.Diagnostics;
using System.IO;
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
{
class ITimeZoneServiceForPsc : IpcService
{
private TimeZoneManager _timeZoneManager;
private bool _writePermission;
public ITimeZoneServiceForPsc(TimeZoneManager timeZoneManager, bool writePermission)
{
_timeZoneManager = timeZoneManager;
_writePermission = writePermission;
}
[CommandHipc(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;
}
[CommandHipc(1)]
// SetDeviceLocationName(nn::time::LocationName)
public ResultCode SetDeviceLocationName(ServiceCtx context)
{
if (!_writePermission)
{
return ResultCode.PermissionDenied;
}
return ResultCode.NotImplemented;
}
[CommandHipc(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;
}
[CommandHipc(3)]
// LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
public ResultCode LoadLocationNameList(ServiceCtx context)
{
return ResultCode.NotImplemented;
}
[CommandHipc(4)]
// LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
public ResultCode LoadTimeZoneRule(ServiceCtx context)
{
return ResultCode.NotImplemented;
}
[CommandHipc(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;
}
[CommandHipc(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;
}
[CommandHipc(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;
}
(ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24);
ResultCode result;
byte[] temp = new byte[bufferSize];
context.Memory.Read(bufferPosition, temp);
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
{
result = _timeZoneManager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream);
}
return result;
}
[CommandHipc(8)] // 9.0.0+
// ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16>
public ResultCode ParseTimeZoneBinary(ServiceCtx context)
{
(ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
ulong timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position;
ulong timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size;
if (timeZoneRuleBufferSize != 0x4000)
{
// TODO: find error code here
Logger.Error?.Print(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{timeZoneRuleBufferSize:x} (expected 0x4000)");
throw new InvalidOperationException();
}
ResultCode result;
byte[] temp = new byte[bufferSize];
context.Memory.Read(bufferPosition, temp);
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
{
result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream);
if (result == ResultCode.Success)
{
MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule);
}
}
return result;
}
[CommandHipc(20)] // 9.0.0+
// GetDeviceLocationNameOperationEventReadableHandle() -> handle<copy>
public ResultCode GetDeviceLocationNameOperationEventReadableHandle(ServiceCtx context)
{
return ResultCode.NotImplemented;
}
[CommandHipc(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();
ulong bufferPosition = context.Request.SendBuff[0].Position;
ulong bufferSize = context.Request.SendBuff[0].Size;
if (bufferSize != 0x4000)
{
// TODO: find error code here
Logger.Error?.Print(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;
}
[CommandHipc(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 == ResultCode.Success)
{
context.ResponseData.WriteStruct(calendar);
}
return resultCode;
}
[CommandHipc(201)]
// ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
public ResultCode ToPosixTime(ServiceCtx context)
{
ulong inBufferPosition = context.Request.SendBuff[0].Position;
ulong inBufferSize = context.Request.SendBuff[0].Size;
CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
if (inBufferSize != 0x4000)
{
// TODO: find error code here
Logger.Error?.Print(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 == ResultCode.Success)
{
ulong outBufferPosition = context.Request.RecvListBuff[0].Position;
ulong outBufferSize = context.Request.RecvListBuff[0].Size;
context.Memory.Write(outBufferPosition, posixTime);
context.ResponseData.Write(1);
}
return resultCode;
}
[CommandHipc(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 == ResultCode.Success)
{
ulong outBufferPosition = context.Request.RecvListBuff[0].Position;
ulong outBufferSize = context.Request.RecvListBuff[0].Size;
context.Memory.Write(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);
}
}
}
}