Implement many objects, improve logging. (#42)

* Implement many objects, improve logging.

Change and rename folders of Services
Add Logging of IpcMessage.
Add "lm" Log Service.
Parse Errors of SetTerminateResult
Add Svc Calls.
Add many object implementations.

* Corrections

Forgotten Debug Conf

* Corrections 2

* Corrections 3

* Corrections 4
This commit is contained in:
Ac_K 2018-02-25 19:58:16 +01:00 committed by gdkchan
parent fba0bf8732
commit e174100474
69 changed files with 660 additions and 27 deletions

View file

@ -26,6 +26,10 @@
Enable the Fatal Logging (Enabled in Debug recommanded).
- `Logging_Enable_Ipc` *(bool)*
Enable the Ipc Message Logging.
- `Logging_Enable_LogFile` *(bool)*
Enable writing the logging inside a Ryujinx.log file.

View file

@ -14,6 +14,7 @@ namespace Ryujinx.Core
public static bool LoggingEnableWarn { get; private set; }
public static bool LoggingEnableError { get; private set; }
public static bool LoggingEnableFatal { get; private set; }
public static bool LoggingEnableIpc { get; private set; }
public static bool LoggingEnableLogFile { get; private set; }
public static JoyCon FakeJoyCon { get; private set; }
@ -30,6 +31,7 @@ namespace Ryujinx.Core
LoggingEnableWarn = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
LoggingEnableIpc = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
FakeJoyCon = new JoyCon

View file

@ -1,6 +1,8 @@
using System;
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace Ryujinx.Core
{
@ -9,13 +11,14 @@ namespace Ryujinx.Core
private static Stopwatch ExecutionTime = new Stopwatch();
private const string LogFileName = "Ryujinx.log";
public static bool EnableInfo = Config.LoggingEnableInfo;
public static bool EnableTrace = Config.LoggingEnableTrace;
public static bool EnableDebug = Config.LoggingEnableDebug;
public static bool EnableWarn = Config.LoggingEnableWarn;
public static bool EnableError = Config.LoggingEnableError;
public static bool EnableFatal = Config.LoggingEnableFatal;
public static bool EnableLogFile = Config.LoggingEnableLogFile;
private static bool EnableInfo = Config.LoggingEnableInfo;
private static bool EnableTrace = Config.LoggingEnableTrace;
private static bool EnableDebug = Config.LoggingEnableDebug;
private static bool EnableWarn = Config.LoggingEnableWarn;
private static bool EnableError = Config.LoggingEnableError;
private static bool EnableFatal = Config.LoggingEnableFatal;
private static bool EnableIpc = Config.LoggingEnableIpc;
private static bool EnableLogFile = Config.LoggingEnableLogFile;
static Logging()
{
@ -128,5 +131,79 @@ namespace Ryujinx.Core
LogFile(Text);
}
}
public static void Ipc(byte[] Data, long CmdPtr, bool Domain)
{
if (EnableIpc)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(IpcLog.Message(Data, CmdPtr, Domain));
Console.ResetColor();
}
}
//https://www.codeproject.com/Articles/36747/Quick-and-Dirty-HexDump-of-a-Byte-Array
public static string HexDump(byte[] bytes, int bytesPerLine = 16)
{
if (bytes == null) return "<null>";
int bytesLength = bytes.Length;
char[] HexChars = "0123456789ABCDEF".ToCharArray();
int firstHexColumn =
8 // 8 characters for the address
+ 3; // 3 spaces
int firstCharColumn = firstHexColumn
+ bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space
+ (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th
+ 2; // 2 spaces
int lineLength = firstCharColumn
+ bytesPerLine // - characters to show the ascii value
+ Environment.NewLine.Length; // Carriage return and line feed (should normally be 2)
char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine;
StringBuilder result = new StringBuilder(expectedLines * lineLength);
for (int i = 0; i < bytesLength; i += bytesPerLine)
{
line[0] = HexChars[(i >> 28) & 0xF];
line[1] = HexChars[(i >> 24) & 0xF];
line[2] = HexChars[(i >> 20) & 0xF];
line[3] = HexChars[(i >> 16) & 0xF];
line[4] = HexChars[(i >> 12) & 0xF];
line[5] = HexChars[(i >> 8) & 0xF];
line[6] = HexChars[(i >> 4) & 0xF];
line[7] = HexChars[(i >> 0) & 0xF];
int hexColumn = firstHexColumn;
int charColumn = firstCharColumn;
for (int j = 0; j < bytesPerLine; j++)
{
if (j > 0 && (j & 7) == 0) hexColumn++;
if (i + j >= bytesLength)
{
line[hexColumn] = ' ';
line[hexColumn + 1] = ' ';
line[charColumn] = ' ';
}
else
{
byte b = bytes[i + j];
line[hexColumn] = HexChars[(b >> 4) & 0xF];
line[hexColumn + 1] = HexChars[b & 0xF];
line[charColumn] = (b < 32 ? '·' : (char)b);
}
hexColumn += 3;
charColumn++;
}
result.Append(line);
}
return result.ToString();
}
}
}

