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

@@ -0,0 +1,33 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Acc
{
class IManagerForApplication : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IManagerForApplication()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, CheckAvailability },
{ 1, GetAccountId }
};
}
public long CheckAvailability(ServiceCtx Context)
{
return 0;
}
public long GetAccountId(ServiceCtx Context)
{
Context.ResponseData.Write(0xcafeL);
return 0;
}
}
}

View File

@@ -0,0 +1,33 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Acc
{
class IProfile : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IProfile()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, GetBase }
};
}
public long GetBase(ServiceCtx Context)
{
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
return 0;
}
}
}

View File

@@ -0,0 +1,49 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Acc
{
class ServiceAcc : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAcc()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 3, ListOpenUsers },
{ 5, GetProfile },
{ 100, InitializeApplicationInfo },
{ 101, GetBaasAccountManagerForApplication }
};
}
public long ListOpenUsers(ServiceCtx Context)
{
return 0;
}
public long GetProfile(ServiceCtx Context)
{
MakeObject(Context, new IProfile());
return 0;
}
public long InitializeApplicationInfo(ServiceCtx Context)
{
return 0;
}
public long GetBaasAccountManagerForApplication(ServiceCtx Context)
{
MakeObject(Context, new IManagerForApplication());
return 0;
}
}
}

View File

@@ -0,0 +1,94 @@
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
using System.IO;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class IApplicationFunctions : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IApplicationFunctions()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, PopLaunchParameter },
{ 20, EnsureSaveData },
{ 21, GetDesiredLanguage },
{ 22, SetTerminateResult },
{ 40, NotifyRunning }
};
}
private const uint LaunchParamsMagic = 0xc79497ca;
public long PopLaunchParameter(ServiceCtx Context)
{
//Only the first 0x18 bytes of the Data seems to be actually used.
MakeObject(Context, new IStorage(MakeLaunchParams()));
return 0;
}
public long EnsureSaveData(ServiceCtx Context)
{
long UIdLow = Context.RequestData.ReadInt64();
long UIdHigh = Context.RequestData.ReadInt64();
Context.ResponseData.Write(0L);
return 0;
}
public long GetDesiredLanguage(ServiceCtx Context)
{
//This is an enumerator where each number is a differnet language.
//0 is Japanese and 1 is English, need to figure out the other codes.
Context.ResponseData.Write(1L);
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);
return 0;
}
private byte[] MakeLaunchParams()
{
//Size needs to be at least 0x88 bytes otherwise application errors.
using (MemoryStream MS = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(MS);
MS.SetLength(0x88);
Writer.Write(LaunchParamsMagic);
Writer.Write(1); //IsAccountSelected? Only lower 8 bits actually used.
Writer.Write(1L); //User Id Low (note: User Id needs to be != 0)
Writer.Write(0L); //User Id High
return MS.ToArray();
}
}
}
}

View File

@@ -0,0 +1,85 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class IApplicationProxy : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IApplicationProxy()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetCommonStateGetter },
{ 1, GetSelfController },
{ 2, GetWindowController },
{ 3, GetAudioController },
{ 4, GetDisplayController },
{ 11, GetLibraryAppletCreator },
{ 20, GetApplicationFunctions },
{ 1000, GetDebugFunctions }
};
}
public long GetCommonStateGetter(ServiceCtx Context)
{
MakeObject(Context, new ICommonStateGetter());
return 0;
}
public long GetSelfController(ServiceCtx Context)
{
MakeObject(Context, new ISelfController());
return 0;
}
public long GetWindowController(ServiceCtx Context)
{
MakeObject(Context, new IWindowController());
return 0;
}
public long GetAudioController(ServiceCtx Context)
{
MakeObject(Context, new IAudioController());
return 0;
}
public long GetDisplayController(ServiceCtx Context)
{
MakeObject(Context, new IDisplayController());
return 0;
}
public long GetLibraryAppletCreator(ServiceCtx Context)
{
MakeObject(Context, new ILibraryAppletCreator());
return 0;
}
public long GetApplicationFunctions(ServiceCtx Context)
{
MakeObject(Context, new IApplicationFunctions());
return 0;
}
public long GetDebugFunctions(ServiceCtx Context)
{
MakeObject(Context, new IDebugFunctions());
return 0;
}
}
}

View File

@@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class IAudioController : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAudioController()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
};
}
}
}

View File

@@ -0,0 +1,74 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class ICommonStateGetter : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ICommonStateGetter()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetEventHandle },
{ 1, ReceiveMessage },
{ 5, GetOperationMode },
{ 6, GetPerformanceMode },
{ 9, GetCurrentFocusState },
};
}
private enum FocusState
{
InFocus = 1,
OutOfFocus = 2
}
private enum OperationMode
{
Handheld = 0,
Docked = 1
}
public long GetEventHandle(ServiceCtx Context)
{
Context.ResponseData.Write(0L);
return 0;
}
public long ReceiveMessage(ServiceCtx Context)
{
//Program expects 0xF at 0x17ae70 on puyo sdk,
//otherwise runs on a infinite loop until it reads said value.
//What it means is still unknown.
Context.ResponseData.Write(0xfL);
return 0; //0x680;
}
public long GetOperationMode(ServiceCtx Context)
{
Context.ResponseData.Write((byte)OperationMode.Handheld);
return 0;
}
public long GetPerformanceMode(ServiceCtx Context)
{
Context.ResponseData.Write((byte)0);
return 0;
}
public long GetCurrentFocusState(ServiceCtx Context)
{
Context.ResponseData.Write((byte)FocusState.InFocus);
return 0;
}
}
}

View File

@@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class IDebugFunctions : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IDebugFunctions()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
};
}
}
}

View File

@@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class IDisplayController : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IDisplayController()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
};
}
}
}

View File

@@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class ILibraryAppletCreator : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ILibraryAppletCreator()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
};
}
}
}

View File

@@ -0,0 +1,75 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class ISelfController : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ISelfController()
{
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;
return 0;
}
public long SetOperationModeChangedNotification(ServiceCtx Context)
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
return 0;
}
public long SetPerformanceModeChangedNotification(ServiceCtx Context)
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
return 0;
}
public long SetFocusHandlingMode(ServiceCtx Context)
{
bool Flag1 = Context.RequestData.ReadByte() != 0 ? true : false;
bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false;
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;
return 0;
}
}
}

View File

@@ -0,0 +1,33 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class IStorage : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public byte[] Data { get; private set; }
public IStorage(byte[] Data)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Open }
};
this.Data = Data;
}
public long Open(ServiceCtx Context)
{
MakeObject(Context, new IStorageAccessor(this));
return 0;
}
}
}

View File

@@ -0,0 +1,62 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class IStorageAccessor : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private IStorage Storage;
public IStorageAccessor(IStorage Storage)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetSize },
{ 11, Read }
};
this.Storage = Storage;
}
public long GetSize(ServiceCtx Context)
{
Context.ResponseData.Write((long)Storage.Data.Length);
return 0;
}
public long Read(ServiceCtx Context)
{
long ReadPosition = Context.RequestData.ReadInt64();
if (Context.Request.RecvListBuff.Count > 0)
{
long Position = Context.Request.RecvListBuff[0].Position;
short Size = Context.Request.RecvListBuff[0].Size;
byte[] Data;
if (Storage.Data.Length > Size)
{
Data = new byte[Size];
Buffer.BlockCopy(Storage.Data, 0, Data, 0, Size);
}
else
{
Data = Storage.Data;
}
AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
}
return 0;
}
}
}

View File

@@ -0,0 +1,33 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class IWindowController : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IWindowController()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, GetAppletResourceUserId },
{ 10, AcquireForegroundRights }
};
}
public long GetAppletResourceUserId(ServiceCtx Context)
{
Context.ResponseData.Write(0L);
return 0;
}
public long AcquireForegroundRights(ServiceCtx Context)
{
return 0;
}
}
}

View File

@@ -0,0 +1,29 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Am
{
class ServiceAppletOE : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAppletOE()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, OpenApplicationProxy }
};
}
public long OpenApplicationProxy(ServiceCtx Context)
{
MakeObject(Context, new IApplicationProxy());
return 0;
}
}
}

View File

@@ -0,0 +1,28 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Apm
{
class ISession : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ISession()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, SetPerformanceConfiguration }
};
}
public long SetPerformanceConfiguration(ServiceCtx Context)
{
int PerfMode = Context.RequestData.ReadInt32();
int PerfConfig = Context.RequestData.ReadInt32();
return 0;
}
}
}

