aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Core/OsHle/Objects
diff options
context:
space:
mode:
authoremmauss <emmausssss@gmail.com>2018-02-20 22:09:23 +0200
committergdkchan <gab.dark.100@gmail.com>2018-02-20 17:09:23 -0300
commit62b827f474f0aa2152dd339fcc7cf31084e16a0b (patch)
tree0e5c55b341aee4db0ccb841a084f253ec5e05657 /Ryujinx.Core/OsHle/Objects
parentcb665bb715834526d73c9469d16114b287faaecd (diff)
Split main project into core,graphics and chocolarm4 subproject (#29)
Diffstat (limited to 'Ryujinx.Core/OsHle/Objects')
-rw-r--r--Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs33
-rw-r--r--Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs33
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs80
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs85
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs20
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs74
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs20
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs20
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs20
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs20
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs53
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IStorage.cs33
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs62
-rw-r--r--Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs33
-rw-r--r--Ryujinx.Core/OsHle/Objects/Apm/ISession.cs28
-rw-r--r--Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs180
-rw-r--r--Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs66
-rw-r--r--Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs20
-rw-r--r--Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs133
-rw-r--r--Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs93
-rw-r--r--Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs247
-rw-r--r--Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs52
-rw-r--r--Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs32
-rw-r--r--Ryujinx.Core/OsHle/Objects/IIpcInterface.cs10
-rw-r--r--Ryujinx.Core/OsHle/Objects/ObjHelper.cs24
-rw-r--r--Ryujinx.Core/OsHle/Objects/Parcel.cs58
-rw-r--r--Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs20
-rw-r--r--Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs42
-rw-r--r--Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs20
-rw-r--r--Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs9
-rw-r--r--Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs176
-rw-r--r--Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs214
-rw-r--r--Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs33
-rw-r--r--Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs25
34 files changed, 2068 insertions, 0 deletions
diff --git a/Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs b/Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs
new file mode 100644
index 00000000..afbfab24
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Acc/IManagerForApplication.cs
@@ -0,0 +1,33 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Acc
+{
+ class IManagerForApplication : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs b/Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs
new file mode 100644
index 00000000..94d17183
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Acc/IProfile.cs
@@ -0,0 +1,33 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Acc
+{
+ class IProfile : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs b/Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs
new file mode 100644
index 00000000..939ad248
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IApplicationFunctions.cs
@@ -0,0 +1,80 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+using System.IO;
+
+using static Ryujinx.Core.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IApplicationFunctions : IIpcInterface
+ {
+ 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 },
+ { 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 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();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs b/Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs
new file mode 100644
index 00000000..4a164daf
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IApplicationProxy.cs
@@ -0,0 +1,85 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+using static Ryujinx.Core.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IApplicationProxy : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs b/Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs
new file mode 100644
index 00000000..c37042fd
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IAudioController.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IAudioController : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public IAudioController()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ //...
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs b/Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs
new file mode 100644
index 00000000..83d61fa6
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/ICommonStateGetter.cs
@@ -0,0 +1,74 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class ICommonStateGetter : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs b/Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs
new file mode 100644
index 00000000..d04d8363
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IDebugFunctions.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IDebugFunctions : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public IDebugFunctions()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ //...
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs b/Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs
new file mode 100644
index 00000000..9eafa70d
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IDisplayController.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IDisplayController : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public IDisplayController()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ //...
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs b/Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs
new file mode 100644
index 00000000..10e0f4f4
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/ILibraryAppletCreator.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class ILibraryAppletCreator : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public ILibraryAppletCreator()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ //...
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs b/Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs
new file mode 100644
index 00000000..1feacfa6
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IParentalControlService.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IParentalControlService : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public IParentalControlService()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ //...
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs b/Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs
new file mode 100644
index 00000000..691bb202
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/ISelfController.cs
@@ -0,0 +1,53 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class ISelfController : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public ISelfController()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ { 11, SetOperationModeChangedNotification },
+ { 12, SetPerformanceModeChangedNotification },
+ { 13, SetFocusHandlingMode },
+ { 16, SetOutOfFocusSuspendingEnabled }
+ };
+ }
+
+ 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 SetOutOfFocusSuspendingEnabled(ServiceCtx Context)
+ {
+ bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
+
+ return 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IStorage.cs b/Ryujinx.Core/OsHle/Objects/Am/IStorage.cs
new file mode 100644
index 00000000..b30059ba
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IStorage.cs
@@ -0,0 +1,33 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+using static Ryujinx.Core.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IStorage : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs b/Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs
new file mode 100644
index 00000000..df260cc3
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IStorageAccessor.cs
@@ -0,0 +1,62 @@
+using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Ipc;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IStorageAccessor : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs b/Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs
new file mode 100644
index 00000000..aa6e961e
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Am/IWindowController.cs
@@ -0,0 +1,33 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Am
+{
+ class IWindowController : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Apm/ISession.cs b/Ryujinx.Core/OsHle/Objects/Apm/ISession.cs
new file mode 100644
index 00000000..3ab33005
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Apm/ISession.cs
@@ -0,0 +1,28 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Apm
+{
+ class ISession : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs
new file mode 100644
index 00000000..061c6376
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Aud/IAudioOut.cs
@@ -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.Objects.Aud
+{
+ class IAudioOut : IIpcInterface
+ {
+ 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;
+ }
+ }
+}
diff --git a/Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs b/Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs
new file mode 100644
index 00000000..05356477
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Aud/IAudioRenderer.cs
@@ -0,0 +1,66 @@
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Aud
+{
+ class IAudioRenderer : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs b/Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs
new file mode 100644
index 00000000..e98e27ca
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Friend/IFriendService.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Friend
+{
+ class IFriendService : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public IFriendService()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ //...
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs
new file mode 100644
index 00000000..88fce28e
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IDirectory.cs
@@ -0,0 +1,133 @@
+using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Ipc;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Ryujinx.Core.OsHle.Objects.FspSrv
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x310)]
+ struct DirectoryEntry
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x300)]
+ public byte[] Name;
+ public int Unknown;
+ public byte Type;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3)]
+ public byte[] Padding;
+ public long Size;
+ }
+
+ enum DirectoryEntryType
+ {
+ Directory,
+ File
+ }
+
+ class IDirectory : IIpcInterface
+ {
+ private List<DirectoryEntry> DirectoryEntries = new List<DirectoryEntry>();
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ private string HostPath;
+
+ public IDirectory(string HostPath, int flags)
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ { 0, Read },
+ { 1, GetEntryCount }
+ };
+
+ this.HostPath = HostPath;
+
+ if ((flags & 1) == 1)
+ {
+ string[] Directories = Directory.GetDirectories(HostPath, "*", SearchOption.TopDirectoryOnly).
+ Where(x => (new FileInfo(x).Attributes & FileAttributes.Hidden) == 0).ToArray();
+
+ foreach (string Directory in Directories)
+ {
+ DirectoryEntry Info = new DirectoryEntry
+ {
+ Name = Encoding.UTF8.GetBytes(Directory),
+ Type = (byte)DirectoryEntryType.Directory,
+ Size = 0
+ };
+
+ Array.Resize(ref Info.Name, 0x300);
+ DirectoryEntries.Add(Info);
+ }
+ }
+
+ if ((flags & 2) == 2)
+ {
+ string[] Files = Directory.GetFiles(HostPath, "*", SearchOption.TopDirectoryOnly).
+ Where(x => (new FileInfo(x).Attributes & FileAttributes.Hidden) == 0).ToArray();
+
+ foreach (string FileName in Files)
+ {
+ DirectoryEntry Info = new DirectoryEntry
+ {
+ Name = Encoding.UTF8.GetBytes(Path.GetFileName(FileName)),
+ Type = (byte)DirectoryEntryType.File,
+ Size = new FileInfo(Path.Combine(HostPath, FileName)).Length
+ };
+
+ Array.Resize(ref Info.Name, 0x300);
+ DirectoryEntries.Add(Info);
+ }
+ }
+ }
+
+ private int LastItem = 0;
+ public long Read(ServiceCtx Context)
+ {
+ long BufferPosition = Context.Request.ReceiveBuff[0].Position;
+ long BufferLen = Context.Request.ReceiveBuff[0].Size;
+ long MaxDirectories = BufferLen / Marshal.SizeOf(typeof(DirectoryEntry));
+
+ if (MaxDirectories > DirectoryEntries.Count - LastItem)
+ {
+ MaxDirectories = DirectoryEntries.Count - LastItem;
+ }
+
+ int CurrentIndex;
+ for (CurrentIndex = 0; CurrentIndex < MaxDirectories; CurrentIndex++)
+ {
+ int CurrentItem = LastItem + CurrentIndex;
+
+ byte[] DirectoryEntry = new byte[Marshal.SizeOf(typeof(DirectoryEntry))];
+ IntPtr Ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DirectoryEntry)));
+ Marshal.StructureToPtr(DirectoryEntries[CurrentItem], Ptr, true);
+ Marshal.Copy(Ptr, DirectoryEntry, 0, Marshal.SizeOf(typeof(DirectoryEntry)));
+ Marshal.FreeHGlobal(Ptr);
+
+ AMemoryHelper.WriteBytes(Context.Memory, BufferPosition + Marshal.SizeOf(typeof(DirectoryEntry)) * CurrentIndex, DirectoryEntry);
+ }
+
+ if (LastItem < DirectoryEntries.Count)
+ {
+ LastItem += CurrentIndex;
+ Context.ResponseData.Write((long)CurrentIndex); // index = number of entries written this call.
+ }
+ else
+ {
+ Context.ResponseData.Write((long)0);
+ }
+
+ return 0;
+ }
+
+ public long GetEntryCount(ServiceCtx Context)
+ {
+ Context.ResponseData.Write((long)DirectoryEntries.Count);
+ return 0;
+ }
+ }
+}
diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs
new file mode 100644
index 00000000..95fbc650
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IFile.cs
@@ -0,0 +1,93 @@
+using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Ipc;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.Core.OsHle.Objects.FspSrv
+{
+ class IFile : IIpcInterface, IDisposable
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ private Stream BaseStream;
+
+ public IFile(Stream BaseStream)
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ { 0, Read },
+ { 1, Write },
+ // { 2, Flush },
+ { 3, SetSize },
+ { 4, GetSize }
+ };
+
+ this.BaseStream = BaseStream;
+ }
+
+ 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 GetSize(ServiceCtx Context)
+ {
+ Context.ResponseData.Write(BaseStream.Length);
+ return 0;
+ }
+
+ public long SetSize(ServiceCtx Context)
+ {
+ long Size = Context.RequestData.ReadInt64();
+ BaseStream.SetLength(Size);
+ return 0;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing && BaseStream != null)
+ {
+ BaseStream.Dispose();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs
new file mode 100644
index 00000000..68b15845
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IFileSystem.cs
@@ -0,0 +1,247 @@
+using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+using System.IO;
+
+using static Ryujinx.Core.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.Core.OsHle.Objects.FspSrv
+{
+ class IFileSystem : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ private string Path;
+
+ public IFileSystem(string Path)
+ {
+ //TODO: implement.
+ 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 }
+ };
+
+ this.Path = Path;
+ }
+
+ public long CreateFile(ServiceCtx Context)
+ {
+ long Position = Context.Request.PtrBuff[0].Position;
+ string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
+ ulong Mode = Context.RequestData.ReadUInt64();
+ uint Size = Context.RequestData.ReadUInt32();
+ string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
+
+ if (FileName != null)
+ {
+ FileStream NewFile = File.Create(FileName);
+ NewFile.SetLength(Size);
+ NewFile.Close();
+ return 0;
+ }
+
+ //TODO: Correct error code.
+ return -1;
+ }
+
+ 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 (FileName != null)
+ {
+ File.Delete(FileName);
+ return 0;
+ }
+
+ //TODO: Correct error code.
+ return -1;
+ }
+
+ public long CreateDirectory(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 (FileName != null)
+ {
+ Directory.CreateDirectory(FileName);
+ return 0;
+ }
+
+ //TODO: Correct error code.
+ return -1;
+ }
+
+ public long DeleteDirectory(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 (FileName != null)
+ {
+ Directory.Delete(FileName);
+ return 0;
+ }
+
+ // TODO: Correct error code.
+ return -1;
+ }
+
+ public long DeleteDirectoryRecursively(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 (FileName != null)
+ {
+ Directory.Delete(FileName, true); // recursive = true
+ return 0;
+ }
+
+ // TODO: Correct error code.
+ return -1;
+ }
+
+ 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 (OldFileName != null && NewFileName != null)
+ {
+ File.Move(OldFileName, NewFileName);
+ return 0;
+ }
+
+ // TODO: Correct error code.
+ return -1;
+ }
+
+ 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 (OldDirName != null && NewDirName != null)
+ {
+ Directory.Move(OldDirName, NewDirName);
+ return 0;
+ }
+
+ // TODO: Correct error code.
+ return -1;
+ }
+
+ 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 (FileName == null)
+ {
+ //TODO: Correct error code.
+ return -1;
+ }
+
+ bool IsFile = File.Exists(FileName);
+
+ Context.ResponseData.Write(IsFile ? 1 : 0);
+
+ 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 (FileName == null)
+ {
+ //TODO: Correct error code.
+ return -1;
+ }
+
+ if (File.Exists(FileName))
+ {
+ FileStream Stream = new FileStream(FileName, FileMode.OpenOrCreate);
+ MakeObject(Context, new IFile(Stream));
+
+ return 0;
+ }
+
+ //TODO: Correct error code.
+ return -1;
+ }
+
+ 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(DirName != null)
+ {
+ if (Directory.Exists(DirName))
+ {
+ MakeObject(Context, new IDirectory(DirName, FilterFlags));
+ return 0;
+ }
+ else
+ {
+ // TODO: correct error code.
+ return -1;
+ }
+ }
+
+ // TODO: Correct error code.
+ return -1;
+ }
+
+ public long Commit(ServiceCtx Context)
+ {
+ return 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs b/Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs
new file mode 100644
index 00000000..4eca37e8
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/FspSrv/IStorage.cs
@@ -0,0 +1,52 @@
+using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.Core.OsHle.Objects.FspSrv
+{
+ class IStorage : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs b/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs
new file mode 100644
index 00000000..d6e8947b
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Hid/IAppletResource.cs
@@ -0,0 +1,32 @@
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Hid
+{
+ class IAppletResource : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/IIpcInterface.cs b/Ryujinx.Core/OsHle/Objects/IIpcInterface.cs
new file mode 100644
index 00000000..c57a0974
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/IIpcInterface.cs
@@ -0,0 +1,10 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects
+{
+ interface IIpcInterface
+ {
+ IReadOnlyDictionary<int, ServiceProcessRequest> Commands { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/ObjHelper.cs b/Ryujinx.Core/OsHle/Objects/ObjHelper.cs
new file mode 100644
index 00000000..a151a287
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/ObjHelper.cs
@@ -0,0 +1,24 @@
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Ipc;
+
+namespace Ryujinx.Core.OsHle.Objects
+{
+ 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);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Parcel.cs b/Ryujinx.Core/OsHle/Objects/Parcel.cs
new file mode 100644
index 00000000..cd7179e1
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Parcel.cs
@@ -0,0 +1,58 @@
+using System;
+using System.IO;
+
+namespace Ryujinx.Core.OsHle.Objects.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();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs b/Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs
new file mode 100644
index 00000000..1116b849
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Time/ISteadyClock.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Time
+{
+ class ISteadyClock : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public ISteadyClock()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ //...
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs b/Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs
new file mode 100644
index 00000000..3aa70691
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Time/ISystemClock.cs
@@ -0,0 +1,42 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Time
+{
+ class ISystemClock : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs b/Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs
new file mode 100644
index 00000000..c083628b
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Time/ITimeZoneService.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Time
+{
+ class ITimeZoneService : IIpcInterface
+ {
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ public ITimeZoneService()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ //...
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs b/Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs
new file mode 100644
index 00000000..f447ca1c
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Time/SystemClockType.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Core.OsHle.Objects.Time
+{
+ enum SystemClockType
+ {
+ User,
+ Network,
+ Local
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs b/Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs
new file mode 100644
index 00000000..b3ec0e90
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Vi/IApplicationDisplayService.cs
@@ -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.Objects.Android.Parcel;
+using static Ryujinx.Core.OsHle.Objects.ObjHelper;
+
+namespace Ryujinx.Core.OsHle.Objects.Vi
+{
+ class IApplicationDisplayService : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs b/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs
new file mode 100644
index 00000000..cfd271e8
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs
@@ -0,0 +1,214 @@
+using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Ipc;
+using Ryujinx.Core.OsHle.Utilities;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+using static Ryujinx.Core.OsHle.Objects.Android.Parcel;
+
+namespace Ryujinx.Core.OsHle.Objects.Vi
+{
+ class IHOSBinderDriver : IIpcInterface
+ {
+ private delegate long ServiceProcessParcel(ServiceCtx Context, byte[] ParcelData);
+
+ private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+ private Dictionary<(string, int), ServiceProcessParcel> m_Methods;
+
+ public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+ private class BufferObj
+ {
+
+ }
+
+ private IdPoolWithObj BufferSlots;
+
+ private byte[] Gbfr;
+
+ public IHOSBinderDriver()
+ {
+ m_Commands = new Dictionary<int, ServiceProcessRequest>()
+ {
+ { 0, TransactParcel },
+ { 1, AdjustRefcount },
+ { 2, GetNativeHandle }
+ };
+
+ m_Methods = new Dictionary<(string, int), ServiceProcessParcel>()
+ {
+ { ("android.gui.IGraphicBufferProducer", 0x1), GraphicBufferProducerRequestBuffer },
+ { ("android.gui.IGraphicBufferProducer", 0x3), GraphicBufferProducerDequeueBuffer },
+ { ("android.gui.IGraphicBufferProducer", 0x7), GraphicBufferProducerQueueBuffer },
+ { ("android.gui.IGraphicBufferProducer", 0x8), GraphicBufferProducerCancelBuffer },
+ { ("android.gui.IGraphicBufferProducer", 0x9), GraphicBufferProducerQuery },
+ { ("android.gui.IGraphicBufferProducer", 0xa), GraphicBufferProducerConnect },
+ { ("android.gui.IGraphicBufferProducer", 0xe), GraphicBufferPreallocateBuffer }
+ };
+
+ BufferSlots = new IdPoolWithObj();
+ }
+
+ 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 = GetParcelData(Data);
+
+ using (MemoryStream MS = new MemoryStream(Data))
+ {
+ BinaryReader Reader = new BinaryReader(MS);
+
+ MS.Seek(4, SeekOrigin.Current);
+
+ int StrSize = Reader.ReadInt32();
+
+ string InterfaceName = Encoding.Unicode.GetString(Data, 8, StrSize * 2);
+
+ if (m_Methods.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
+ {
+ return ProcReq(Context, Data);
+ }
+ else
+ {
+ throw new NotImplementedException($"{InterfaceName} {Code}");
+ }
+ }
+ }
+
+ private long GraphicBufferProducerRequestBuffer(ServiceCtx Context, byte[] ParcelData)
+ {
+ int GbfrSize = Gbfr?.Length ?? 0;
+
+ byte[] Data = new byte[GbfrSize + 4];
+
+ if (Gbfr != null)
+ {
+ Buffer.BlockCopy(Gbfr, 0, Data, 0, GbfrSize);
+ }
+
+ return MakeReplyParcel(Context, Data);
+ }
+
+ private long GraphicBufferProducerDequeueBuffer(ServiceCtx Context, byte[] ParcelData)
+ {
+ //Note: It seems that the maximum number of slots is 64, because if we return
+ //a Slot number > 63, it seems to cause a buffer overrun and it reads garbage.
+ //Note 2: The size of each object associated with the slot is 0x30.
+ int Slot = BufferSlots.GenerateId(new BufferObj());
+
+ return MakeReplyParcel(Context, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ private long GraphicBufferProducerQueueBuffer(ServiceCtx Context, byte[] ParcelData)
+ {
+ return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
+ }
+
+ private long GraphicBufferProducerCancelBuffer(ServiceCtx Context, byte[] ParcelData)
+ {
+ using (MemoryStream MS = new MemoryStream(ParcelData))
+ {
+ BinaryReader Reader = new BinaryReader(MS);
+
+ MS.Seek(0x50, SeekOrigin.Begin);
+
+ int Slot = Reader.ReadInt32();
+
+ BufferSlots.Delete(Slot);
+
+ return MakeReplyParcel(Context, 0);
+ }
+ }
+
+ private long GraphicBufferProducerQuery(ServiceCtx Context, byte[] ParcelData)
+ {
+ return MakeReplyParcel(Context, 0, 0);
+ }
+
+ private long GraphicBufferProducerConnect(ServiceCtx Context, byte[] ParcelData)
+ {
+ return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
+ }
+
+ private long GraphicBufferPreallocateBuffer(ServiceCtx Context, byte[] ParcelData)
+ {
+ int GbfrSize = ParcelData.Length - 0x54;
+
+ Gbfr = new byte[GbfrSize];
+
+ Buffer.BlockCopy(ParcelData, 0x54, Gbfr, 0, GbfrSize);
+
+ using (MemoryStream MS = new MemoryStream(ParcelData))
+ {
+ BinaryReader Reader = new BinaryReader(MS);
+
+ MS.Seek(0xd4, SeekOrigin.Begin);
+
+ int Handle = Reader.ReadInt32();
+
+ HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
+
+ Context.Ns.Gpu.Renderer.FrameBufferPtr = NvMap.Address;
+ }
+
+ 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].Position;
+
+ byte[] Reply = MakeParcel(Data, new byte[0]);
+
+ AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply);
+
+ return 0;
+ }
+
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs b/Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs
new file mode 100644
index 00000000..f1b3cdd0
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Vi/IManagerDisplayService.cs
@@ -0,0 +1,33 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Vi
+{
+ class IManagerDisplayService : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs b/Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs
new file mode 100644
index 00000000..4c83c25f
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Objects/Vi/ISystemDisplayService.cs
@@ -0,0 +1,25 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Objects.Vi
+{
+ class ISystemDisplayService : IIpcInterface
+ {
+ 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;
+ }
+ }
+} \ No newline at end of file