View file

@ -7,7 +7,7 @@ namespace Ryujinx.Core.OsHle.Handles
public AThread Thread { get; private set; }
public int ProcessorId { get; private set; }
public int Priority { get; private set; }
public int Priority { get; set; }
public int ThreadId => Thread.ThreadId;

View file

@ -0,0 +1,179 @@
using System;
using System.IO;
namespace Ryujinx.Core.OsHle.Ipc
{
public static class IpcLog
{
public static string Message(byte[] Data, long CmdPtr, bool Domain)
{
string IpcMessage = "";
using (MemoryStream MS = new MemoryStream(Data))
{
BinaryReader Reader = new BinaryReader(MS);
int Word0 = Reader.ReadInt32();
int Word1 = Reader.ReadInt32();
int Type = (Word0 & 0xffff);
int PtrBuffCount = (Word0 >> 16) & 0xf;
int SendBuffCount = (Word0 >> 20) & 0xf;
int RecvBuffCount = (Word0 >> 24) & 0xf;
int XchgBuffCount = (Word0 >> 28) & 0xf;
int RawDataSize = (Word1 >> 0) & 0x3ff;
int RecvListFlags = (Word1 >> 10) & 0xf;
bool HndDescEnable = ((Word1 >> 31) & 0x1) != 0;
IpcMessage += Environment.NewLine + $" {Logging.GetExecutionTime()} | IpcMessage >" + Environment.NewLine +
$" Type: {Enum.GetName(typeof(IpcMessageType), Type)}" + Environment.NewLine +
$" PtrBuffCount: {PtrBuffCount.ToString()}" + Environment.NewLine +
$" SendBuffCount: {SendBuffCount.ToString()}" + Environment.NewLine +
$" RecvBuffCount: {RecvBuffCount.ToString()}" + Environment.NewLine +
$" XchgBuffCount: {XchgBuffCount.ToString()}" + Environment.NewLine +
$" RawDataSize: {RawDataSize.ToString()}" + Environment.NewLine +
$" RecvListFlags: {RecvListFlags.ToString()}" + Environment.NewLine +
$" HndDescEnable: {HndDescEnable.ToString()}" + Environment.NewLine;
if (HndDescEnable)
{
int Word = Reader.ReadInt32();
bool HasPId = (Word & 1) != 0;
int[] ToCopy = new int[(Word >> 1) & 0xf];
int[] ToMove = new int[(Word >> 5) & 0xf];
long PId = HasPId ? Reader.ReadInt64() : 0;
for (int Index = 0; Index < ToCopy.Length; Index++)
{
ToCopy[Index] = Reader.ReadInt32();
}
for (int Index = 0; Index < ToMove.Length; Index++)
{
ToMove[Index] = Reader.ReadInt32();
}
IpcMessage += Environment.NewLine + " HndDesc:" + Environment.NewLine +
$" PId: {PId.ToString()}" + Environment.NewLine +
$" ToCopy.Length: {ToCopy.Length.ToString()}" + Environment.NewLine +
$" ToMove.Length: {ToMove.Length.ToString()}" + Environment.NewLine;
}
for (int Index = 0; Index < PtrBuffCount; Index++)
{
long IpcPtrBuffDescWord0 = Reader.ReadUInt32();
long IpcPtrBuffDescWord1 = Reader.ReadUInt32();
long Position = IpcPtrBuffDescWord1;
Position |= (IpcPtrBuffDescWord0 << 20) & 0x0f00000000;
Position |= (IpcPtrBuffDescWord0 << 30) & 0x7000000000;
int IpcPtrBuffDescIndex = ((int)IpcPtrBuffDescWord0 >> 0) & 0x03f;
IpcPtrBuffDescIndex |= ((int)IpcPtrBuffDescWord0 >> 3) & 0x1c0;
short Size = (short)(IpcPtrBuffDescWord0 >> 16);
IpcMessage += Environment.NewLine + $" PtrBuff[{Index}]:" + Environment.NewLine +
$" Position: {Position.ToString()}" + Environment.NewLine +
$" IpcPtrBuffDescIndex: {IpcPtrBuffDescIndex.ToString()}" + Environment.NewLine +
$" Size: {Size.ToString()}" + Environment.NewLine;
}
ReadIpcBuffValues(Reader, SendBuffCount, IpcMessage, "SendBuff");
ReadIpcBuffValues(Reader, RecvBuffCount, IpcMessage, "RecvBuff");
ReadIpcBuffValues(Reader, XchgBuffCount, IpcMessage, "XchgBuff");
RawDataSize *= 4;
long RecvListPos = Reader.BaseStream.Position + RawDataSize;
long Pad0 = 0;
if ((Reader.BaseStream.Position + CmdPtr & 0xf) != 0)
{
Pad0 = 0x10 - (Reader.BaseStream.Position + CmdPtr & 0xf);
}
Reader.BaseStream.Seek(Pad0, SeekOrigin.Current);
int RecvListCount = RecvListFlags - 2;
if (RecvListCount == 0)
{
RecvListCount = 1;
}
else if (RecvListCount < 0)
{
RecvListCount = 0;
}
if (Domain && (IpcMessageType)Type == IpcMessageType.Request)
{
int DomWord0 = Reader.ReadInt32();
int DomCmd = (DomWord0 & 0xff);
RawDataSize = (DomWord0 >> 16) & 0xffff;
int DomObjId = Reader.ReadInt32();
Reader.ReadInt64(); //Padding
IpcMessage += Environment.NewLine + $" Domain:" + Environment.NewLine +
$" DomCmd: {Enum.GetName(typeof(IpcDomCmd), DomCmd)}" + Environment.NewLine +
$" DomObjId: {DomObjId.ToString()}" + Environment.NewLine;
}
byte[] RawData = Reader.ReadBytes(RawDataSize);
IpcMessage += Environment.NewLine + $" RawData:" + Environment.NewLine + Logging.HexDump(RawData);
Reader.BaseStream.Seek(RecvListPos, SeekOrigin.Begin);
for (int Index = 0; Index < RecvListCount; Index++)
{
long RecvListBuffValue = Reader.ReadInt64();
long RecvListBuffPosition = RecvListBuffValue & 0xffffffffffff;
long RecvListBuffSize = (short)(RecvListBuffValue >> 48);
IpcMessage += Environment.NewLine + $" RecvList[{Index}]:" + Environment.NewLine +
$" Value: {RecvListBuffValue.ToString()}" + Environment.NewLine +
$" Position: {RecvListBuffPosition.ToString()}" + Environment.NewLine +
$" Size: {RecvListBuffSize.ToString()}" + Environment.NewLine;
}
}
return IpcMessage;
}
private static void ReadIpcBuffValues(BinaryReader Reader, int Count, string IpcMessage, string BufferName)
{
for (int Index = 0; Index < Count; Index++)
{
long Word0 = Reader.ReadUInt32();
long Word1 = Reader.ReadUInt32();
long Word2 = Reader.ReadUInt32();
long Position = Word1;
Position |= (Word2 << 4) & 0x0f00000000;
Position |= (Word2 << 34) & 0x7000000000;
long Size = Word0;
Size |= (Word2 << 8) & 0xf00000000;
int Flags = (int)Word2 & 3;
IpcMessage += Environment.NewLine + $" {BufferName}[{Index}]:" + Environment.NewLine +
$" Position: {Position.ToString()}" + Environment.NewLine +
$" Flags: {Flags.ToString()}" + Environment.NewLine +
$" Size: {Size.ToString()}" + Environment.NewLine;
}
}
}
}