View File

@@ -0,0 +1,29 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Apm
{
class ServiceApm : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceApm()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, OpenSession }
};
}
public long OpenSession(ServiceCtx Context)
{
MakeObject(Context, new ISession());
return 0;
}
}
}

View File

@@ -0,0 +1,180 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
using System;
using System.Collections.Generic;
using System.IO;
namespace Ryujinx.Core.OsHle.IpcServices.Aud
{
class IAudioOut : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAudioOut()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetAudioOutState },
{ 1, StartAudioOut },
{ 2, StopAudioOut },
{ 3, AppendAudioOutBuffer },
{ 4, RegisterBufferEvent },
{ 5, GetReleasedAudioOutBuffer },
{ 6, ContainsAudioOutBuffer },
{ 7, AppendAudioOutBuffer_ex },
{ 8, GetReleasedAudioOutBuffer_ex }
};
}
enum AudioOutState
{
Started,
Stopped
};
//IAudioOut
private AudioOutState State = AudioOutState.Stopped;
private Queue<long> KeysQueue = new Queue<long>();
//OpenAL
private bool OpenALInstalled = true;
private AudioContext AudioCtx;
private int Source;
private int Buffer;
//Return State of IAudioOut
public long GetAudioOutState(ServiceCtx Context)
{
Context.ResponseData.Write((int)State);
return 0;
}
public long StartAudioOut(ServiceCtx Context)
{
if (State == AudioOutState.Stopped)
{
State = AudioOutState.Started;
try
{
AudioCtx = new AudioContext(); //Create the audio context
}
catch (Exception)
{
Logging.Warn("OpenAL Error! PS: Install OpenAL Core SDK!");
OpenALInstalled = false;
}
if (OpenALInstalled) AL.Listener(ALListenerf.Gain, (float)8.0); //Add more gain to it
}
return 0;
}
public long StopAudioOut(ServiceCtx Context)
{
if (State == AudioOutState.Started)
{
if (OpenALInstalled)
{
if (AudioCtx == null) //Needed to call the instance of AudioContext()
return 0;
AL.SourceStop(Source);
AL.DeleteSource(Source);
}
State = AudioOutState.Stopped;
}
return 0;
}
public long AppendAudioOutBuffer(ServiceCtx Context)
{
long BufferId = Context.RequestData.ReadInt64();
KeysQueue.Enqueue(BufferId);
byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5);
using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
{
BinaryReader Reader = new BinaryReader(MS);
long PointerNextBuffer = Reader.ReadInt64();
long PointerSampleBuffer = Reader.ReadInt64();
long CapacitySampleBuffer = Reader.ReadInt64();
long SizeDataInSampleBuffer = Reader.ReadInt64();
long OffsetDataInSampleBuffer = Reader.ReadInt64();
byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer);
if (OpenALInstalled)
{
if (AudioCtx == null) //Needed to call the instance of AudioContext()
return 0;
Buffer = AL.GenBuffer();
AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000);
Source = AL.GenSource();
AL.SourceQueueBuffer(Source, Buffer);
}
}
return 0;
}
public long RegisterBufferEvent(ServiceCtx Context)
{
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
return 0;
}
public long GetReleasedAudioOutBuffer(ServiceCtx Context)
{
long TempKey = 0;
if (KeysQueue.Count > 0) TempKey = KeysQueue.Dequeue();
AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, BitConverter.GetBytes(TempKey));
int ReleasedBuffersCount = 1;
Context.ResponseData.Write(ReleasedBuffersCount);
if (OpenALInstalled)
{
if (AudioCtx == null) //Needed to call the instance of AudioContext()
return 0;
AL.SourcePlay(Source);
int[] FreeBuffers = AL.SourceUnqueueBuffers(Source, 1);
AL.DeleteBuffers(FreeBuffers);
}
return 0;
}
public long ContainsAudioOutBuffer(ServiceCtx Context)
{
return 0;
}
public long AppendAudioOutBuffer_ex(ServiceCtx Context)
{
return 0;
}
public long GetReleasedAudioOutBuffer_ex(ServiceCtx Context)
{
return 0;
}
}
}

View File

@@ -0,0 +1,66 @@
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Aud
{
class IAudioRenderer : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAudioRenderer()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 4, RequestUpdateAudioRenderer },
{ 5, StartAudioRenderer },
{ 6, StopAudioRenderer },
{ 7, QuerySystemEvent }
};
}
public long RequestUpdateAudioRenderer(ServiceCtx Context)
{
//(buffer<unknown, 5, 0>) -> (buffer<unknown, 6, 0>, buffer<unknown, 6, 0>)
long Position = Context.Request.ReceiveBuff[0].Position;
//0x40 bytes header
Context.Memory.WriteInt32(Position + 0x4, 0xb0); //Behavior Out State Size? (note: this is the last section)
Context.Memory.WriteInt32(Position + 0x8, 0x18e0); //Memory Pool Out State Size?
Context.Memory.WriteInt32(Position + 0xc, 0x600); //Voice Out State Size?
Context.Memory.WriteInt32(Position + 0x14, 0xe0); //Effect Out State Size?
Context.Memory.WriteInt32(Position + 0x1c, 0x20); //Sink Out State Size?
Context.Memory.WriteInt32(Position + 0x20, 0x10); //Performance Out State Size?
Context.Memory.WriteInt32(Position + 0x3c, 0x20e0); //Total Size (including 0x40 bytes header)
for (int Offset = 0x40; Offset < 0x40 + 0x18e0; Offset += 0x10)
{
Context.Memory.WriteInt32(Position + Offset, 5);
}
return 0;
}
public long StartAudioRenderer(ServiceCtx Context)
{
return 0;
}
public long StopAudioRenderer(ServiceCtx Context)
{
return 0;
}
public long QuerySystemEvent(ServiceCtx Context)
{
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
return 0;
}
}
}

View File

@@ -0,0 +1,57 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using System.Text;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Aud
{
class ServiceAudOut : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAudOut()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, ListAudioOuts },
{ 1, OpenAudioOut },
};
}
public long ListAudioOuts(ServiceCtx Context)
{
long Position = Context.Request.ReceiveBuff[0].Position;
AMemoryHelper.WriteBytes(Context.Memory, Position, Encoding.ASCII.GetBytes("iface"));
Context.ResponseData.Write(1);
return 0;
}
public long OpenAudioOut(ServiceCtx Context)
{
MakeObject(Context, new IAudioOut());
Context.ResponseData.Write(48000); //Sample Rate
Context.ResponseData.Write(2); //Channel Count
Context.ResponseData.Write(2); //PCM Format
/*
0 - Invalid
1 - INT8
2 - INT16
3 - INT24
4 - INT32
5 - PCM Float
6 - ADPCM
*/
Context.ResponseData.Write(0); //Unknown
return 0;
}
}
}

View File

@@ -0,0 +1,51 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Aud
{
class ServiceAudRen : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAudRen()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, OpenAudioRenderer },
{ 1, GetAudioRendererWorkBufferSize },
};
}
public long OpenAudioRenderer(ServiceCtx Context)
{
MakeObject(Context, new IAudioRenderer());
return 0;
}
public long GetAudioRendererWorkBufferSize(ServiceCtx Context)
{
int SampleRate = Context.RequestData.ReadInt32();
int Unknown4 = Context.RequestData.ReadInt32();
int Unknown8 = Context.RequestData.ReadInt32();
int UnknownC = Context.RequestData.ReadInt32();
int Unknown10 = Context.RequestData.ReadInt32();
int Unknown14 = Context.RequestData.ReadInt32();
int Unknown18 = Context.RequestData.ReadInt32();
int Unknown1c = Context.RequestData.ReadInt32();
int Unknown20 = Context.RequestData.ReadInt32();
int Unknown24 = Context.RequestData.ReadInt32();
int Unknown28 = Context.RequestData.ReadInt32();
int Unknown2c = Context.RequestData.ReadInt32();
int Rev1Magic = Context.RequestData.ReadInt32();
Context.ResponseData.Write(0x400L);
return 0;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace Ryujinx.Core.OsHle.IpcServices
{
static class ErrorCode
{
public static long MakeError(ErrorModule Module, int Code)
{
return (int)Module | (Code << 9);
}
}
}

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

@@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Friend
{
class IFriendService : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IFriendService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
};
}
}
}

View File

