From e6700b314f1384f015666767baf9ea1d8411e330 Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Thu, 14 Sep 2023 09:50:19 +0200
Subject: lbl: Migrate service to Horizon (#5628)

* lbl: Migrate service to Horizon

* Fix formatting

* Addresses gdkchan's feedback

* Fix comments
---
 .../SystemAppletProxy/ICommonStateGetter.cs        |   9 +-
 src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs |  92 ---------------
 .../HOS/Services/Lbl/LblControllerServer.cs        |  54 ---------
 src/Ryujinx.HLE/HOS/Services/ServerBase.cs         |   8 ++
 src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs          |  16 +--
 src/Ryujinx.Horizon/HorizonStatic.cs               |   2 +-
 src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs       | 130 +++++++++++++++++++++
 src/Ryujinx.Horizon/Lbl/LblIpcServer.cs            |  43 +++++++
 src/Ryujinx.Horizon/Lbl/LblMain.cs                 |  17 +++
 src/Ryujinx.Horizon/LogManager/LmIpcServer.cs      |   8 +-
 src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs          |   8 +-
 src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs        |  20 ++--
 src/Ryujinx.Horizon/Sdk/Lbl/ILblController.cs      |  20 ++++
 src/Ryujinx.Horizon/Sdk/Lbl/LblApi.cs              |  43 +++++++
 src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs          |   2 +-
 src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs                |  14 ++-
 src/Ryujinx.Horizon/ServiceTable.cs                |   6 +-
 17 files changed, 311 insertions(+), 181 deletions(-)
 delete mode 100644 src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs
 delete mode 100644 src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs
 create mode 100644 src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs
 create mode 100644 src/Ryujinx.Horizon/Lbl/LblIpcServer.cs
 create mode 100644 src/Ryujinx.Horizon/Lbl/LblMain.cs
 create mode 100644 src/Ryujinx.Horizon/Sdk/Lbl/ILblController.cs
 create mode 100644 src/Ryujinx.Horizon/Sdk/Lbl/LblApi.cs

(limited to 'src')

diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
index 0d2ec8bc..602fc2c4 100644
--- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
@@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Services.Settings.Types;
 using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService;
 using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Lbl;
 using System;
 
 namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
@@ -15,7 +16,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 
         private readonly Apm.ManagerServer _apmManagerServer;
         private readonly Apm.SystemManagerServer _apmSystemManagerServer;
-        private readonly Lbl.LblControllerServer _lblControllerServer;
 
         private bool _vrModeEnabled;
 #pragma warning disable CS0414, IDE0052 // Remove unread private member
@@ -34,7 +34,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 
             _apmManagerServer = new Apm.ManagerServer(context);
             _apmSystemManagerServer = new Apm.SystemManagerServer(context);
-            _lblControllerServer = new Lbl.LblControllerServer(context);
 
             _acquiredSleepLockEvent = new KEvent(context.Device.System.KernelContext);
         }
@@ -215,13 +214,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 
             _vrModeEnabled = vrModeEnabled;
 
+            using var lblApi = new LblApi();
+
             if (vrModeEnabled)
             {
-                _lblControllerServer.EnableVrMode();
+                lblApi.EnableVrMode().AbortOnFailure();
             }
             else
             {
-                _lblControllerServer.DisableVrMode();
+                lblApi.DisableVrMode().AbortOnFailure();
             }
 
             // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used.