View file

@ -41,6 +41,8 @@ namespace Ryujinx.Core.OsHle.Ipc
public IpcMessage(byte[] Data, long CmdPtr, bool Domain) : this()
{
Logging.Ipc(Data, CmdPtr, Domain);
using (MemoryStream MS = new MemoryStream(Data))
{
BinaryReader Reader = new BinaryReader(MS);

View file

@ -1,7 +0,0 @@
namespace Ryujinx.Core.OsHle.IpcServices
{
enum ErrorModule
{
Fs = 2,
}
}

View file

@ -1,4 +1,5 @@
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
using System.IO;
@ -19,6 +20,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{ 1, PopLaunchParameter },
{ 20, EnsureSaveData },
{ 21, GetDesiredLanguage },
{ 22, SetTerminateResult },
{ 40, NotifyRunning }
};
}
@ -52,6 +54,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
return 0;
}
public long SetTerminateResult(ServiceCtx Context)
{
int ErrorCode = Context.RequestData.ReadInt32();
int Module = ErrorCode & 0xFF;
int Description = (ErrorCode >> 9) & 0xFFF;
Logging.Info($"({(ErrorModule)Module}){2000 + Module}-{Description}");
return 0;
}
public long NotifyRunning(ServiceCtx Context)
{
Context.ResponseData.Write(1);

View file

@ -13,14 +13,21 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, Exit },
{ 10, SetScreenShotPermission },
{ 11, SetOperationModeChangedNotification },
{ 12, SetPerformanceModeChangedNotification },
{ 13, SetFocusHandlingMode },
{ 14, SetRestartMessageEnabled },
{ 16, SetOutOfFocusSuspendingEnabled }
};
}
public long Exit(ServiceCtx Context)
{
return 0;
}
public long SetScreenShotPermission(ServiceCtx Context)
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
@ -51,6 +58,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
return 0;
}
public long SetRestartMessageEnabled(ServiceCtx Context)
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
return 0;
}
public long SetOutOfFocusSuspendingEnabled(ServiceCtx Context)
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;