@@ -0,0 +1,29 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Friend
{
class ServiceFriend : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceFriend()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, CreateFriendService }
};
}
public static long CreateFriendService(ServiceCtx Context)
{
MakeObject(Context, new IFriendService());
return 0;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
{
static class FsErr
{
public const int PathDoesNotExist = 1;
public const int PathAlreadyExists = 2;
public const int PathAlreadyInUse = 7;
}
}

View File

@@ -0,0 +1,117 @@
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.FspSrv
{
class IDirectory : IIpcService, IDisposable
{
private const int DirectoryEntrySize = 0x310;
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private List<string> DirectoryEntries;
private int CurrentItemIndex;
public event EventHandler<EventArgs> Disposed;
public string HostPath { get; private set; }
public IDirectory(string HostPath, int Flags)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Read },
{ 1, GetEntryCount }
};
this.HostPath = HostPath;
DirectoryEntries = new List<string>();
if ((Flags & 1) != 0)
{
DirectoryEntries.AddRange(Directory.GetDirectories(HostPath));
}
if ((Flags & 2) != 0)
{
DirectoryEntries.AddRange(Directory.GetFiles(HostPath));
}
CurrentItemIndex = 0;
}
public long Read(ServiceCtx Context)
{
long BufferPosition = Context.Request.ReceiveBuff[0].Position;
long BufferLen = Context.Request.ReceiveBuff[0].Size;
int MaxReadCount = (int)(BufferLen / DirectoryEntrySize);
int Count = Math.Min(DirectoryEntries.Count - CurrentItemIndex, MaxReadCount);
for (int Index = 0; Index < Count; Index++)
{
long Position = BufferPosition + Index * DirectoryEntrySize;
WriteDirectoryEntry(Context, Position, DirectoryEntries[CurrentItemIndex++]);
}
Context.ResponseData.Write((long)Count);
return 0;
}
private void WriteDirectoryEntry(ServiceCtx Context, long Position, string FullPath)
{
for (int Offset = 0; Offset < 0x300; Offset += 8)
{
Context.Memory.WriteInt64(Position + Offset, 0);
}
byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(FullPath));
AMemoryHelper.WriteBytes(Context.Memory, Position, NameBuffer);
int Type = 0;
long Size = 0;
if (File.Exists(FullPath))
{
Type = 1;
Size = new FileInfo(FullPath).Length;
}
Context.Memory.WriteInt32(Position + 0x300, 0); //Padding?
Context.Memory.WriteInt32(Position + 0x304, Type);
Context.Memory.WriteInt64(Position + 0x308, Size);
}
public long GetEntryCount(ServiceCtx Context)
{
Context.ResponseData.Write((long)DirectoryEntries.Count);
return 0;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Disposed?.Invoke(this, EventArgs.Empty);
}
}
}
}

View File

@@ -0,0 +1,111 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
using System.IO;
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
{
class IFile : IIpcService, IDisposable
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private Stream BaseStream;
public event EventHandler<EventArgs> Disposed;
public string HostPath { get; private set; }
public IFile(Stream BaseStream, string HostPath)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Read },
{ 1, Write },
{ 2, Flush },
{ 3, SetSize },
{ 4, GetSize }
};
this.BaseStream = BaseStream;
this.HostPath = HostPath;
}
public long Read(ServiceCtx Context)
{
long Position = Context.Request.ReceiveBuff[0].Position;
long Zero = Context.RequestData.ReadInt64();
long Offset = Context.RequestData.ReadInt64();
long Size = Context.RequestData.ReadInt64();
byte[] Data = new byte[Size];
BaseStream.Seek(Offset, SeekOrigin.Begin);
int ReadSize = BaseStream.Read(Data, 0, (int)Size);
AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
Context.ResponseData.Write((long)ReadSize);
return 0;
}
public long Write(ServiceCtx Context)
{
long Position = Context.Request.SendBuff[0].Position;
long Zero = Context.RequestData.ReadInt64();
long Offset = Context.RequestData.ReadInt64();
long Size = Context.RequestData.ReadInt64();
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, Position, (int)Size);
BaseStream.Seek(Offset, SeekOrigin.Begin);
BaseStream.Write(Data, 0, (int)Size);
return 0;
}
public long Flush(ServiceCtx Context)
{
BaseStream.Flush();
return 0;
}
public long SetSize(ServiceCtx Context)
{
long Size = Context.RequestData.ReadInt64();
BaseStream.SetLength(Size);
return 0;
}
public long GetSize(ServiceCtx Context)
{
Context.ResponseData.Write(BaseStream.Length);
return 0;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && BaseStream != null)
{
BaseStream.Dispose();
Disposed?.Invoke(this, EventArgs.Empty);
}
}
}
}

View File

