Stub a few services, add support for generating call stacks on the CPU
This commit is contained in:
parent
4906acdde9
commit
bd9b1e2c6b
20 changed files with 267 additions and 41 deletions
|
@ -1,4 +1,6 @@
|
|||
public static class AOptimizations
|
||||
{
|
||||
public static bool DisableMemoryChecks = false;
|
||||
|
||||
public static bool GenerateCallStack = true;
|
||||
}
|
|
@ -8,8 +8,6 @@ namespace ChocolArm64.Instruction
|
|||
{
|
||||
static partial class AInstEmit
|
||||
{
|
||||
private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
|
||||
public static void Brk(AILEmitterCtx Context)
|
||||
{
|
||||
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
|
||||
|
@ -30,9 +28,7 @@ namespace ChocolArm64.Instruction
|
|||
|
||||
Context.EmitLdc_I4(Op.Id);
|
||||
|
||||
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
Context.EmitPrivateCall(typeof(AThreadState), MthdName);
|
||||
|
||||
//Check if the thread should still be running, if it isn't then we return 0
|
||||
//to force a return to the dispatcher and then exit the thread.
|
||||
|
@ -73,11 +69,7 @@ namespace ChocolArm64.Instruction
|
|||
Context.EmitLdc_I8(Op.Position);
|
||||
Context.EmitLdc_I4(Op.RawOpCode);
|
||||
|
||||
string MthdName = nameof(AThreadState.OnUndefined);
|
||||
|
||||
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
|
||||
|
||||
Context.EmitCall(MthdInfo);
|
||||
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.OnUndefined));
|
||||
|
||||
if (Context.CurrBlock.Next != null)
|
||||
{
|
||||
|
|
|
@ -35,6 +35,14 @@ namespace ChocolArm64.Instruction
|
|||
{
|
||||
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
||||
|
||||
if (AOptimizations.GenerateCallStack)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||
Context.EmitLdc_I8(Op.Imm);
|
||||
|
||||
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
|
||||
}
|
||||
|
||||
Context.EmitLdc_I(Op.Position + 4);
|
||||
Context.EmitStint(AThreadState.LRIndex);
|
||||
Context.EmitStoreState();
|
||||
|
@ -72,6 +80,14 @@ namespace ChocolArm64.Instruction
|
|||
{
|
||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||
|
||||
if (AOptimizations.GenerateCallStack)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
|
||||
}
|
||||
|
||||
Context.EmitLdc_I(Op.Position + 4);
|
||||
Context.EmitStint(AThreadState.LRIndex);
|
||||
Context.EmitStoreState();
|
||||
|
@ -84,6 +100,14 @@ namespace ChocolArm64.Instruction
|
|||
{
|
||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||
|
||||
if (AOptimizations.GenerateCallStack)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.JumpMethod));
|
||||
}
|
||||
|
||||
Context.EmitStoreState();
|
||||
Context.EmitLdintzr(Op.Rn);
|
||||
|
||||
|
@ -105,6 +129,13 @@ namespace ChocolArm64.Instruction
|
|||
|
||||
public static void Ret(AILEmitterCtx Context)
|
||||
{
|
||||
if (AOptimizations.GenerateCallStack)
|
||||
{
|
||||
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||
|
||||
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.ExitMethod));
|
||||
}
|
||||
|
||||
Context.EmitStoreState();
|
||||
Context.EmitLdint(AThreadState.LRIndex);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using ChocolArm64.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ChocolArm64.State
|
||||
|
@ -56,10 +57,17 @@ namespace ChocolArm64.State
|
|||
public event EventHandler<AInstExceptionEventArgs> SvcCall;
|
||||
public event EventHandler<AInstUndefinedEventArgs> Undefined;
|
||||
|
||||
private Stack<long> CallStack;
|
||||
|
||||
private static Stopwatch TickCounter;
|
||||
|
||||
private static double HostTickFreq;
|
||||
|
||||
public AThreadState()
|
||||
{
|
||||
CallStack = new Stack<long>();
|
||||
}
|
||||
|
||||
static AThreadState()
|
||||
{
|
||||
HostTickFreq = 1.0 / Stopwatch.Frequency;
|
||||
|
@ -83,5 +91,27 @@ namespace ChocolArm64.State
|
|||
{
|
||||
Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode));
|
||||
}
|
||||
|
||||
internal void EnterMethod(long Position)
|
||||
{
|
||||
CallStack.Push(Position);
|
||||
}
|
||||
|
||||
internal void ExitMethod()
|
||||
{
|
||||
CallStack.TryPop(out _);
|
||||
}
|
||||
|
||||
internal void JumpMethod(long Position)
|
||||
{
|
||||
CallStack.TryPop(out _);
|
||||
|
||||
CallStack.Push(Position);
|
||||
}
|
||||
|
||||
public long[] GetCallStack()
|
||||
{
|
||||
return CallStack.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -461,6 +461,21 @@ namespace ChocolArm64.Translation
|
|||
EmitCall(ObjType.GetMethod(MthdName));
|
||||
}
|
||||
|
||||
public void EmitPrivateCall(Type ObjType, string MthdName)
|
||||
{
|
||||
if (ObjType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ObjType));
|
||||
}
|
||||
|
||||
if (MthdName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(MthdName));
|
||||
}
|
||||
|
||||
EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic));
|
||||
}
|
||||
|
||||
public void EmitCall(MethodInfo MthdInfo)
|
||||
{
|
||||
if (MthdInfo == null)
|
||||
|
|
|
@ -7,14 +7,16 @@ namespace Ryujinx.Core.Loaders
|
|||
{
|
||||
class Executable
|
||||
{
|
||||
private AMemory Memory;
|
||||
|
||||
private List<ElfDyn> Dynamic;
|
||||
|
||||
private Dictionary<long, string> m_SymbolTable;
|
||||
|
||||
public IReadOnlyDictionary<long, string> SymbolTable => m_SymbolTable;
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
private AMemory Memory;
|
||||
|
||||
public long ImageBase { get; private set; }
|
||||
public long ImageEnd { get; private set; }
|
||||
|
||||
|
@ -24,6 +26,8 @@ namespace Ryujinx.Core.Loaders
|
|||
|
||||
m_SymbolTable = new Dictionary<long, string>();
|
||||
|
||||
Name = Exe.Name;
|
||||
|
||||
this.Memory = Memory;
|
||||
this.ImageBase = ImageBase;
|
||||
this.ImageEnd = ImageBase;
|
||||
|
|
|
@ -2,6 +2,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
|||
{
|
||||
public interface IExecutable
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
byte[] Text { get; }
|
||||
byte[] RO { get; }
|
||||
byte[] Data { get; }
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
|||
{
|
||||
class Nro : IExecutable
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public byte[] Text { get; private set; }
|
||||
public byte[] RO { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
|
@ -14,8 +16,10 @@ namespace Ryujinx.Core.Loaders.Executables
|
|||
public int DataOffset { get; private set; }
|
||||
public int BssSize { get; private set; }
|
||||
|
||||
public Nro(Stream Input)
|
||||
public Nro(Stream Input, string Name)
|
||||
{
|
||||
this.Name = Name;
|
||||
|
||||
BinaryReader Reader = new BinaryReader(Input);
|
||||
|
||||
Input.Seek(4, SeekOrigin.Begin);
|
||||
|
|
|
@ -6,6 +6,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
|||
{
|
||||
class Nso : IExecutable
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public byte[] Text { get; private set; }
|
||||
public byte[] RO { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
|
@ -27,8 +29,10 @@ namespace Ryujinx.Core.Loaders.Executables
|
|||
HasDataHash = 1 << 5
|
||||
}
|
||||
|
||||
public Nso(Stream Input)
|
||||
public Nso(Stream Input, string Name)
|
||||
{
|
||||
this.Name = Name;
|
||||
|
||||
BinaryReader Reader = new BinaryReader(Input);
|
||||
|
||||
Input.Seek(0, SeekOrigin.Begin);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
ServiceApm,
|
||||
ServiceAudio,
|
||||
ServiceBsd,
|
||||
ServiceCaps,
|
||||
ServiceFriend,
|
||||
ServiceFs,
|
||||
ServiceHid,
|
||||
|
|
|
@ -58,7 +58,9 @@ namespace Ryujinx.Core.OsHle
|
|||
|
||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||
{
|
||||
Nso Program = new Nso(Input);
|
||||
string Name = Path.GetFileNameWithoutExtension(File);
|
||||
|
||||
Nso Program = new Nso(Input, Name);
|
||||
|
||||
MainProcess.LoadProgram(Program);
|
||||
}
|
||||
|
@ -80,13 +82,15 @@ namespace Ryujinx.Core.OsHle
|
|||
{
|
||||
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
|
||||
|
||||
string Name = Path.GetFileNameWithoutExtension(FileName);
|
||||
|
||||
Process MainProcess = MakeProcess();
|
||||
|
||||
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
||||
{
|
||||
MainProcess.LoadProgram(IsNro
|
||||
? (IExecutable)new Nro(Input)
|
||||
: (IExecutable)new Nso(Input));
|
||||
? (IExecutable)new Nro(Input, Name)
|
||||
: (IExecutable)new Nso(Input, Name));
|
||||
}
|
||||
|
||||
MainProcess.SetEmptyArgs();
|
||||
|
|
|
@ -156,13 +156,13 @@ namespace Ryujinx.Core.OsHle.Kernel
|
|||
private void SvcSetThreadActivity(AThreadState ThreadState)
|
||||
{
|
||||
int Handle = (int)ThreadState.X0;
|
||||
bool Active = (int)ThreadState.X1 != 0;
|
||||
bool Active = (int)ThreadState.X1 == 0;
|
||||
|
||||
KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
|
||||
KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
|
||||
|
||||
if (CurrThread != null)
|
||||
if (Thread != null)
|
||||
{
|
||||
Process.Scheduler.SetThreadActivity(CurrThread, Active);
|
||||
Process.Scheduler.SetThreadActivity(Thread, Active);
|
||||
|
||||
ThreadState.X0 = 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using ChocolArm64;
|
||||
using ChocolArm64.Events;
|
||||
using ChocolArm64.Memory;
|
||||
using ChocolArm64.State;
|
||||
using Ryujinx.Core.Loaders;
|
||||
using Ryujinx.Core.Loaders.Executables;
|
||||
using Ryujinx.Core.OsHle.Exceptions;
|
||||
|
@ -10,6 +11,7 @@ using Ryujinx.Core.OsHle.Services.Nv;
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
|
@ -47,9 +49,11 @@ namespace Ryujinx.Core.OsHle
|
|||
|
||||
private ConcurrentDictionary<long, KThread> Threads;
|
||||
|
||||
private KThread MainThread;
|
||||
|
||||
private List<Executable> Executables;
|
||||
|
||||
private KThread MainThread;
|
||||
private Dictionary<long, string> SymbolTable;
|
||||
|
||||
private long ImageBase;
|
||||
|
||||
|
@ -121,6 +125,8 @@ namespace Ryujinx.Core.OsHle
|
|||
return false;
|
||||
}
|
||||
|
||||
MakeSymbolTable();
|
||||
|
||||
MapRWMemRegion(
|
||||
MemoryRegions.MainStackAddress,
|
||||
MemoryRegions.MainStackSize,
|
||||
|
@ -227,11 +233,9 @@ namespace Ryujinx.Core.OsHle
|
|||
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
|
||||
}
|
||||
|
||||
private ATranslator GetTranslator()
|
||||
private void MakeSymbolTable()
|
||||
{
|
||||
if (Translator == null)
|
||||
{
|
||||
Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
|
||||
SymbolTable = new Dictionary<long, string>();
|
||||
|
||||
foreach (Executable Exe in Executables)
|
||||
{
|
||||
|
@ -240,7 +244,12 @@ namespace Ryujinx.Core.OsHle
|
|||
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ATranslator GetTranslator()
|
||||
{
|
||||
if (Translator == null)
|
||||
{
|
||||
Translator = new ATranslator(SymbolTable);
|
||||
|
||||
Translator.CpuTrace += CpuTraceHandler;
|
||||
|
@ -249,6 +258,16 @@ namespace Ryujinx.Core.OsHle
|
|||
return Translator;
|
||||
}
|
||||
|
||||
public void EnableCpuTracing()
|
||||
{
|
||||
Translator.EnableCpuTrace = true;
|
||||
}
|
||||
|
||||
public void DisableCpuTracing()
|
||||
{
|
||||
Translator.EnableCpuTrace = false;
|
||||
}
|
||||
|
||||
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
|
||||
{
|
||||
string NsoName = string.Empty;
|
||||
|
@ -263,17 +282,47 @@ namespace Ryujinx.Core.OsHle
|
|||
}
|
||||
}
|
||||
|
||||
Logging.Trace(LogClass.Loader, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
|
||||
Logging.Trace(LogClass.CPU, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
|
||||
}
|
||||
|
||||
public void EnableCpuTracing()
|
||||
public void PrintStackTrace(AThreadState ThreadState)
|
||||
{
|
||||
Translator.EnableCpuTrace = true;
|
||||
long[] Positions = ThreadState.GetCallStack();
|
||||
|
||||
StringBuilder Trace = new StringBuilder();
|
||||
|
||||
Trace.AppendLine("Guest stack trace:");
|
||||
|
||||
foreach (long Position in Positions)
|
||||
{
|
||||
if (!SymbolTable.TryGetValue(Position, out string SubName))
|
||||
{
|
||||
SubName = $"Sub{Position:x16}";
|
||||
}
|
||||
|
||||
public void DisableCpuTracing()
|
||||
Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
|
||||
}
|
||||
|
||||
Logging.Trace(LogClass.CPU, Trace.ToString());
|
||||
}
|
||||
|
||||
private string GetNsoNameAndAddress(long Position)
|
||||
{
|
||||
Translator.EnableCpuTrace = false;
|
||||
string Name = string.Empty;
|
||||
|
||||
for (int Index = Executables.Count - 1; Index >= 0; Index--)
|
||||
{
|
||||
if (Position >= Executables[Index].ImageBase)
|
||||
{
|
||||
long Offset = Position - Executables[Index].ImageBase;
|
||||
|
||||
Name = $"{Executables[Index].Name}:{Offset:x8}";
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Name;
|
||||
}
|
||||
|
||||
private int GetFreeTlsSlot(AThread Thread)
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
|
|||
{ 20, EnsureSaveData },
|
||||
{ 21, GetDesiredLanguage },
|
||||
{ 22, SetTerminateResult },
|
||||
{ 23, GetDisplayVersion },
|
||||
{ 40, NotifyRunning }
|
||||
};
|
||||
}
|
||||
|
@ -67,6 +68,15 @@ namespace Ryujinx.Core.OsHle.Services.Am
|
|||
return 0;
|
||||
}
|
||||
|
||||
public long GetDisplayVersion(ServiceCtx Context)
|
||||
{
|
||||
//FIXME: Need to check correct version on a switch.
|
||||
Context.ResponseData.Write(1L);
|
||||
Context.ResponseData.Write(0L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long NotifyRunning(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(1);
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Text;
|
|||
|
||||
namespace Ryujinx.Core.OsHle.Services.Aud
|
||||
{
|
||||
class IAudioDeviceService : IpcService
|
||||
class IAudioDevice : IpcService
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
|
@ -14,12 +14,13 @@ namespace Ryujinx.Core.OsHle.Services.Aud
|
|||
|
||||
private KEvent SystemEvent;
|
||||
|
||||
public IAudioDeviceService()
|
||||
public IAudioDevice()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, ListAudioDeviceName },
|
||||
{ 1, SetAudioDeviceOutputVolume },
|
||||
{ 3, GetActiveAudioDeviceName },
|
||||
{ 4, QueryAudioDeviceSystemEvent },
|
||||
{ 5, GetActiveChannelCount }
|
||||
};
|
||||
|
@ -72,6 +73,20 @@ namespace Ryujinx.Core.OsHle.Services.Aud
|
|||
return 0;
|
||||
}
|
||||
|
||||
public long GetActiveAudioDeviceName(ServiceCtx Context)
|
||||
{
|
||||
string Name = "FIXME";
|
||||
|
||||
long Position = Context.Request.ReceiveBuff[0].Position;
|
||||
long Size = Context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
byte[] Buffer = Encoding.ASCII.GetBytes(Name + '\0');
|
||||
|
||||
AMemoryHelper.WriteBytes(Context.Memory, Position, Buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long QueryAudioDeviceSystemEvent(ServiceCtx Context)
|
||||
{
|
||||
int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);
|
|
@ -53,7 +53,7 @@ namespace Ryujinx.Core.OsHle.Services.Aud
|
|||
{
|
||||
long UserId = Context.RequestData.ReadInt64();
|
||||
|
||||
MakeObject(Context, new IAudioDeviceService());
|
||||
MakeObject(Context, new IAudioDevice());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
20
Ryujinx.Core/OsHle/Services/Caps/IAlbumAccessorService.cs
Normal file
20
Ryujinx.Core/OsHle/Services/Caps/IAlbumAccessorService.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services.Caps
|
||||
{
|
||||
class IAlbumAccessorService : IpcService
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IAlbumAccessorService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Services/Caps/IScreenshotService.cs
Normal file
20
Ryujinx.Core/OsHle/Services/Caps/IScreenshotService.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using Ryujinx.Core.OsHle.Ipc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Core.OsHle.Services.Caps
|
||||
{
|
||||
class IScreenshotService : IpcService
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IScreenshotService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using Ryujinx.Core.OsHle.Services.Am;
|
|||
using Ryujinx.Core.OsHle.Services.Apm;
|
||||
using Ryujinx.Core.OsHle.Services.Aud;
|
||||
using Ryujinx.Core.OsHle.Services.Bsd;
|
||||
using Ryujinx.Core.OsHle.Services.Caps;
|
||||
using Ryujinx.Core.OsHle.Services.Friend;
|
||||
using Ryujinx.Core.OsHle.Services.FspSrv;
|
||||
using Ryujinx.Core.OsHle.Services.Hid;
|
||||
|
@ -57,9 +58,18 @@ namespace Ryujinx.Core.OsHle.Services
|
|||
case "bsd:u":
|
||||
return new IClient();
|
||||
|
||||
case "caps:a":
|
||||
return new IAlbumAccessorService();
|
||||
|
||||
case "caps:ss":
|
||||
return new IScreenshotService();
|
||||
|
||||
case "friend:a":
|
||||
return new IServiceCreator();
|
||||
|
||||
case "friend:u":
|
||||
return new IServiceCreator();
|
||||
|
||||
case "fsp-srv":
|
||||
return new IFileSystemProxy();
|
||||
|
||||
|
|
|
@ -16,10 +16,23 @@ namespace Ryujinx.Core.OsHle.Services.Time
|
|||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, GetDeviceLocationName },
|
||||
{ 101, ToCalendarTimeWithMyRule }
|
||||
};
|
||||
}
|
||||
|
||||
public long GetDeviceLocationName(ServiceCtx Context)
|
||||
{
|
||||
Logging.Stub(LogClass.ServiceTime, "Stubbed");
|
||||
|
||||
for (int Index = 0; Index < 0x24; Index++)
|
||||
{
|
||||
Context.ResponseData.Write((byte)0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long ToCalendarTimeWithMyRule(ServiceCtx Context)
|
||||
{
|
||||
long PosixTime = Context.RequestData.ReadInt64();
|
||||
|
|
Reference in a new issue