View file

@ -0,0 +1,63 @@
namespace Ryujinx.Core.OsHle.IpcServices
{
enum ErrorModule
{
Kernel = 1,
Fs = 2,
Nvidia_TransferMemory = 3,
Ncm = 5,
Dd = 6,
Lr = 8,
Loader = 9,
IPC_Command_Interface = 10,
IPC = 11,
Pm = 15,
Ns = 16,
Htc = 18,
Sm = 21,
RO_Userland = 22,
SdMmc = 24,
Spl = 26,
Ethc = 100,
I2C = 101,
Settings = 105,
Nifm = 110,
Display = 114,
Ntc = 116,
Fdm = 117,
Pcie = 120,
Friends = 121,
SSL = 123,
Account = 124,
Mii = 126,
Am = 128,
Play_Report = 129,
Pcv = 133,
Omm = 134,
Nim = 137,
Psc = 138,
Usb = 140,
Nsd = 141,
Btm = 143,
Erpt = 147,
Apm = 148,
Audio = 153,
Npns = 154,
Arp = 157,
Boot = 158,
Nfc = 161,
Userland_Assert = 162,
Userland_Crash = 168,
Hid = 203,
Capture = 206,
Libnx = 345,
Homebrew_ABI = 346,
Homebrew_Loader = 347,
libnx_Nvidia_Errors = 348,
Tc = 651,
General_Web_Applet = 800,
Wifi_Web_Auth_Applet = 809,
Whitelisted_Applet = 810,
ShopN = 811
}
}

View file

@ -11,7 +11,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
public IActiveApplicationDeviceList()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>() { };
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, ActivateVibrationDevice }
};
}
public long ActivateVibrationDevice(ServiceCtx Context)
{
int VibrationDeviceHandle = Context.RequestData.ReadInt32();
return 0;
}
}
}

View file