@@ -0,0 +1,383 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
using System.IO;
using static Ryujinx.Core.OsHle.IpcServices.ErrorCode;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
{
class IFileSystem : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private HashSet<string> OpenPaths;
private string Path;
public IFileSystem(string Path)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, CreateFile },
{ 1, DeleteFile },
{ 2, CreateDirectory },
{ 3, DeleteDirectory },
{ 4, DeleteDirectoryRecursively },
{ 5, RenameFile },
{ 6, RenameDirectory },
{ 7, GetEntryType },
{ 8, OpenFile },
{ 9, OpenDirectory },
{ 10, Commit },
{ 11, GetFreeSpaceSize },
{ 12, GetTotalSpaceSize },
//{ 13, CleanDirectoryRecursively },
//{ 14, GetFileTimeStampRaw }
};
OpenPaths = new HashSet<string>();
this.Path = Path;
}
public long CreateFile(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
long Mode = Context.RequestData.ReadInt64();
int Size = Context.RequestData.ReadInt32();
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
if (FileName == null)
{
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (File.Exists(FileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
}
if (IsPathAlreadyInUse(FileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
using (FileStream NewFile = File.Create(FileName))
{
NewFile.SetLength(Size);
}
return 0;
}
public long DeleteFile(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
if (!File.Exists(FileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (IsPathAlreadyInUse(FileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
File.Delete(FileName);
return 0;
}
public long CreateDirectory(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
string DirName = Context.Ns.VFs.GetFullPath(Path, Name);
if (DirName == null)
{
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (Directory.Exists(DirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
}
if (IsPathAlreadyInUse(DirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
Directory.CreateDirectory(DirName);
return 0;
}
public long DeleteDirectory(ServiceCtx Context)
{
return DeleteDirectory(Context, false);
}
public long DeleteDirectoryRecursively(ServiceCtx Context)
{
return DeleteDirectory(Context, true);
}
private long DeleteDirectory(ServiceCtx Context, bool Recursive)
{
long Position = Context.Request.PtrBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
string DirName = Context.Ns.VFs.GetFullPath(Path, Name);
if (!Directory.Exists(DirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (IsPathAlreadyInUse(DirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
Directory.Delete(DirName, Recursive);
return 0;
}
public long RenameFile(ServiceCtx Context)
{
long OldPosition = Context.Request.PtrBuff[0].Position;
long NewPosition = Context.Request.PtrBuff[0].Position;
string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition);
string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition);
string OldFileName = Context.Ns.VFs.GetFullPath(Path, OldName);
string NewFileName = Context.Ns.VFs.GetFullPath(Path, NewName);
if (!File.Exists(OldFileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (File.Exists(NewFileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
}
if (IsPathAlreadyInUse(OldFileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
File.Move(OldFileName, NewFileName);
return 0;
}
public long RenameDirectory(ServiceCtx Context)
{
long OldPosition = Context.Request.PtrBuff[0].Position;
long NewPosition = Context.Request.PtrBuff[0].Position;
string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition);
string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition);
string OldDirName = Context.Ns.VFs.GetFullPath(Path, OldName);
string NewDirName = Context.Ns.VFs.GetFullPath(Path, NewName);
if (!Directory.Exists(OldDirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (Directory.Exists(NewDirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
}
if (IsPathAlreadyInUse(OldDirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
Directory.Move(OldDirName, NewDirName);
return 0;
}
public long GetEntryType(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
if (File.Exists(FileName))
{
Context.ResponseData.Write(1);
}
else if (Directory.Exists(FileName))
{
Context.ResponseData.Write(0);
}
else
{
Context.ResponseData.Write(0);
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
return 0;
}
public long OpenFile(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
int FilterFlags = Context.RequestData.ReadInt32();
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
if (!File.Exists(FileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (IsPathAlreadyInUse(FileName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
FileStream Stream = new FileStream(FileName, FileMode.Open);
IFile FileInterface = new IFile(Stream, FileName);
FileInterface.Disposed += RemoveFileInUse;
lock (OpenPaths)
{
OpenPaths.Add(FileName);
}
MakeObject(Context, FileInterface);
return 0;
}
public long OpenDirectory(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
int FilterFlags = Context.RequestData.ReadInt32();
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
string DirName = Context.Ns.VFs.GetFullPath(Path, Name);
if (!Directory.Exists(DirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
}
if (IsPathAlreadyInUse(DirName))
{
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
}
IDirectory DirInterface = new IDirectory(DirName, FilterFlags);
DirInterface.Disposed += RemoveDirectoryInUse;
lock (OpenPaths)
{
OpenPaths.Add(DirName);
}
MakeObject(Context, DirInterface);
return 0;
}
public long Commit(ServiceCtx Context)
{
return 0;
}
public long GetFreeSpaceSize(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
Context.ResponseData.Write(Context.Ns.VFs.GetDrive().AvailableFreeSpace);
return 0;
}
public long GetTotalSpaceSize(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
Context.ResponseData.Write(Context.Ns.VFs.GetDrive().TotalSize);
return 0;
}
private bool IsPathAlreadyInUse(string Path)
{
lock (OpenPaths)
{
return OpenPaths.Contains(Path);
}
}
private void RemoveFileInUse(object sender, EventArgs e)
{
IFile FileInterface = (IFile)sender;
lock (OpenPaths)
{
FileInterface.Disposed -= RemoveFileInUse;
OpenPaths.Remove(FileInterface.HostPath);
}
}
private void RemoveDirectoryInUse(object sender, EventArgs e)
{
IDirectory DirInterface = (IDirectory)sender;
lock (OpenPaths)
{
DirInterface.Disposed -= RemoveDirectoryInUse;
OpenPaths.Remove(DirInterface.HostPath);
}
}
}
}

View File

@@ -0,0 +1,52 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using System.IO;
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
{
class IStorage : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private Stream BaseStream;
public IStorage(Stream BaseStream)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Read }
};
this.BaseStream = BaseStream;
}
public long Read(ServiceCtx Context)
{
long Offset = Context.RequestData.ReadInt64();
long Size = Context.RequestData.ReadInt64();
if (Context.Request.ReceiveBuff.Count > 0)
{
IpcBuffDesc BuffDesc = Context.Request.ReceiveBuff[0];
//Use smaller length to avoid overflows.
if (Size > BuffDesc.Size)
{
Size = BuffDesc.Size;
}
byte[] Data = new byte[Size];
BaseStream.Seek(Offset, SeekOrigin.Begin);
BaseStream.Read(Data, 0, Data.Length);
AMemoryHelper.WriteBytes(Context.Memory, BuffDesc.Position, Data);
}
return 0;
}
}
}

View File

@@ -0,0 +1,67 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
{
class ServiceFspSrv : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceFspSrv()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, Initialize },
{ 18, MountSdCard },
{ 51, MountSaveData },
{ 200, OpenDataStorageByCurrentProcess },
{ 203, OpenRomStorage },
{ 1005, GetGlobalAccessLogMode }
};
}
public long Initialize(ServiceCtx Context)
{
return 0;
}
public long MountSdCard(ServiceCtx Context)
{
MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetSdCardPath()));
return 0;
}
public long MountSaveData(ServiceCtx Context)
{
MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetGameSavesPath()));
return 0;
}
public long OpenDataStorageByCurrentProcess(ServiceCtx Context)
{
MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs));
return 0;
}
public long OpenRomStorage(ServiceCtx Context)
{
MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs));
return 0;
}
public long GetGlobalAccessLogMode(ServiceCtx Context)
{
Context.ResponseData.Write(0);
return 0;
}
}
}

View File

@@ -0,0 +1,27 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Hid
{
class IActiveApplicationDeviceList : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IActiveApplicationDeviceList()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, ActivateVibrationDevice }
};
}
public long ActivateVibrationDevice(ServiceCtx Context)
{
int VibrationDeviceHandle = Context.RequestData.ReadInt32();
return 0;
}
}
}

View File

@@ -0,0 +1,32 @@
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Hid
{
class IAppletResource : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private HSharedMem Handle;
public IAppletResource(HSharedMem Handle)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetSharedMemoryHandle }
};
this.Handle = Handle;
}
public static long GetSharedMemoryHandle(ServiceCtx Context)
{
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.HidHandle);
return 0;
}
}
}

View File

@@ -0,0 +1,108 @@
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Hid
{
class ServiceHid : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceHid()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, CreateAppletResource },
{ 11, ActivateTouchScreen },
{ 100, SetSupportedNpadStyleSet },
{ 101, GetSupportedNpadStyleSet },
{ 102, SetSupportedNpadIdType },
{ 103, ActivateNpad },
{ 120, SetNpadJoyHoldType },
{ 121, GetNpadJoyHoldType },
{ 200, GetVibrationDeviceInfo },
{ 203, CreateActiveVibrationDeviceList },
};
}
public long CreateAppletResource(ServiceCtx Context)
{
HSharedMem HidHndData = Context.Ns.Os.Handles.GetData<HSharedMem>(Context.Ns.Os.HidHandle);
MakeObject(Context, new IAppletResource(HidHndData));
return 0;
}
public long ActivateTouchScreen(ServiceCtx Context)
{
long Unknown = Context.RequestData.ReadInt64();
return 0;
}
public long GetSupportedNpadStyleSet(ServiceCtx Context)
{
Context.ResponseData.Write(0);
return 0;
}
public long SetSupportedNpadStyleSet(ServiceCtx Context)
{
long Unknown0 = Context.RequestData.ReadInt64();
long Unknown8 = Context.RequestData.ReadInt64();
return 0;
}
public long SetSupportedNpadIdType(ServiceCtx Context)
{
long Unknown = Context.RequestData.ReadInt64();
return 0;
}
public long ActivateNpad(ServiceCtx Context)
{
long Unknown = Context.RequestData.ReadInt64();
return 0;
}
public long SetNpadJoyHoldType(ServiceCtx Context)
{
long Unknown0 = Context.RequestData.ReadInt64();
long Unknown8 = Context.RequestData.ReadInt64();
return 0;
}
public long GetNpadJoyHoldType(ServiceCtx Context)
{
Context.ResponseData.Write(0L);
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());
return 0;
}
}
}

View File

@@ -0,0 +1,10 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices
{
interface IIpcService
{
IReadOnlyDictionary<int, ServiceProcessRequest> Commands { get; }
}
}

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

@@ -0,0 +1,31 @@
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
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceLm()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Initialize }
};
}
public long Initialize(ServiceCtx Context)
{
Context.Session.Initialize();
MakeObject(Context, new ILogger());
return 0;
}
}
}

View File

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

View File