diff --git a/src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs b/src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs
deleted file mode 100644
index 75d78743..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Lbl
-{
-    abstract class ILblController : IpcService
-    {
-        public ILblController(ServiceCtx context) { }
-
-        protected abstract void SetCurrentBrightnessSettingForVrMode(float currentBrightnessSettingForVrMode);
-        protected abstract float GetCurrentBrightnessSettingForVrMode();
-        internal abstract void EnableVrMode();
-        internal abstract void DisableVrMode();
-        protected abstract bool IsVrModeEnabled();
-
-        [CommandCmif(17)]
-        // SetBrightnessReflectionDelayLevel(float, float)
-        public ResultCode SetBrightnessReflectionDelayLevel(ServiceCtx context)
-        {
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(18)]
-        // GetBrightnessReflectionDelayLevel(float) -> float
-        public ResultCode GetBrightnessReflectionDelayLevel(ServiceCtx context)
-        {
-            context.ResponseData.Write(0.0f);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(21)]
-        // SetCurrentAmbientLightSensorMapping(unknown<0xC>)
-        public ResultCode SetCurrentAmbientLightSensorMapping(ServiceCtx context)
-        {
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(22)]
-        // GetCurrentAmbientLightSensorMapping() -> unknown<0xC>
-        public ResultCode GetCurrentAmbientLightSensorMapping(ServiceCtx context)
-        {
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(24)] // 3.0.0+
-        // SetCurrentBrightnessSettingForVrMode(float)
-        public ResultCode SetCurrentBrightnessSettingForVrMode(ServiceCtx context)
-        {
-            float currentBrightnessSettingForVrMode = context.RequestData.ReadSingle();
-
-            SetCurrentBrightnessSettingForVrMode(currentBrightnessSettingForVrMode);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(25)] // 3.0.0+
-        // GetCurrentBrightnessSettingForVrMode() -> float
-        public ResultCode GetCurrentBrightnessSettingForVrMode(ServiceCtx context)
-        {
-            float currentBrightnessSettingForVrMode = GetCurrentBrightnessSettingForVrMode();
-
-            context.ResponseData.Write(currentBrightnessSettingForVrMode);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(26)] // 3.0.0+
-        // EnableVrMode()
-        public ResultCode EnableVrMode(ServiceCtx context)
-        {
-            EnableVrMode();
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(27)] // 3.0.0+
-        // DisableVrMode()
-        public ResultCode DisableVrMode(ServiceCtx context)
-        {
-            DisableVrMode();
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(28)] // 3.0.0+
-        // IsVrModeEnabled() -> bool
-        public ResultCode IsVrModeEnabled(ServiceCtx context)
-        {
-            context.ResponseData.Write(IsVrModeEnabled());
-
-            return ResultCode.Success;
-        }
-    }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs b/src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs
deleted file mode 100644
index 899e882e..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Lbl
-{
-    [Service("lbl")]
-    class LblControllerServer : ILblController
-    {
-        private bool _vrModeEnabled;
-        private float _currentBrightnessSettingForVrMode;
-
-        public LblControllerServer(ServiceCtx context) : base(context) { }
-
-        protected override void SetCurrentBrightnessSettingForVrMode(float currentBrightnessSettingForVrMode)
-        {
-            if (float.IsNaN(currentBrightnessSettingForVrMode) || float.IsInfinity(currentBrightnessSettingForVrMode))
-            {
-                _currentBrightnessSettingForVrMode = 0.0f;
-
-                return;
-            }
-
-            _currentBrightnessSettingForVrMode = currentBrightnessSettingForVrMode;
-        }
-
-        protected override float GetCurrentBrightnessSettingForVrMode()
-        {
-            if (float.IsNaN(_currentBrightnessSettingForVrMode) || float.IsInfinity(_currentBrightnessSettingForVrMode))
-            {
-                return 0.0f;
-            }
-
-            return _currentBrightnessSettingForVrMode;
-        }
-
-        internal override void EnableVrMode()
-        {
-            _vrModeEnabled = true;
-
-            // NOTE: Service check _vrModeEnabled field value in a thread and then change the screen brightness.
-            //       Since we don't support that. It's fine to do nothing.
-        }
-
-        internal override void DisableVrMode()
-        {
-            _vrModeEnabled = false;
-
-            // NOTE: Service check _vrModeEnabled field value in a thread and then change the screen brightness.
-            //       Since we don't support that. It's fine to do nothing.
-        }
-
-        protected override bool IsVrModeEnabled()
-        {
-            return _vrModeEnabled;
-        }
-    }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs
index f107f502..9d7e4d4c 100644
--- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -6,6 +6,7 @@ using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Kernel.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Horizon;
 using Ryujinx.Horizon.Common;
 using System;
 using System.Buffers;
@@ -172,6 +173,13 @@ namespace Ryujinx.HLE.HOS.Services
             _selfProcess = KernelStatic.GetCurrentProcess();
             _selfThread = KernelStatic.GetCurrentThread();
 
+            HorizonStatic.Register(
+                default,
+                _context.Syscall,
+                _selfProcess.CpuMemory,
+                _selfThread.ThreadContext,
+                (int)_selfThread.ThreadContext.GetX(1));
+
             if (SmObjectFactory != null)
             {
                 _context.Syscall.ManageNamedPort(out int serverPortHandle, "sm:", 50);
diff --git a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs
index f39929dd..5d01cd9d 100644
--- a/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs
+++ b/src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs
@@ -6,8 +6,8 @@ namespace Ryujinx.Horizon.Bcat
 {
     internal class BcatIpcServer
     {
-        private const int BcatMaxSessionsCount = 8;
-        private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4;
+        private const int MaxSessionsCount = 8;
+        private const int TotalMaxSessionsCount = MaxSessionsCount * 4;
 
         private const int PointerBufferSize = 0x400;
         private const int MaxDomains = 64;
@@ -17,7 +17,7 @@ namespace Ryujinx.Horizon.Bcat
         private SmApi _sm;
         private BcatServerManager _serverManager;
 
-        private static readonly ManagerOptions _bcatManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
         internal void Initialize()
         {
@@ -26,13 +26,13 @@ namespace Ryujinx.Horizon.Bcat
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
-            _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _bcatManagerOptions, BcatTotalMaxSessionsCount);
+            _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _managerOptions, TotalMaxSessionsCount);
 
 #pragma warning disable IDE0055 // Disable formatting
-            _serverManager.RegisterServer((int)BcatPortIndex.Admin,   ServiceName.Encode("bcat:a"), BcatMaxSessionsCount);
-            _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), BcatMaxSessionsCount);
-            _serverManager.RegisterServer((int)BcatPortIndex.User,    ServiceName.Encode("bcat:u"), BcatMaxSessionsCount);
-            _serverManager.RegisterServer((int)BcatPortIndex.System,  ServiceName.Encode("bcat:s"), BcatMaxSessionsCount);
+            _serverManager.RegisterServer((int)BcatPortIndex.Admin,   ServiceName.Encode("bcat:a"), MaxSessionsCount);
+            _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), MaxSessionsCount);
+            _serverManager.RegisterServer((int)BcatPortIndex.User,    ServiceName.Encode("bcat:u"), MaxSessionsCount);
+            _serverManager.RegisterServer((int)BcatPortIndex.System,  ServiceName.Encode("bcat:s"), MaxSessionsCount);
 #pragma warning restore IDE0055
         }
 