@ -24,6 +24,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
{ 103, ActivateNpad },
{ 120, SetNpadJoyHoldType },
{ 121, GetNpadJoyHoldType },
{ 200, GetVibrationDeviceInfo },
{ 203, CreateActiveVibrationDeviceList },
};
}
@ -88,6 +89,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
return 0;
}
public long GetVibrationDeviceInfo(ServiceCtx Context)
{
int VibrationDeviceHandle = Context.RequestData.ReadInt32();
Context.ResponseData.Write(0L); //VibrationDeviceInfoForIpc
return 0;
}
public long CreateActiveVibrationDeviceList(ServiceCtx Context)
{
MakeObject(Context, new IActiveApplicationDeviceList());

View file

@ -0,0 +1,143 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Ryujinx.Core.OsHle.IpcServices.Lm
{
class ILogger : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ILogger()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Log }
};
}
enum Flags
{
Padding,
IsHead,
IsTail
}
enum Severity
{
Trace,
Info,
Warning,
Error,
Critical
}
enum Field
{
Padding,
Skip,
Message,
Line,
Filename,
Function,
Module,
Thread
}
public long Log(ServiceCtx Context)
{
long BufferPosition = Context.Request.PtrBuff[0].Position;
long BufferLen = Context.Request.PtrBuff[0].Size;
byte[] LogBuffer = AMemoryHelper.ReadBytes(Context.Memory, BufferPosition, (int)BufferLen);
MemoryStream LogMessage = new MemoryStream(LogBuffer);
BinaryReader bReader = new BinaryReader(LogMessage);
//Header reading.
long Pid = bReader.ReadInt64();
long ThreadCxt = bReader.ReadInt64();
int Infos = bReader.ReadInt32();
int PayloadLen = bReader.ReadInt32();
int iFlags = Infos & 0xFFFF;
int iSeverity = (Infos >> 17) & 0x7F;
int iVerbosity = (Infos >> 25) & 0x7F;
//ToDo: For now we don't care about Head or Tail Log.
bool IsHeadLog = Convert.ToBoolean(iFlags & (int)Flags.IsHead);
bool IsTailLog = Convert.ToBoolean(iFlags & (int)Flags.IsTail);
string LogString = "nn::diag::detail::LogImpl()" + Environment.NewLine + Environment.NewLine +
"Header:" + Environment.NewLine +
$" Pid: {Pid}" + Environment.NewLine +
$" ThreadContext: {ThreadCxt}" + Environment.NewLine +
$" Flags: {IsHeadLog}/{IsTailLog}" + Environment.NewLine +
$" Severity: {Enum.GetName(typeof(Severity), iSeverity)}" + Environment.NewLine +
$" Verbosity: {iVerbosity}";
LogString += Environment.NewLine + Environment.NewLine + "Message:" + Environment.NewLine;
string StrMessage = "", StrLine = "", StrFilename = "", StrFunction = "",
StrModule = "", StrThread = "";
do
{
byte FieldType = bReader.ReadByte();
byte FieldSize = bReader.ReadByte();
if ((Field)FieldType != Field.Skip || FieldSize != 0)
{
byte[] Message = bReader.ReadBytes(FieldSize);
switch ((Field)FieldType)
{
case Field.Message:
StrMessage = Encoding.UTF8.GetString(Message);
break;
case Field.Line:
StrLine = BitConverter.ToInt32(Message, 0).ToString();
break;
case Field.Filename:
StrFilename = Encoding.UTF8.GetString(Message);
break;
case Field.Function:
StrFunction = Encoding.UTF8.GetString(Message);
break;
case Field.Module:
StrModule = Encoding.UTF8.GetString(Message);
break;
case Field.Thread:
StrThread = Encoding.UTF8.GetString(Message);
break;
}
}
}
while (LogMessage.Position != PayloadLen + 0x18); // 0x18 - Size of Header LogMessage.
LogString += StrModule + " > " + StrThread + ": " + StrFilename + "@" + StrFunction + "(" + StrLine + ") '" + StrMessage + "'" + Environment.NewLine;
switch((Severity)iSeverity)
{
case Severity.Trace: Logging.Trace(LogString); break;
case Severity.Info: Logging.Info(LogString); break;
case Severity.Warning: Logging.Warn(LogString); break;
case Severity.Error: Logging.Error(LogString); break;
case Severity.Critical: Logging.Fatal(LogString); break;
}
return 0;
}
}
}

View file

@ -1,6 +1,8 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Lm
{
class ServiceLm : IIpcService
@ -21,6 +23,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Lm
{
Context.Session.Initialize();
MakeObject(Context, new ILogger());
return 0;
}
}

View file

@ -1,19 +1,19 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Time
namespace Ryujinx.Core.OsHle.IpcServices.Ns
{
class ITimeZoneService : IIpcService
class ServiceNs : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ITimeZoneService()
public ServiceNs()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
//{ 1, Function }
};
}
}

View file