@@ -0,0 +1,627 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.Utilities;
using Ryujinx.Graphics.Gpu;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{
class ServiceNvDrv : IIpcService
{
private delegate long ServiceProcessIoctl(ServiceCtx Context);
private static Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds =
new Dictionary<(string, int), ServiceProcessIoctl>()
{
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
{ ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig },
{ ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait },
{ ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize },
{ ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo },
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
{ ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData },
{ ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap },
{ ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo },
{ ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx },
{ ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind },
{ ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier },
{ ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority },
{ ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 },
{ ("/dev/nvmap", 0x0101), NvMapIocCreate },
{ ("/dev/nvmap", 0x0103), NvMapIocFromId },
{ ("/dev/nvmap", 0x0104), NvMapIocAlloc },
{ ("/dev/nvmap", 0x0109), NvMapIocParam },
{ ("/dev/nvmap", 0x010e), NvMapIocGetId },
};
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceNvDrv()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Open },
{ 1, Ioctl },
{ 2, Close },
{ 3, Initialize },
{ 4, QueryEvent },
{ 8, SetClientPid },
};
}
public static long Open(ServiceCtx Context)
{
long NamePtr = Context.Request.SendBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr);
int Fd = Context.Ns.Os.Fds.GenerateId(new FileDesc(Name));
Context.ResponseData.Write(Fd);
Context.ResponseData.Write(0);
return 0;
}
public static long Ioctl(ServiceCtx Context)
{
int Fd = Context.RequestData.ReadInt32();
int Cmd = Context.RequestData.ReadInt32() & 0xffff;
FileDesc FdData = Context.Ns.Os.Fds.GetData<FileDesc>(Fd);
long Position = Context.Request.PtrBuff[0].Position;
Context.ResponseData.Write(0);
if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessIoctl ProcReq))
{
return ProcReq(Context);
}
else
{
throw new NotImplementedException($"{FdData.Name} {Cmd:x4}");
}
}
public static long Close(ServiceCtx Context)
{
int Fd = Context.RequestData.ReadInt32();
Context.Ns.Os.Fds.Delete(Fd);
Context.ResponseData.Write(0);
return 0;
}
public static long Initialize(ServiceCtx Context)
{
long TransferMemSize = Context.RequestData.ReadInt64();
int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0];
Context.ResponseData.Write(0);
return 0;
}
public static long QueryEvent(ServiceCtx Context)
{
int Fd = Context.RequestData.ReadInt32();
int EventId = Context.RequestData.ReadInt32();
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(0xcafe);
Context.ResponseData.Write(0);
return 0;
}
public static long SetClientPid(ServiceCtx Context)
{
long Pid = Context.RequestData.ReadInt64();
Context.ResponseData.Write(0);
return 0;
}
private static long NvGpuAsIoctlBindChannel(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
int Fd = Context.Memory.ReadInt32(Position);
return 0;
}
private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
int Pages = Reader.ReadInt32();
int PageSize = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
long Align = Reader.ReadInt64();
if ((Flags & 1) != 0)
{
Align = Context.Ns.Gpu.ReserveMemory(Align, (long)Pages * PageSize, 1);
}
else
{
Align = Context.Ns.Gpu.ReserveMemory((long)Pages * PageSize, Align);
}
Context.Memory.WriteInt64(Position + 0x10, Align);
return 0;
}
private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
int Flags = Reader.ReadInt32();
int Kind = Reader.ReadInt32();
int Handle = Reader.ReadInt32();
int PageSize = Reader.ReadInt32();
long BuffAddr = Reader.ReadInt64();
long MapSize = Reader.ReadInt64();
long Offset = Reader.ReadInt64();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
if (NvMap != null)
{
if ((Flags & 1) != 0)
{
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, Offset, NvMap.Size);
}
else
{
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, NvMap.Size);
}
}
Context.Memory.WriteInt64(Position + 0x20, Offset);
return 0;
}
private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position);
long Unused = Reader.ReadInt64();
int BuffSize = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
BuffSize = 0x30;
Writer.WriteInt64(Unused);
Writer.WriteInt32(BuffSize);
Writer.WriteInt32(Padding);
Writer.WriteInt64(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt64(0);
Writer.WriteInt64(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt64(0);
return 0;
}
private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
int BigPageSize = Reader.ReadInt32();
int AsFd = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Reserved = Reader.ReadInt32();
long Unknown10 = Reader.ReadInt64();
long Unknown18 = Reader.ReadInt64();
long Unknown20 = Reader.ReadInt64();
return 0;
}
private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82);
for (int Index = 0; Index < 0x101; Index++)
{
Writer.WriteByte(0);
}
return 0;
}
private static long NvHostIoctlCtrlEventWait(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
int SyncPtId = Reader.ReadInt32();
int Threshold = Reader.ReadInt32();
int Timeout = Reader.ReadInt32();
int Value = Reader.ReadInt32();
Context.Memory.WriteInt32(Position + 0xc, 0xcafe);
return 0;
}
private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
Context.Memory.WriteInt32(Position, 1);
return 0;
}
private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemWriter Writer = new MemWriter(Context.Memory, Position);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
Writer.WriteInt32(0);
return 0;
}
private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position);
//Note: We should just ignore the BuffAddr, because official code
//does __memcpy_device from Position + 0x10 to BuffAddr.
long BuffSize = Reader.ReadInt64();
long BuffAddr = Reader.ReadInt64();
BuffSize = 0xa0;
Writer.WriteInt64(BuffSize);
Writer.WriteInt64(BuffAddr);
Writer.WriteInt32(0x120); //NVGPU_GPU_ARCH_GM200
Writer.WriteInt32(0xb); //NVGPU_GPU_IMPL_GM20B
Writer.WriteInt32(0xa1);
Writer.WriteInt32(1);
Writer.WriteInt64(0x40000);
Writer.WriteInt64(0);
Writer.WriteInt32(2);
Writer.WriteInt32(0x20); //NVGPU_GPU_BUS_TYPE_AXI
Writer.WriteInt32(0x20000);
Writer.WriteInt32(0x20000);
Writer.WriteInt32(0x1b);
Writer.WriteInt32(0x30000);
Writer.WriteInt32(1);
Writer.WriteInt32(0x503);
Writer.WriteInt32(0x503);
Writer.WriteInt32(0x80);
Writer.WriteInt32(0x28);
Writer.WriteInt32(0);
Writer.WriteInt64(0x55);
Writer.WriteInt32(0x902d); //FERMI_TWOD_A
Writer.WriteInt32(0xb197); //MAXWELL_B
Writer.WriteInt32(0xb1c0); //MAXWELL_COMPUTE_B
Writer.WriteInt32(0xb06f); //MAXWELL_CHANNEL_GPFIFO_A
Writer.WriteInt32(0xa140); //KEPLER_INLINE_TO_MEMORY_B
Writer.WriteInt32(0xb0b5); //MAXWELL_DMA_COPY_A
Writer.WriteInt32(1);
Writer.WriteInt32(0);
Writer.WriteInt32(2);
Writer.WriteInt32(1);
Writer.WriteInt32(0);
Writer.WriteInt32(1);
Writer.WriteInt32(0x21d70);
Writer.WriteInt32(0);
Writer.WriteByte((byte)'g');
Writer.WriteByte((byte)'m');
Writer.WriteByte((byte)'2');
Writer.WriteByte((byte)'0');
Writer.WriteByte((byte)'b');
Writer.WriteByte((byte)'\0');
Writer.WriteByte((byte)'\0');
Writer.WriteByte((byte)'\0');
Writer.WriteInt64(0);
return 0;
}
private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
int MaskBuffSize = Reader.ReadInt32();
int Reserved = Reader.ReadInt32();
long MaskBuffAddr = Reader.ReadInt64();
long Unknown = Reader.ReadInt64();
return 0;
}
private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
Context.Memory.WriteInt32(Position + 0, 7);
Context.Memory.WriteInt32(Position + 4, 1);
return 0;
}
private static long NvMapIoctlChannelSetUserData(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
return 0;
}
private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
int Fd = Context.Memory.ReadInt32(Position);
return 0;
}
private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10);
long GpFifo = Reader.ReadInt64();
int Count = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int FenceId = Reader.ReadInt32();
int FenceVal = Reader.ReadInt32();
for (int Index = 0; Index < Count; Index++)
{
long GpFifoHdr = Reader.ReadInt64();
long GpuAddr = GpFifoHdr & 0xffffffffff;
int Size = (int)(GpFifoHdr >> 40) & 0x7ffffc;
long CpuAddr = Context.Ns.Gpu.GetCpuAddr(GpuAddr);
if (CpuAddr != -1)
{
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size);
NsGpuPBEntry[] PushBuffer = NsGpuPBEntry.DecodePushBuffer(Data);
Context.Ns.Gpu.ProcessPushBuffer(PushBuffer, Context.Memory);
}
}
Writer.WriteInt32(0);
Writer.WriteInt32(0);
return 0;
}
private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
int ClassNum = Context.Memory.ReadInt32(Position + 0);
int Flags = Context.Memory.ReadInt32(Position + 4);
Context.Memory.WriteInt32(Position + 8, 0);
return 0;
}
private static long NvMapIoctlChannelZcullBind(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
long GpuVa = Reader.ReadInt64();
int Mode = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
return 0;
}
private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
long Offset = Reader.ReadInt64();
long Size = Reader.ReadInt64();
int Mem = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
return 0;
}
private static long NvMapIoctlChannelSetPriority(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
int Priority = Context.Memory.ReadInt32(Position);
return 0;
}
private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context)
{
long Position = Context.Request.PtrBuff[0].Position;
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc);
int Count = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Unknown8 = Reader.ReadInt32();
long Fence = Reader.ReadInt64();
int Unknown14 = Reader.ReadInt32();
int Unknown18 = Reader.ReadInt32();
Writer.WriteInt32(0);
Writer.WriteInt32(0);
return 0;
}
private static long NvMapIocCreate(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
int Size = Context.Memory.ReadInt32(Position);
int Id = Context.Ns.Os.NvMapIds.GenerateId();
int Handle = Context.Ns.Os.Handles.GenerateId(new HNvMap(Id, Size));
Context.Memory.WriteInt32(Position + 4, Handle);
return 0;
}
private static long NvMapIocFromId(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
int Id = Context.Memory.ReadInt32(Position);
int Handle = -1;
foreach (KeyValuePair<int, object> KV in Context.Ns.Os.Handles)
{
if (KV.Value is HNvMap NvMap && NvMap.Id == Id)
{
Handle = KV.Key;
break;
}
}
Context.Memory.WriteInt32(Position + 4, Handle);
return 0;
}
private static long NvMapIocAlloc(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
int Handle = Reader.ReadInt32();
int HeapMask = Reader.ReadInt32();
int Flags = Reader.ReadInt32();
int Align = Reader.ReadInt32();
byte Kind = (byte)Reader.ReadInt64();
long Addr = Reader.ReadInt64();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
if (NvMap != null)
{
NvMap.Address = Addr;
NvMap.Align = Align;
NvMap.Kind = Kind;
}
Logging.Debug($"NvMapIocAlloc at {NvMap.Address:x16}");
return 0;
}
private static long NvMapIocParam(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
int Handle = Reader.ReadInt32();
int Param = Reader.ReadInt32();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
int Response = 0;
switch (Param)
{
case 1: Response = NvMap.Size; break;
case 2: Response = NvMap.Align; break;
case 4: Response = 0x40000000; break;
case 5: Response = NvMap.Kind; break;
}
Context.Memory.WriteInt32(Position + 8, Response);
return 0;
}
private static long NvMapIocGetId(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
int Handle = Context.Memory.ReadInt32(Position + 4);
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
Context.Memory.WriteInt32(Position, NvMap.Id);
return 0;
}
}
}