diff --git a/src/Ryujinx.Horizon/HorizonStatic.cs b/src/Ryujinx.Horizon/HorizonStatic.cs
index 1e483cd4..3e992ead 100644
--- a/src/Ryujinx.Horizon/HorizonStatic.cs
+++ b/src/Ryujinx.Horizon/HorizonStatic.cs
@@ -4,7 +4,7 @@ using System;
 
 namespace Ryujinx.Horizon
 {
-    static class HorizonStatic
+    public static class HorizonStatic
     {
         [ThreadStatic]
         private static HorizonOptions _options;
diff --git a/src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs b/src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs
new file mode 100644
index 00000000..0a27d5ef
--- /dev/null
+++ b/src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs
@@ -0,0 +1,130 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Lbl;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Lbl.Ipc
+{
+    partial class LblController : ILblController
+    {
+        private bool _vrModeEnabled;
+        private float _currentBrightnessSettingForVrMode;
+
+        [CmifCommand(17)]
+        public Result SetBrightnessReflectionDelayLevel(float unknown0, float unknown1)
+        {
+            // NOTE: Stubbed in system module.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(18)]
+        public Result GetBrightnessReflectionDelayLevel(out float unknown1, float unknown0)
+        {
+            // NOTE: Stubbed in system module.
+
+            unknown1 = 0.0f;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(19)]
+        public Result SetCurrentBrightnessMapping(float unknown0, float unknown1, float unknown2)
+        {
+            // NOTE: Stubbed in system module.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(20)]
+        public Result GetCurrentBrightnessMapping(out float unknown0, out float unknown1, out float unknown2)
+        {
+            // NOTE: Stubbed in system module.
+
+            unknown0 = 0.0f;
+            unknown1 = 0.0f;
+            unknown2 = 0.0f;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(21)]
+        public Result SetCurrentAmbientLightSensorMapping(float unknown0, float unknown1, float unknown2)
+        {
+            // NOTE: Stubbed in system module.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(22)]
+        public Result GetCurrentAmbientLightSensorMapping(out float unknown0, out float unknown1, out float unknown2)
+        {
+            // NOTE: Stubbed in system module.
+
+            unknown0 = 0.0f;
+            unknown1 = 0.0f;
+            unknown2 = 0.0f;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(24)]
+        public Result SetCurrentBrightnessSettingForVrMode(float currentBrightnessSettingForVrMode)
+        {
+            if (float.IsNaN(currentBrightnessSettingForVrMode) || float.IsInfinity(currentBrightnessSettingForVrMode))
+            {
+                _currentBrightnessSettingForVrMode = 0.0f;
+            }
+            else
+            {
+                _currentBrightnessSettingForVrMode = currentBrightnessSettingForVrMode;
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(25)]
+        public Result GetCurrentBrightnessSettingForVrMode(out float currentBrightnessSettingForVrMode)
+        {
+            if (float.IsNaN(_currentBrightnessSettingForVrMode) || float.IsInfinity(_currentBrightnessSettingForVrMode))
+            {
+                currentBrightnessSettingForVrMode = 0.0f;
+            }
+            else
+            {
+                currentBrightnessSettingForVrMode = _currentBrightnessSettingForVrMode;
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(26)]
+        public Result EnableVrMode()
+        {
+            _vrModeEnabled = true;
+
+            // NOTE: The service checks _vrModeEnabled field value in a thread and then changes the screen brightness.
+            //       Since we don't support that, it's fine to do nothing.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(27)]
+        public Result DisableVrMode()
+        {
+            _vrModeEnabled = false;
+
+            // NOTE: The service checks _vrModeEnabled field value in a thread and then changes the screen brightness.
+            //       Since we don't support that, it's fine to do nothing.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(28)]
+        public Result IsVrModeEnabled(out bool vrModeEnabled)
+        {
+            vrModeEnabled = _vrModeEnabled;
+
+            return Result.Success;
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Lbl/LblIpcServer.cs b/src/Ryujinx.Horizon/Lbl/LblIpcServer.cs
new file mode 100644
index 00000000..53e74d51
--- /dev/null
+++ b/src/Ryujinx.Horizon/Lbl/LblIpcServer.cs
@@ -0,0 +1,43 @@
+using Ryujinx.Horizon.Lbl.Ipc;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.Sdk.Sm;
+
+namespace Ryujinx.Horizon.Lbl
+{
+    class LblIpcServer
+    {
+        private const int MaxSessionsCount = 5;
+
+        private const int PointerBufferSize = 0;
+        private const int MaxDomains = 0;
+        private const int MaxDomainObjects = 0;
+        private const int MaxPortsCount = 1;
+
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+
+        private SmApi _sm;
+        private ServerManager _serverManager;
+
+        public void Initialize()
+        {
+            HeapAllocator allocator = new();
+
+            _sm = new SmApi();
+            _sm.Initialize().AbortOnFailure();
+
+            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _managerOptions, MaxSessionsCount);
+
+            _serverManager.RegisterObjectForServer(new LblController(), ServiceName.Encode("lbl"), MaxSessionsCount);
+        }
+
+        public void ServiceRequests()
+        {
+            _serverManager.ServiceRequests();
+        }
+
+        public void Shutdown()
+        {
+            _serverManager.Dispose();
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Lbl/LblMain.cs b/src/Ryujinx.Horizon/Lbl/LblMain.cs
new file mode 100644
index 00000000..f471f31b
--- /dev/null
+++ b/src/Ryujinx.Horizon/Lbl/LblMain.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.Horizon.Lbl
+{
+    class LblMain : IService
+    {
+        public static void Main(ServiceTable serviceTable)
+        {
+            LblIpcServer ipcServer = new();
+
+            ipcServer.Initialize();
+
+            serviceTable.SignalServiceReady();
+
+            ipcServer.ServiceRequests();
+            ipcServer.Shutdown();
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs
index d1a405b8..6bdc3c42 100644
--- a/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs
+++ b/src/Ryujinx.Horizon/LogManager/LmIpcServer.cs
@@ -6,14 +6,14 @@ namespace Ryujinx.Horizon.LogManager
 {
     class LmIpcServer
     {
-        private const int LogMaxSessionsCount = 42;
+        private const int MaxSessionsCount = 42;
 
         private const int PointerBufferSize = 0x400;
         private const int MaxDomains = 31;
         private const int MaxDomainObjects = 61;
         private const int MaxPortsCount = 1;
 
-        private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
         private SmApi _sm;
         private ServerManager _serverManager;
@@ -25,9 +25,9 @@ namespace Ryujinx.Horizon.LogManager
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
-            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount);
+            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _managerOptions, MaxSessionsCount);
 
-            _serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), LogMaxSessionsCount);
+            _serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), MaxSessionsCount);
         }
 
         public void ServiceRequests()
diff --git a/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs b/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs
index e60b2558..b6615d2c 100644
--- a/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs
+++ b/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs
@@ -6,14 +6,14 @@ namespace Ryujinx.Horizon.MmNv
 {
     class MmNvIpcServer
     {
-        private const int MmNvMaxSessionsCount = 9;
+        private const int MaxSessionsCount = 40;
 
         private const int PointerBufferSize = 0;
         private const int MaxDomains = 0;
         private const int MaxDomainObjects = 0;
         private const int MaxPortsCount = 1;
 
-        private static readonly ManagerOptions _mmNvOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
         private SmApi _sm;
         private ServerManager _serverManager;
@@ -25,9 +25,9 @@ namespace Ryujinx.Horizon.MmNv
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
-            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _mmNvOptions, MmNvMaxSessionsCount);
+            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _managerOptions, MaxSessionsCount);
 
-            _serverManager.RegisterObjectForServer(new Request(), ServiceName.Encode("mm:u"), MmNvMaxSessionsCount);
+            _serverManager.RegisterObjectForServer(new Request(), ServiceName.Encode("mm:u"), MaxSessionsCount);
         }
 
         public void ServiceRequests()
diff --git a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs
index 9c185520..410f997e 100644
--- a/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs
+++ b/src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs
@@ -6,15 +6,15 @@ namespace Ryujinx.Horizon.Prepo
 {
     class PrepoIpcServer
     {
-        private const int PrepoMaxSessionsCount = 12;
-        private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6;
+        private const int MaxSessionsCount = 12;
+        private const int TotalMaxSessionsCount = MaxSessionsCount * 6;
 
         private const int PointerBufferSize = 0x80;
         private const int MaxDomains = 64;
         private const int MaxDomainObjects = 16;
         private const int MaxPortsCount = 6;
 
-        private static readonly ManagerOptions _prepoManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
         private SmApi _sm;
         private PrepoServerManager _serverManager;
@@ -26,15 +26,15 @@ namespace Ryujinx.Horizon.Prepo
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
-            _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _prepoManagerOptions, PrepoTotalMaxSessionsCount);
+            _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _managerOptions, TotalMaxSessionsCount);
 
 #pragma warning disable IDE0055 // Disable formatting
-            _serverManager.RegisterServer((int)PrepoPortIndex.Admin,   ServiceName.Encode("prepo:a"),  PrepoMaxSessionsCount); // 1.0.0-5.1.0
-            _serverManager.RegisterServer((int)PrepoPortIndex.Admin2,  ServiceName.Encode("prepo:a2"), PrepoMaxSessionsCount); // 6.0.0+
-            _serverManager.RegisterServer((int)PrepoPortIndex.Manager, ServiceName.Encode("prepo:m"),  PrepoMaxSessionsCount);
-            _serverManager.RegisterServer((int)PrepoPortIndex.User,    ServiceName.Encode("prepo:u"),  PrepoMaxSessionsCount);
-            _serverManager.RegisterServer((int)PrepoPortIndex.System,  ServiceName.Encode("prepo:s"),  PrepoMaxSessionsCount);
-            _serverManager.RegisterServer((int)PrepoPortIndex.Debug,   ServiceName.Encode("prepo:d"),  PrepoMaxSessionsCount); // 1.0.0
+            _serverManager.RegisterServer((int)PrepoPortIndex.Admin,   ServiceName.Encode("prepo:a"),  MaxSessionsCount); // 1.0.0-5.1.0
+            _serverManager.RegisterServer((int)PrepoPortIndex.Admin2,  ServiceName.Encode("prepo:a2"), MaxSessionsCount); // 6.0.0+
+            _serverManager.RegisterServer((int)PrepoPortIndex.Manager, ServiceName.Encode("prepo:m"),  MaxSessionsCount);
+            _serverManager.RegisterServer((int)PrepoPortIndex.User,    ServiceName.Encode("prepo:u"),  MaxSessionsCount);
+            _serverManager.RegisterServer((int)PrepoPortIndex.System,  ServiceName.Encode("prepo:s"),  MaxSessionsCount);
+            _serverManager.RegisterServer((int)PrepoPortIndex.Debug,   ServiceName.Encode("prepo:d"),  MaxSessionsCount); // 1.0.0
 #pragma warning restore IDE0055
         }
 
diff --git a/src/Ryujinx.Horizon/Sdk/Lbl/ILblController.cs b/src/Ryujinx.Horizon/Sdk/Lbl/ILblController.cs
new file mode 100644
index 00000000..594722e9
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Lbl/ILblController.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Sdk.Lbl
+{
+    interface ILblController : IServiceObject
+    {
+        Result SetBrightnessReflectionDelayLevel(float unknown0, float unknown1);
+        Result GetBrightnessReflectionDelayLevel(out float unknown1, float unknown0);
+        Result SetCurrentBrightnessMapping(float unknown0, float unknown1, float unknown2);
+        Result GetCurrentBrightnessMapping(out float unknown0, out float unknown1, out float unknown2);
+        Result SetCurrentAmbientLightSensorMapping(float unknown0, float unknown1, float unknown2);
+        Result GetCurrentAmbientLightSensorMapping(out float unknown0, out float unknown1, out float unknown2);
+        Result SetCurrentBrightnessSettingForVrMode(float currentBrightnessSettingForVrMode);
+        Result GetCurrentBrightnessSettingForVrMode(out float currentBrightnessSettingForVrMode);
+        Result EnableVrMode();
+        Result DisableVrMode();
+        Result IsVrModeEnabled(out bool vrModeEnabled);
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/Lbl/LblApi.cs b/src/Ryujinx.Horizon/Sdk/Lbl/LblApi.cs
new file mode 100644
index 00000000..843a9acd
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Lbl/LblApi.cs
@@ -0,0 +1,43 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sm;
+using System;
+
+namespace Ryujinx.Horizon.Sdk.Lbl
+{
+    public class LblApi : IDisposable
+    {
+        private const string LblName = "lbl";
+
+        private int _sessionHandle;
+
+        public LblApi()
+        {
+            using var smApi = new SmApi();
+
+            smApi.Initialize();
+            smApi.GetServiceHandle(out _sessionHandle, ServiceName.Encode(LblName)).AbortOnFailure();
+        }
+
+        public Result EnableVrMode()
+        {
+            return ServiceUtil.SendRequest(out _, _sessionHandle, 26, sendPid: false, ReadOnlySpan<byte>.Empty);
+        }
+
+        public Result DisableVrMode()
+        {
+            return ServiceUtil.SendRequest(out _, _sessionHandle, 27, sendPid: false, ReadOnlySpan<byte>.Empty);
+        }
+
+        public void Dispose()
+        {
+            if (_sessionHandle != 0)
+            {
+                HorizonStatic.Syscall.CloseHandle(_sessionHandle);
+
+                _sessionHandle = 0;
+            }
+
+            GC.SuppressFinalize(this);
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs b/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs
index f90d39c2..c89f118c 100644
--- a/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs
+++ b/src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
 namespace Ryujinx.Horizon.Sdk.Sm
 {
     [StructLayout(LayoutKind.Sequential, Pack = 1)]
-    readonly struct ServiceName
+    public readonly struct ServiceName
     {
         public static ServiceName Invalid { get; } = new(0);
 
diff --git a/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs b/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs
index 3e5635bf..1ab400bd 100644
--- a/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs
+++ b/src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs
@@ -5,7 +5,7 @@ using System;
 
 namespace Ryujinx.Horizon.Sdk.Sm
 {
-    class SmApi
+    public class SmApi : IDisposable
     {
         private const string SmName = "sm:";
 
@@ -109,5 +109,17 @@ namespace Ryujinx.Horizon.Sdk.Sm
 
             return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data);
         }
+
+        public void Dispose()
+        {
+            if (_portHandle != 0)
+            {
+                HorizonStatic.Syscall.CloseHandle(_portHandle);
+
+                _portHandle = 0;
+            }
+
+            GC.SuppressFinalize(this);
+        }
     }
 }
diff --git a/src/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs
index 8dfacbeb..cd443329 100644
--- a/src/Ryujinx.Horizon/ServiceTable.cs
+++ b/src/Ryujinx.Horizon/ServiceTable.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Horizon.Bcat;
+using Ryujinx.Horizon.Lbl;
 using Ryujinx.Horizon.LogManager;
 using Ryujinx.Horizon.MmNv;
 using Ryujinx.Horizon.Prepo;
@@ -23,10 +24,11 @@ namespace Ryujinx.Horizon
                 entries.Add(new ServiceEntry(T.Main, this, options));
             }
 
-            RegisterService<LmMain>();
-            RegisterService<PrepoMain>();
             RegisterService<BcatMain>();
+            RegisterService<LblMain>();
+            RegisterService<LmMain>();
             RegisterService<MmNvMain>();
+            RegisterService<PrepoMain>();
 
             _totalServices = entries.Count;
 
-- 
cgit v1.2.3-70-g09d2