@ -6,6 +6,7 @@ using Ryujinx.Core.OsHle.IpcServices.Friend;
using Ryujinx.Core.OsHle.IpcServices.FspSrv;
using Ryujinx.Core.OsHle.IpcServices.Hid;
using Ryujinx.Core.OsHle.IpcServices.Lm;
using Ryujinx.Core.OsHle.IpcServices.Ns;
using Ryujinx.Core.OsHle.IpcServices.NvServices;
using Ryujinx.Core.OsHle.IpcServices.Pctl;
using Ryujinx.Core.OsHle.IpcServices.Pl;
@ -24,6 +25,7 @@ namespace Ryujinx.Core.OsHle.IpcServices
switch (Name)
{
case "acc:u0": return new ServiceAcc();
case "aoc:u": return new ServiceNs();
case "apm": return new ServiceApm();
case "apm:p": return new ServiceApm();
case "appletOE": return new ServiceAppletOE();
@ -42,6 +44,8 @@ namespace Ryujinx.Core.OsHle.IpcServices
case "time:s": return new ServiceTime();
case "time:u": return new ServiceTime();
case "vi:m": return new ServiceVi();
case "vi:s": return new ServiceVi();
case "vi:u": return new ServiceVi();
}
throw new NotImplementedException(Name);

View file

@ -0,0 +1,69 @@
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Time
{
class ITimeZoneService : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local);
public ITimeZoneService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 101, ToCalendarTimeWithMyRule }
};
}
//(nn::time::PosixTime)-> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
public long ToCalendarTimeWithMyRule(ServiceCtx Context)
{
long PosixTime = Context.RequestData.ReadInt64();
Epoch = Epoch.AddSeconds(PosixTime).ToLocalTime();
/*
struct CalendarTime {
u16_le year;
u8 month; // Starts at 1
u8 day; // Starts at 1
u8 hour;
u8 minute;
u8 second;
INSERT_PADDING_BYTES(1);
};
*/
Context.ResponseData.Write((short)Epoch.Year);
Context.ResponseData.Write((byte)Epoch.Month);
Context.ResponseData.Write((byte)Epoch.Day);
Context.ResponseData.Write((byte)Epoch.Hour);
Context.ResponseData.Write((byte)Epoch.Minute);
Context.ResponseData.Write((byte)Epoch.Second);
Context.ResponseData.Write((byte)0);
/* Thanks to TuxSH
struct CalendarAdditionalInfo {
u32 tm_wday; //day of week [0,6] (Sunday = 0)
s32 tm_yday; //day of year [0,365]
struct timezone {
char[8] tz_name;
bool isDaylightSavingTime;
s32 utcOffsetSeconds;
};
};
*/
Context.ResponseData.Write((int)Epoch.DayOfWeek);
Context.ResponseData.Write(Epoch.DayOfYear);
Context.ResponseData.Write(new byte[8]);
Context.ResponseData.Write(Convert.ToByte(Epoch.IsDaylightSavingTime()));
Context.ResponseData.Write(0);
return 0;
}
}
}

View file

@ -30,6 +30,8 @@ namespace Ryujinx.Core.OsHle.Svc
{ 0x09, SvcStartThread },
{ 0x0b, SvcSleepThread },
{ 0x0c, SvcGetThreadPriority },
{ 0x0d, SvcSetThreadPriority },
{ 0x0f, SvcSetThreadCoreMask },
{ 0x13, SvcMapSharedMemory },
{ 0x14, SvcUnmapSharedMemory },
{ 0x15, SvcCreateTransferMemory },
@ -44,6 +46,7 @@ namespace Ryujinx.Core.OsHle.Svc
{ 0x1f, SvcConnectToNamedPort },
{ 0x21, SvcSendSyncRequest },
{ 0x22, SvcSendSyncRequestWithUserBuffer },
{ 0x25, SvcGetThreadId },
{ 0x26, SvcBreak },
{ 0x27, SvcOutputDebugString },
{ 0x29, SvcGetInfo }

View file

@ -81,5 +81,44 @@ namespace Ryujinx.Core.OsHle.Svc
//TODO: Error codes.
}
private void SvcSetThreadPriority(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X1;
int Prio = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
if (Thread != null)
{
Thread.Priority = Prio;
ThreadState.X0 = (int)SvcResult.Success;
}
//TODO: Error codes.
}
private void SvcSetThreadCoreMask(AThreadState ThreadState)
{
ThreadState.X0 = (int)SvcResult.Success;
//TODO: Error codes.
}
private void SvcGetThreadId(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
if (Thread != null)
{
ThreadState.X1 = (ulong)Thread.ThreadId;
ThreadState.X0 = (int)SvcResult.Success;
}
//TODO: Error codes.
}
}
}

View file

@ -16,6 +16,9 @@ Logging_Enable_Error = true
#Enabled print fatal logs
Logging_Enable_Fatal = true
#Enabled print Ipc logs
Logging_Enable_Ipc = false
#Saved logs into Ryujinx.log
Logging_Enable_LogFile = false