View File

@@ -0,0 +1,24 @@
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
namespace Ryujinx.Core.OsHle.IpcServices
{
static class ObjHelper
{
public static void MakeObject(ServiceCtx Context, object Obj)
{
if (Context.Session is HDomain Dom)
{
Context.Response.ResponseObjIds.Add(Dom.GenerateObjectId(Obj));
}
else
{
HSessionObj HndData = new HSessionObj(Context.Session, Obj);
int VHandle = Context.Ns.Os.Handles.GenerateId(HndData);
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle);
}
}
}
}

View File

@@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Pctl
{
class IParentalControlService : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IParentalControlService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
};
}
}
}

View File

@@ -0,0 +1,29 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Pctl
{
class ServicePctl : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServicePctl()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, CreateService }
};
}
public static long CreateService(ServiceCtx Context)
{
MakeObject(Context, new IParentalControlService());
return 0;
}
}
}

View File

@@ -0,0 +1,51 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Pl
{
class ServicePl : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServicePl()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, GetLoadState },
{ 2, GetFontSize },
{ 3, GetSharedMemoryAddressOffset },
{ 4, GetSharedMemoryNativeHandle }
};
}
public static long GetLoadState(ServiceCtx Context)
{
Context.ResponseData.Write(1); //Loaded
return 0;
}
public static long GetFontSize(ServiceCtx Context)
{
Context.ResponseData.Write(Horizon.FontSize);
return 0;
}
public static long GetSharedMemoryAddressOffset(ServiceCtx Context)
{
Context.ResponseData.Write(0);
return 0;
}
public static long GetSharedMemoryNativeHandle(ServiceCtx Context)
{
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle);
return 0;
}
}
}

View File

@@ -0,0 +1,54 @@
using Ryujinx.Core.OsHle.IpcServices.Acc;
using Ryujinx.Core.OsHle.IpcServices.Am;
using Ryujinx.Core.OsHle.IpcServices.Apm;
using Ryujinx.Core.OsHle.IpcServices.Aud;
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;
using Ryujinx.Core.OsHle.IpcServices.Set;
using Ryujinx.Core.OsHle.IpcServices.Sm;
using Ryujinx.Core.OsHle.IpcServices.Time;
using Ryujinx.Core.OsHle.IpcServices.Vi;
using System;
namespace Ryujinx.Core.OsHle.IpcServices
{
static class ServiceFactory
{
public static IIpcService MakeService(string Name)
{
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();
case "audout:u": return new ServiceAudOut();
case "audren:u": return new ServiceAudRen();
case "friend:a": return new ServiceFriend();
case "fsp-srv": return new ServiceFspSrv();
case "hid": return new ServiceHid();
case "lm": return new ServiceLm();
case "nvdrv": return new ServiceNvDrv();
case "nvdrv:a": return new ServiceNvDrv();
case "pctl:a": return new ServicePctl();
case "pl:u": return new ServicePl();
case "set": return new ServiceSet();
case "sm:": return new ServiceSm();
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,46 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Set
{
class ServiceSet : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSet()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, GetAvailableLanguageCodes }
};
}
private const int LangCodesCount = 13;
public static long GetAvailableLanguageCodes(ServiceCtx Context)
{
int PtrBuffSize = Context.RequestData.ReadInt32();
if (Context.Request.RecvListBuff.Count > 0)
{
long Position = Context.Request.RecvListBuff[0].Position;
short Size = Context.Request.RecvListBuff[0].Size;
//This should return an array of ints with values matching the LanguageCode enum.
byte[] Data = new byte[Size];
Data[0] = 0;
Data[1] = 1;
AMemoryHelper.WriteBytes(Context.Memory, Position, Data);
}
Context.ResponseData.Write(LangCodesCount);
return 0;
}
}
}

View File

@@ -0,0 +1,67 @@
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Sm
{
class ServiceSm : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSm()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Initialize },
{ 1, GetService }
};
}
private const int SmNotInitialized = 0x415;
public long Initialize(ServiceCtx Context)
{
Context.Session.Initialize();
return 0;
}
public long GetService(ServiceCtx Context)
{
//Only for kernel version > 3.0.0.
if (!Context.Session.IsInitialized)
{
//return SmNotInitialized;
}
string Name = string.Empty;
for (int Index = 0; Index < 8 &&
Context.RequestData.BaseStream.Position <
Context.RequestData.BaseStream.Length; Index++)
{
byte Chr = Context.RequestData.ReadByte();
if (Chr >= 0x20 && Chr < 0x7f)
{
Name += (char)Chr;
}
}
if (Name == string.Empty)
{
return 0;
}
HSession Session = new HSession(ServiceFactory.MakeService(Name));
int Handle = Context.Ns.Os.Handles.GenerateId(Session);
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
return 0;
}
}
}

View File

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

View File

@@ -0,0 +1,42 @@
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Time
{
class ISystemClock : 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.Utc);
private SystemClockType ClockType;
public ISystemClock(SystemClockType ClockType)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetCurrentTime }
};
this.ClockType = ClockType;
}
public long GetCurrentTime(ServiceCtx Context)
{
DateTime CurrentTime = DateTime.Now;
if (ClockType == SystemClockType.User ||
ClockType == SystemClockType.Network)
{
CurrentTime = CurrentTime.ToUniversalTime();
}
Context.ResponseData.Write((long)(DateTime.Now - Epoch).TotalSeconds);
return 0;
}
}
}

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

@@ -0,0 +1,62 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Time
{
class ServiceTime : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceTime()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetStandardUserSystemClock },
{ 1, GetStandardNetworkSystemClock },
{ 2, GetStandardSteadyClock },
{ 3, GetTimeZoneService },
{ 4, GetStandardLocalSystemClock }
};
}
public long GetStandardUserSystemClock(ServiceCtx Context)
{
MakeObject(Context, new ISystemClock(SystemClockType.User));
return 0;
}
public long GetStandardNetworkSystemClock(ServiceCtx Context)
{
MakeObject(Context, new ISystemClock(SystemClockType.Network));
return 0;
}
public long GetStandardSteadyClock(ServiceCtx Context)
{
MakeObject(Context, new ISteadyClock());
return 0;
}
public long GetTimeZoneService(ServiceCtx Context)
{
MakeObject(Context, new ITimeZoneService());
return 0;
}
public long GetStandardLocalSystemClock(ServiceCtx Context)
{
MakeObject(Context, new ISystemClock(SystemClockType.Local));
return 0;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Ryujinx.Core.OsHle.IpcServices.Time
{
enum SystemClockType
{
User,
Network,
Local
}
}

View File

@@ -0,0 +1,60 @@
using System.IO;
namespace Ryujinx.Core.OsHle.IpcServices.Android
{
struct GbpBuffer
{
public int Magic { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public int Stride { get; private set; }
public int Format { get; private set; }
public int Usage { get; private set; }
public int Pid { get; private set; }
public int RefCount { get; private set; }
public int FdsCount { get; private set; }
public int IntsCount { get; private set; }
public byte[] RawData { get; private set; }
public int Size => RawData.Length + 10 * 4;
public GbpBuffer(BinaryReader Reader)
{
Magic = Reader.ReadInt32();
Width = Reader.ReadInt32();
Height = Reader.ReadInt32();
Stride = Reader.ReadInt32();
Format = Reader.ReadInt32();
Usage = Reader.ReadInt32();
Pid = Reader.ReadInt32();
RefCount = Reader.ReadInt32();
FdsCount = Reader.ReadInt32();
IntsCount = Reader.ReadInt32();
RawData = Reader.ReadBytes((FdsCount + IntsCount) * 4);
}
public void Write(BinaryWriter Writer)
{
Writer.Write(Magic);
Writer.Write(Width);
Writer.Write(Height);
Writer.Write(Stride);
Writer.Write(Format);
Writer.Write(Usage);
Writer.Write(Pid);
Writer.Write(RefCount);
Writer.Write(FdsCount);
Writer.Write(IntsCount);
Writer.Write(RawData);
}
}
}

View File

@@ -0,0 +1,176 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using System.IO;
using static Ryujinx.Core.OsHle.IpcServices.Android.Parcel;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Vi
{
class IApplicationDisplayService : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IApplicationDisplayService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 100, GetRelayService },
{ 101, GetSystemDisplayService },
{ 102, GetManagerDisplayService },
{ 103, GetIndirectDisplayTransactionService },
{ 1010, OpenDisplay },
{ 2020, OpenLayer },
{ 2030, CreateStrayLayer },
{ 2101, SetLayerScalingMode },
{ 5202, GetDisplayVSyncEvent }
};
}
public long GetRelayService(ServiceCtx Context)
{
MakeObject(Context, new IHOSBinderDriver());
return 0;
}
public long GetSystemDisplayService(ServiceCtx Context)
{
MakeObject(Context, new ISystemDisplayService());
return 0;
}
public long GetManagerDisplayService(ServiceCtx Context)
{
MakeObject(Context, new IManagerDisplayService());
return 0;
}
public long GetIndirectDisplayTransactionService(ServiceCtx Context)
{
MakeObject(Context, new IHOSBinderDriver());
return 0;
}
public long OpenDisplay(ServiceCtx Context)
{
string Name = GetDisplayName(Context);
long DisplayId = Context.Ns.Os.Displays.GenerateId(new Display(Name));
Context.ResponseData.Write(DisplayId);
return 0;
}
public long OpenLayer(ServiceCtx Context)
{
long LayerId = Context.RequestData.ReadInt64();
long UserId = Context.RequestData.ReadInt64();
long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
AMemoryHelper.WriteBytes(Context.Memory, ParcelPtr, Parcel);
Context.ResponseData.Write((long)Parcel.Length);
return 0;
}
public long CreateStrayLayer(ServiceCtx Context)
{
long LayerFlags = Context.RequestData.ReadInt64();
long DisplayId = Context.RequestData.ReadInt64();
long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
Display Disp = Context.Ns.Os.Displays.GetData<Display>((int)DisplayId);
byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
AMemoryHelper.WriteBytes(Context.Memory, ParcelPtr, Parcel);
Context.ResponseData.Write(0L);
Context.ResponseData.Write((long)Parcel.Length);
return 0;
}
public long SetLayerScalingMode(ServiceCtx Context)
{
int ScalingMode = Context.RequestData.ReadInt32();
long Unknown = Context.RequestData.ReadInt64();
return 0;
}
public long GetDisplayVSyncEvent(ServiceCtx Context)
{
string Name = GetDisplayName(Context);
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
return 0;
}
private byte[] MakeIGraphicsBufferProducer(long BasePtr)
{
long Id = 0x20;
long CookiePtr = 0L;
using (MemoryStream MS = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(MS);
//flat_binder_object (size is 0x28)
Writer.Write(2); //Type (BINDER_TYPE_WEAK_BINDER)
Writer.Write(0); //Flags
Writer.Write((int)(Id >> 0));
Writer.Write((int)(Id >> 32));
Writer.Write((int)(CookiePtr >> 0));
Writer.Write((int)(CookiePtr >> 32));
Writer.Write((byte)'d');
Writer.Write((byte)'i');
Writer.Write((byte)'s');
Writer.Write((byte)'p');
Writer.Write((byte)'d');
Writer.Write((byte)'r');
Writer.Write((byte)'v');
Writer.Write((byte)'\0');
Writer.Write(0L); //Pad
return MakeParcel(MS.ToArray(), new byte[] { 0, 0, 0, 0 });
}
}
private string GetDisplayName(ServiceCtx Context)
{
string Name = string.Empty;
for (int Index = 0; Index < 8 &&
Context.RequestData.BaseStream.Position <
Context.RequestData.BaseStream.Length; Index++)
{
byte Chr = Context.RequestData.ReadByte();
if (Chr >= 0x20 && Chr < 0x7f)
{
Name += (char)Chr;
}
}
return Name;
}
}
}

View File

@@ -0,0 +1,76 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.IpcServices.Android;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Vi
{
class IHOSBinderDriver : IIpcService, IDisposable
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private NvFlinger Flinger;
public IHOSBinderDriver()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, TransactParcel },
{ 1, AdjustRefcount },
{ 2, GetNativeHandle }
};
Flinger = new NvFlinger();
}
public long TransactParcel(ServiceCtx Context)
{
int Id = Context.RequestData.ReadInt32();
int Code = Context.RequestData.ReadInt32();
long DataPos = Context.Request.SendBuff[0].Position;
long DataSize = Context.Request.SendBuff[0].Size;
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize);
Data = Parcel.GetParcelData(Data);
return Flinger.ProcessParcelRequest(Context, Data, Code);
}
public long AdjustRefcount(ServiceCtx Context)
{
int Id = Context.RequestData.ReadInt32();
int AddVal = Context.RequestData.ReadInt32();
int Type = Context.RequestData.ReadInt32();
return 0;
}
public long GetNativeHandle(ServiceCtx Context)
{
int Id = Context.RequestData.ReadInt32();
uint Unk = Context.RequestData.ReadUInt32();
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe);
return 0;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Flinger.Dispose();
}
}
}
}

View File

@@ -0,0 +1,33 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Vi
{
class IManagerDisplayService : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IManagerDisplayService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 2010, CreateManagedLayer },
{ 6000, AddToLayerStack }
};
}
public static long CreateManagedLayer(ServiceCtx Context)
{
Context.ResponseData.Write(0L); //LayerId
return 0;
}
public static long AddToLayerStack(ServiceCtx Context)
{
return 0;
}
}
}

View File

@@ -0,0 +1,25 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Vi
{
class ISystemDisplayService : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ISystemDisplayService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 2205, SetLayerZ }
};
}
public static long SetLayerZ(ServiceCtx Context)
{
return 0;
}
}
}

View File

@@ -0,0 +1,395 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles;
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using static Ryujinx.Core.OsHle.IpcServices.Android.Parcel;
namespace Ryujinx.Core.OsHle.IpcServices.Android
{
class NvFlinger : IDisposable
{
private delegate long ServiceProcessParcel(ServiceCtx Context, BinaryReader ParcelReader);
private Dictionary<(string, int), ServiceProcessParcel> Commands;
private const int BufferQueueCount = 0x40;
private const int BufferQueueMask = BufferQueueCount - 1;
[Flags]
private enum HalTransform
{
FlipX = 1 << 0,
FlipY = 1 << 1,
Rotate90 = 1 << 2
}
private enum BufferState
{
Free,
Dequeued,
Queued,
Acquired
}
private struct BufferEntry
{
public BufferState State;
public HalTransform Transform;
public GbpBuffer Data;
}
private BufferEntry[] BufferQueue;
private ManualResetEvent WaitBufferFree;
private bool KeepRunning;
public NvFlinger()
{
Commands = new Dictionary<(string, int), ServiceProcessParcel>()
{
{ ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
{ ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
{ ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
};
BufferQueue = new BufferEntry[0x40];
WaitBufferFree = new ManualResetEvent(false);
KeepRunning = true;
}
public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
{
using (MemoryStream MS = new MemoryStream(ParcelData))
{
BinaryReader Reader = new BinaryReader(MS);
MS.Seek(4, SeekOrigin.Current);
int StrSize = Reader.ReadInt32();
string InterfaceName = Encoding.Unicode.GetString(Reader.ReadBytes(StrSize * 2));
long Remainder = MS.Position & 0xf;
if (Remainder != 0)
{
MS.Seek(0x10 - Remainder, SeekOrigin.Current);
}
MS.Seek(0x50, SeekOrigin.Begin);
if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
{
Logging.Debug($"{InterfaceName} {ProcReq.Method.Name}");
return ProcReq(Context, Reader);
}
else
{
throw new NotImplementedException($"{InterfaceName} {Code}");
}
}
}
private long GbpRequestBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
int Slot = ParcelReader.ReadInt32();
using (MemoryStream MS = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(MS);
BufferEntry Entry = BufferQueue[Slot];
int BufferCount = 1; //?
long BufferSize = Entry.Data.Size;
Writer.Write(BufferCount);
Writer.Write(BufferSize);
Entry.Data.Write(Writer);
Writer.Write(0);
return MakeReplyParcel(Context, MS.ToArray());
}
}
private long GbpDequeueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
//TODO: Errors.
int Format = ParcelReader.ReadInt32();
int Width = ParcelReader.ReadInt32();
int Height = ParcelReader.ReadInt32();
int GetTimestamps = ParcelReader.ReadInt32();
int Usage = ParcelReader.ReadInt32();
int Slot = GetFreeSlotBlocking(Width, Height);
return MakeReplyParcel(Context, Slot, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
//TODO: Errors.
int Slot = ParcelReader.ReadInt32();
int Unknown4 = ParcelReader.ReadInt32();
int Unknown8 = ParcelReader.ReadInt32();
int Unknownc = ParcelReader.ReadInt32();
int Timestamp = ParcelReader.ReadInt32();
int IsAutoTimestamp = ParcelReader.ReadInt32();
int CropTop = ParcelReader.ReadInt32();
int CropLeft = ParcelReader.ReadInt32();
int CropRight = ParcelReader.ReadInt32();
int CropBottom = ParcelReader.ReadInt32();
int ScalingMode = ParcelReader.ReadInt32();
int Transform = ParcelReader.ReadInt32();
int StickyTransform = ParcelReader.ReadInt32();
int Unknown34 = ParcelReader.ReadInt32();
int Unknown38 = ParcelReader.ReadInt32();
int IsFenceValid = ParcelReader.ReadInt32();
int Fence0Id = ParcelReader.ReadInt32();
int Fence0Value = ParcelReader.ReadInt32();
int Fence1Id = ParcelReader.ReadInt32();
int Fence1Value = ParcelReader.ReadInt32();
BufferQueue[Slot].Transform = (HalTransform)Transform;
BufferQueue[Slot].State = BufferState.Queued;
SendFrameBuffer(Context, Slot);
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
}
private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
//TODO: Errors.
int Slot = ParcelReader.ReadInt32();
BufferQueue[Slot].State = BufferState.Free;
return MakeReplyParcel(Context, 0);
}
private long GbpQuery(ServiceCtx Context, BinaryReader ParcelReader)
{
return MakeReplyParcel(Context, 0, 0);
}
private long GbpConnect(ServiceCtx Context, BinaryReader ParcelReader)
{
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
}
private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
int Slot = ParcelReader.ReadInt32();
int BufferCount = ParcelReader.ReadInt32();
long BufferSize = ParcelReader.ReadInt64();
BufferQueue[Slot].State = BufferState.Free;
BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
return MakeReplyParcel(Context, 0);
}
private long MakeReplyParcel(ServiceCtx Context, params int[] Ints)
{
using (MemoryStream MS = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(MS);
foreach (int Int in Ints)
{
Writer.Write(Int);
}
return MakeReplyParcel(Context, MS.ToArray());
}
}
private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
{
long ReplyPos = Context.Request.ReceiveBuff[0].Position;
long ReplySize = Context.Request.ReceiveBuff[0].Size;
byte[] Reply = MakeParcel(Data, new byte[0]);
AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply);
return 0;
}
private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot)
{
int FbWidth = BufferQueue[Slot].Data.Width;
int FbHeight = BufferQueue[Slot].Data.Height;
int FbSize = FbWidth * FbHeight * 4;
HNvMap NvMap = GetNvMap(Context, Slot);
if (FbSize < 0 || NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize)
{
Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!");
BufferQueue[Slot].State = BufferState.Free;
WaitBufferFree.Set();
return;
}
BufferQueue[Slot].State = BufferState.Acquired;
float ScaleX = 1;
float ScaleY = 1;
float Rotate = 0;
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX))
{
ScaleX = -1;
}
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY))
{
ScaleY = -1;
}
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
{
Rotate = MathF.PI * 0.5f;
}
byte* Fb = (byte*)Context.Ns.Ram + NvMap.Address;
Context.Ns.Gpu.Renderer.QueueAction(delegate()
{
Context.Ns.Gpu.Renderer.SetFrameBuffer(
Fb,
FbWidth,
FbHeight,
ScaleX,
ScaleY,
Rotate);
BufferQueue[Slot].State = BufferState.Free;
lock (WaitBufferFree)
{
WaitBufferFree.Set();
}
});
}
private HNvMap GetNvMap(ServiceCtx Context, int Slot)
{
int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
if (!BitConverter.IsLittleEndian)
{
byte[] RawValue = BitConverter.GetBytes(NvMapHandle);
Array.Reverse(RawValue);
NvMapHandle = BitConverter.ToInt32(RawValue, 0);
}
return Context.Ns.Os.Handles.GetData<HNvMap>(NvMapHandle);
}
private int GetFreeSlotBlocking(int Width, int Height)
{
int Slot;
do
{
lock (WaitBufferFree)
{
if ((Slot = GetFreeSlot(Width, Height)) != -1)
{
break;
}
Logging.Debug("Waiting for a free BufferQueue slot...");
if (!KeepRunning)
{
break;
}
WaitBufferFree.Reset();
}
WaitBufferFree.WaitOne();
}
while (KeepRunning);
Logging.Debug($"Found free BufferQueue slot {Slot}!");
return Slot;
}
private int GetFreeSlot(int Width, int Height)
{
lock (BufferQueue)
{
for (int Slot = 0; Slot < BufferQueue.Length; Slot++)
{
if (BufferQueue[Slot].State != BufferState.Free)
{
continue;
}
GbpBuffer Data = BufferQueue[Slot].Data;
if (Data.Width == Width &&
Data.Height == Height)
{
BufferQueue[Slot].State = BufferState.Dequeued;
return Slot;
}
}
}
return -1;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (WaitBufferFree)
{
KeepRunning = false;
WaitBufferFree.Set();
}
WaitBufferFree.Dispose();
}
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.IO;
namespace Ryujinx.Core.OsHle.IpcServices.Android
{
static class Parcel
{
public static byte[] GetParcelData(byte[] Parcel)
{
if (Parcel == null)
{
throw new ArgumentNullException(nameof(Parcel));
}
using (MemoryStream MS = new MemoryStream(Parcel))
{
BinaryReader Reader = new BinaryReader(MS);
int DataSize = Reader.ReadInt32();
int DataOffset = Reader.ReadInt32();
int ObjsSize = Reader.ReadInt32();
int ObjsOffset = Reader.ReadInt32();
MS.Seek(DataOffset - 0x10, SeekOrigin.Current);
return Reader.ReadBytes(DataSize);
}
}
public static byte[] MakeParcel(byte[] Data, byte[] Objs)
{
if (Data == null)
{
throw new ArgumentNullException(nameof(Data));
}
if (Objs == null)
{
throw new ArgumentNullException(nameof(Objs));
}
using (MemoryStream MS = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(MS);
Writer.Write(Data.Length);
Writer.Write(0x10);
Writer.Write(Objs.Length);
Writer.Write(Data.Length + 0x10);
Writer.Write(Data);
Writer.Write(Objs);
return MS.ToArray();
}
}
}
}

View File

@@ -0,0 +1,31 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Vi
{
class ServiceVi : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceVi()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 2, GetDisplayService }
};
}
public long GetDisplayService(ServiceCtx Context)
{
int Unknown = Context.RequestData.ReadInt32();
MakeObject(Context, new IApplicationDisplayService());
return 0;
}
}
}