From 550747eac6c0f6da14070c8b6d208bde6f1d1eb9 Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Sun, 8 Jan 2023 13:13:39 +0100
Subject: [PATCH] Horizon: Impl Prepo, Fixes bugs, Clean things (#4220)

* Horizon: Impl Prepo, Fixes bugs, Clean things

* remove ToArray()

* resultCode > status

* Remove old services

* Addresses gdkchan's comments and more cleanup

* Addresses Gdkchan's feedback 2

* Reorganize services, make sure service are loaded before guest

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* Create interfaces for lm and sm

Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
---
 Ryujinx.HLE/HOS/Horizon.cs                    |   3 +
 Ryujinx.HLE/HOS/ProgramLoader.cs              |   2 +
 Ryujinx.HLE/HOS/Services/Lm/ILogService.cs    |  19 --
 .../HOS/Services/Lm/LogService/ILogger.cs     | 109 ---------
 .../Lm/LogService/Types/LmLogField.cs         |  18 --
 .../Lm/LogService/Types/LmLogLevel.cs         |  11 -
 .../HOS/Services/Prepo/IPrepoService.cs       | 182 ---------------
 Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs  |  15 --
 Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs |   2 +-
 .../Hipc/HipcGenerator.cs                     |  57 +++--
 Ryujinx.Horizon/HeapAllocator.cs              |  12 +-
 Ryujinx.Horizon/HorizonOptions.cs             |   6 +-
 Ryujinx.Horizon/HorizonStatic.cs              |  26 +--
 Ryujinx.Horizon/IService.cs                   |   2 +-
 .../LogManager/{ => Ipc}/LmLogger.cs          |  37 ++-
 .../{LmLog.cs => Ipc/LogService.cs}           |  11 +-
 Ryujinx.Horizon/LogManager/LmIpcServer.cs     |  31 +--
 Ryujinx.Horizon/LogManager/LmMain.cs          |   9 +-
 Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs     | 218 ++++++++++++++++++
 Ryujinx.Horizon/Prepo/PrepoIpcServer.cs       |  49 ++++
 Ryujinx.Horizon/Prepo/PrepoMain.cs            |  17 ++
 Ryujinx.Horizon/Prepo/PrepoResult.cs          |  15 ++
 Ryujinx.Horizon/Prepo/PrepoServerManager.cs   |  30 +++
 Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs |  12 +
 .../Types/PrepoServicePermissionLevel.cs      |   5 +-
 Ryujinx.Horizon/Ryujinx.Horizon.csproj        |   4 +
 Ryujinx.Horizon/Sdk/Account/Uid.cs            |  62 +++++
 Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs           |  12 +
 Ryujinx.Horizon/Sdk/Lm/ILogService.cs         |  11 +
 Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs    |  20 ++
 Ryujinx.Horizon/Sdk/ServiceUtil.cs            |   9 +-
 .../Sdk/Sf/Cmif/CmifDomainInHeader.cs         |  10 +-
 .../Sdk/Sf/Cmif/CmifDomainRequestType.cs      |   4 +-
 Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs    |  81 ++++---
 Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs  |   6 +-
 Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs    |   8 +-
 .../Sdk/Sf/Cmif/CmifRequestFormat.cs          |  26 +--
 Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs   |   4 +-
 Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs    |  12 +-
 .../Cmif/DomainServiceObjectDispatchTable.cs  |   4 +-
 .../Sf/Cmif/DomainServiceObjectProcessor.cs   |  10 +-
 Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs |   4 +-
 .../Sf/Cmif/ServerMessageRuntimeMetadata.cs   |  33 +--
 .../Sdk/Sf/Cmif/ServiceDispatchContext.cs     |  18 +-
 .../Sdk/Sf/Cmif/ServiceDispatchTable.cs       |   4 +-
 .../Sdk/Sf/Cmif/ServiceDispatchTableBase.cs   |  16 +-
 Ryujinx.Horizon/Sdk/Sf/CommandArg.cs          |  36 +--
 .../Sdk/Sf/CommandArgAttributes.cs            |   8 +-
 Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs      |  22 +-
 .../Sdk/Sf/CommandSerialization.cs            |   3 +-
 .../Sdk/Sf/Hipc/HipcBufferFlags.cs            |  12 +-
 Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs |   4 +-
 Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs    |   4 +-
 Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs    | 122 +++++-----
 .../Sdk/Sf/Hipc/HipcMessageData.cs            |   8 +-
 Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs   |  18 +-
 .../Sdk/Sf/Hipc/HipcReceiveListEntry.cs       |   2 +-
 Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs     |  21 +-
 .../Sdk/Sf/Hipc/HipcStaticDescriptor.cs       |   8 +-
 Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs |  16 +-
 Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs         |  20 +-
 .../Sdk/Sf/Hipc/ServerDomainSessionManager.cs |   2 +-
 Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs  |   4 +-
 .../Sdk/Sf/Hipc/ServerManagerBase.cs          |  33 +--
 Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs  |  16 +-
 .../Sdk/Sf/Hipc/ServerSessionManager.cs       |  41 ++--
 .../Sdk/Sf/HipcCommandProcessor.cs            |  38 +--
 .../Sdk/Sf/RawDataOffsetCalculator.cs         |  18 +-
 Ryujinx.Horizon/Sdk/Sf/SfResult.cs            |  36 ++-
 Ryujinx.Horizon/Sdk/Sm/IManagerService.cs     |   8 +
 Ryujinx.Horizon/Sdk/Sm/IUserService.cs        |  13 ++
 Ryujinx.Horizon/Sdk/Sm/SmApi.cs               |  22 +-
 Ryujinx.Horizon/ServiceEntry.cs               |  16 +-
 Ryujinx.Horizon/ServiceTable.cs               |  46 +++-
 Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs        |  10 +-
 Ryujinx.Horizon/Sm/Impl/ServiceManager.cs     |   9 +-
 Ryujinx.Horizon/Sm/Ipc/ManagerService.cs      |   8 +
 Ryujinx.Horizon/Sm/{ => Ipc}/UserService.cs   |  10 +-
 Ryujinx.Horizon/Sm/ManagerService.cs          |   8 -
 Ryujinx.Horizon/Sm/SmMain.cs                  |  30 +--
 Ryujinx.Horizon/Sm/SmResult.cs                |  20 +-
 Ryujinx.Horizon/Sm/SmServerManager.cs         |  30 +++
 Ryujinx.Horizon/Sm/Types/SmPortIndex.cs       |   8 +
 83 files changed, 1106 insertions(+), 880 deletions(-)
 delete mode 100644 Ryujinx.HLE/HOS/Services/Lm/ILogService.cs
 delete mode 100644 Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
 delete mode 100644 Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs
 delete mode 100644 Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs
 delete mode 100644 Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs
 delete mode 100644 Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs
 rename Ryujinx.Horizon/LogManager/{ => Ipc}/LmLogger.cs (81%)
 rename Ryujinx.Horizon/LogManager/{LmLog.cs => Ipc/LogService.cs} (55%)
 create mode 100644 Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs
 create mode 100644 Ryujinx.Horizon/Prepo/PrepoIpcServer.cs
 create mode 100644 Ryujinx.Horizon/Prepo/PrepoMain.cs
 create mode 100644 Ryujinx.Horizon/Prepo/PrepoResult.cs
 create mode 100644 Ryujinx.Horizon/Prepo/PrepoServerManager.cs
 create mode 100644 Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs
 rename {Ryujinx.HLE/HOS/Services => Ryujinx.Horizon}/Prepo/Types/PrepoServicePermissionLevel.cs (52%)
 create mode 100644 Ryujinx.Horizon/Sdk/Account/Uid.cs
 create mode 100644 Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs
 create mode 100644 Ryujinx.Horizon/Sdk/Lm/ILogService.cs
 create mode 100644 Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs
 create mode 100644 Ryujinx.Horizon/Sdk/Sm/IManagerService.cs
 create mode 100644 Ryujinx.Horizon/Sdk/Sm/IUserService.cs
 create mode 100644 Ryujinx.Horizon/Sm/Ipc/ManagerService.cs
 rename Ryujinx.Horizon/Sm/{ => Ipc}/UserService.cs (91%)
 delete mode 100644 Ryujinx.Horizon/Sm/ManagerService.cs
 create mode 100644 Ryujinx.Horizon/Sm/SmServerManager.cs
 create mode 100644 Ryujinx.Horizon/Sm/Types/SmPortIndex.cs

diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index a8b5be33d8..ca3f8103e8 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -123,6 +123,8 @@ namespace Ryujinx.HLE.HOS
 
         internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
 
+        internal ServiceTable ServiceTable { get; private set; }
+
         public bool IsPaused { get; private set; }
 
         public Horizon(Switch device)
@@ -326,6 +328,7 @@ namespace Ryujinx.HLE.HOS
 
         private void StartNewServices()
         {
+            ServiceTable = new ServiceTable();
             var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices));
 
             foreach (var service in services)
diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs
index 09e1ac31c4..b6a39a20a4 100644
--- a/Ryujinx.HLE/HOS/ProgramLoader.cs
+++ b/Ryujinx.HLE/HOS/ProgramLoader.cs
@@ -182,6 +182,8 @@ namespace Ryujinx.HLE.HOS
             byte[] arguments = null,
             params IExecutable[] executables)
         {
+            context.Device.System.ServiceTable.WaitServicesReady();
+
             LibHac.Result rc = metaData.GetNpdm(out var npdm);
 
             if (rc.IsFailure())
diff --git a/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs b/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs
deleted file mode 100644
index 0976431b2a..0000000000
--- a/Ryujinx.HLE/HOS/Services/Lm/ILogService.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Ryujinx.HLE.HOS.Services.Lm.LogService;
-
-namespace Ryujinx.HLE.HOS.Services.Lm
-{
-    [Service("lm")]
-    class ILogService : IpcService
-    {
-        public ILogService(ServiceCtx context) { }
-
-        [CommandHipc(0)]
-        // Initialize(u64, pid) -> object<nn::lm::ILogger>
-        public ResultCode Initialize(ServiceCtx context)
-        {
-            MakeObject(context, new ILogger());
-
-            return ResultCode.Success;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
deleted file mode 100644
index 3181668f77..0000000000
--- a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using Ryujinx.Common.Logging;
-using System.IO;
-using System.Text;
-
-namespace Ryujinx.HLE.HOS.Services.Lm.LogService
-{
-    class ILogger : IpcService
-    {
-        public ILogger() { }
-
-        [CommandHipc(0)]
-        // Log(buffer<unknown, 0x21>)
-        public ResultCode Log(ServiceCtx context)
-        {
-            Logger.Guest?.Print(LogClass.ServiceLm, LogImpl(context));
-
-            return ResultCode.Success;
-        }
-
-        private string LogImpl(ServiceCtx context)
-        {
-            (ulong bufPos, ulong bufSize) = context.Request.GetBufferType0x21();
-
-            byte[] logBuffer = new byte[bufSize];
-
-            context.Memory.Read(bufPos, logBuffer);
-
-            using MemoryStream ms = new MemoryStream(logBuffer);
-
-            BinaryReader reader = new BinaryReader(ms);
-
-            long pid = reader.ReadInt64();
-            long threadContext = reader.ReadInt64();
-            short flags = reader.ReadInt16();
-            byte level = reader.ReadByte();
-            byte verbosity = reader.ReadByte();
-            int payloadLength = reader.ReadInt32();
-
-            StringBuilder sb = new StringBuilder();
-
-            sb.AppendLine($"Guest Log:\n  Log level: {(LmLogLevel)level}");
-
-            while (ms.Position < ms.Length)
-            {
-                int type = ReadEncodedInt(reader);
-                int size = ReadEncodedInt(reader);
-
-                LmLogField field = (LmLogField)type;
-
-                string fieldStr = string.Empty;
-
-                if (field == LmLogField.Start)
-                {
-                    reader.ReadBytes(size);
-
-                    continue;
-                }
-                else if (field == LmLogField.Stop)
-                {
-                    break;
-                }
-                else if (field == LmLogField.Line)
-                {
-                    fieldStr = $"{field}: {reader.ReadInt32()}";
-                }
-                else if (field == LmLogField.DropCount)
-                {
-                    fieldStr = $"{field}: {reader.ReadInt64()}";
-                }
-                else if (field == LmLogField.Time)
-                {
-                    fieldStr = $"{field}: {reader.ReadInt64()}s";
-                }
-                else if (field < LmLogField.Count)
-                {
-                    fieldStr = $"{field}: '{Encoding.UTF8.GetString(reader.ReadBytes(size)).TrimEnd()}'";
-                }
-                else
-                {
-                    fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.ReadBytes(size)).TrimEnd()}'";
-                }
-
-                sb.AppendLine($"    {fieldStr}");
-            }
-
-            return sb.ToString();
-        }
-
-        private static int ReadEncodedInt(BinaryReader reader)
-        {
-            int result = 0;
-            int position = 0;
-
-            byte encoded;
-
-            do
-            {
-                encoded = reader.ReadByte();
-
-                result += (encoded & 0x7F) << (7 * position);
-
-                position++;
-
-            } while ((encoded & 0x80) != 0);
-
-            return result;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs
deleted file mode 100644
index 3f93e16761..0000000000
--- a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogField.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Lm.LogService
-{
-    enum LmLogField
-    {
-        Start       = 0,
-        Stop        = 1,
-        Message     = 2,
-        Line        = 3,
-        Filename    = 4,
-        Function    = 5,
-        Module      = 6,
-        Thread      = 7,
-        DropCount   = 8,
-        Time        = 9,
-        ProgramName = 10,
-        Count
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs
deleted file mode 100644
index ee1a8396ab..0000000000
--- a/Ryujinx.HLE/HOS/Services/Lm/LogService/Types/LmLogLevel.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Lm.LogService
-{
-    enum LmLogLevel
-    {
-        Trace,
-        Info,
-        Warning,
-        Error,
-        Critical
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs
deleted file mode 100644
index 019626f1b5..0000000000
--- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-using MsgPack;
-using MsgPack.Serialization;
-using Ryujinx.Common;
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Utilities;
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-using Ryujinx.HLE.Utilities;
-using System;
-using System.Text;
-
-namespace Ryujinx.HLE.HOS.Services.Prepo
-{
-    [Service("prepo:a",  PrepoServicePermissionLevel.Admin)] // 1.0.0-5.1.0
-    [Service("prepo:a2", PrepoServicePermissionLevel.Admin)] // 6.0.0+
-    [Service("prepo:m",  PrepoServicePermissionLevel.Manager)]
-    [Service("prepo:u",  PrepoServicePermissionLevel.User)]
-    [Service("prepo:s",  PrepoServicePermissionLevel.System)]
-    class IPrepoService : IpcService
-    {
-        private PrepoServicePermissionLevel _permission;
-        private ulong _systemSessionId;
-
-        public IPrepoService(ServiceCtx context, PrepoServicePermissionLevel permission)
-        {
-            _permission = permission;
-        }
-
-        [CommandHipc(10100)] // 1.0.0-5.1.0
-        [CommandHipc(10102)] // 6.0.0-9.2.0
-        [CommandHipc(10104)] // 10.0.0+
-        // SaveReport(u64, pid, buffer<u8, 9>, buffer<bytes, 5>)
-        public ResultCode SaveReport(ServiceCtx context)
-        {
-            if ((_permission & PrepoServicePermissionLevel.User) == 0)
-            {
-                return ResultCode.PermissionDenied;
-            }
-
-            // We don't care about the differences since we don't use the play report.
-            return ProcessReport(context, withUserID: false);
-        }
-
-        [CommandHipc(10101)] // 1.0.0-5.1.0
-        [CommandHipc(10103)] // 6.0.0-9.2.0
-        [CommandHipc(10105)] // 10.0.0+
-        // SaveReportWithUser(nn::account::Uid, u64, pid, buffer<u8, 9>, buffer<bytes, 5>)
-        public ResultCode SaveReportWithUser(ServiceCtx context)
-        {
-            if ((_permission & PrepoServicePermissionLevel.User) == 0)
-            {
-                return ResultCode.PermissionDenied;
-            }
-
-            // We don't care about the differences since we don't use the play report.
-            return ProcessReport(context, withUserID: true);
-        }
-
-        [CommandHipc(10200)]
-        // RequestImmediateTransmission()
-        public ResultCode RequestImmediateTransmission(ServiceCtx context)
-        {
-            // It signals an event of nn::prepo::detail::service::core::TransmissionStatusManager that requests the transmission of the report.
-            // Since we don't use reports it's fine to do nothing.
-
-            return ResultCode.Success;
-        }
-
-        [CommandHipc(10300)]
-        // GetTransmissionStatus() -> u32
-        public ResultCode GetTransmissionStatus(ServiceCtx context)
-        {
-            // It returns the transmission result of nn::prepo::detail::service::core::TransmissionStatusManager.
-            // Since we don't use reports it's fine to return ResultCode.Success.
-            context.ResponseData.Write((int)ResultCode.Success);
-
-            return ResultCode.Success;
-        }
-
-        [CommandHipc(10400)] // 9.0.0+
-        // GetSystemSessionId() -> u64
-        public ResultCode GetSystemSessionId(ServiceCtx context)
-        {
-            if ((_permission & PrepoServicePermissionLevel.User) == 0)
-            {
-                return ResultCode.PermissionDenied;
-            }
-
-            if (_systemSessionId == 0)
-            {
-                byte[] randomBuffer = new byte[8];
-
-                Random.Shared.NextBytes(randomBuffer);
-
-                _systemSessionId = BitConverter.ToUInt64(randomBuffer, 0);
-            }
-
-            context.ResponseData.Write(_systemSessionId);
-
-            return ResultCode.Success;
-        }
-
-        [CommandHipc(20100)]
-        // SaveSystemReport(u64, pid, buffer<u8, 9>, buffer<bytes, 5>)
-        public ResultCode SaveSystemReport(ServiceCtx context)
-        {
-            if ((_permission & PrepoServicePermissionLevel.System) != 0)
-            {
-                return ResultCode.PermissionDenied;
-            }
-
-            // We don't care about the differences since we don't use the play report.
-            return ProcessReport(context, withUserID: false);
-        }
-
-        [CommandHipc(20101)]
-        // SaveSystemReportWithUser(nn::account::Uid, u64, pid, buffer<u8, 9>, buffer<bytes, 5>)
-        public ResultCode SaveSystemReportWithUser(ServiceCtx context)
-        {
-            if ((_permission & PrepoServicePermissionLevel.System) != 0)
-            {
-                return ResultCode.PermissionDenied;
-            }
-
-            // We don't care about the differences since we don't use the play report.
-            return ProcessReport(context, withUserID: true);
-        }
-
-        private ResultCode ProcessReport(ServiceCtx context, bool withUserID)
-        {
-            UserId userId   = withUserID ? context.RequestData.ReadStruct<UserId>() : new UserId();
-            string gameRoom = StringUtils.ReadUtf8String(context);
-
-            if (withUserID)
-            {
-                if (userId.IsNull)
-                {
-                    return ResultCode.InvalidArgument;
-                }
-            }
-
-            if (gameRoom == string.Empty)
-            {
-                return ResultCode.InvalidState;
-            }
-
-            ulong inputPosition = context.Request.SendBuff[0].Position;
-            ulong inputSize     = context.Request.SendBuff[0].Size;
-
-            if (inputSize == 0)
-            {
-                return ResultCode.InvalidBufferSize;
-            }
-
-            byte[] inputBuffer = new byte[inputSize];
-
-            context.Memory.Read(inputPosition, inputBuffer);
-
-            Logger.Info?.Print(LogClass.ServicePrepo, ReadReportBuffer(inputBuffer, gameRoom, userId));
-
-            return ResultCode.Success;
-        }
-
-        private string ReadReportBuffer(byte[] buffer, string room, UserId userId)
-        {
-            StringBuilder     builder            = new StringBuilder();
-            MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(buffer);
-
-            builder.AppendLine();
-            builder.AppendLine("PlayReport log:");
-
-            if (!userId.IsNull)
-            {
-                builder.AppendLine($" UserId: {userId}");
-            }
-
-            builder.AppendLine($" Room: {room}");
-            builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
-
-            return builder.ToString();
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs
deleted file mode 100644
index 3199e27055..0000000000
--- a/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Prepo
-{
-    enum ResultCode
-    {
-        ModuleId       = 129,
-        ErrorCodeShift = 9,
-
-        Success = 0,
-
-        InvalidArgument   = (1  << ErrorCodeShift) | ModuleId,
-        InvalidState      = (5  << ErrorCodeShift) | ModuleId,
-        InvalidBufferSize = (9  << ErrorCodeShift) | ModuleId,
-        PermissionDenied  = (90 << ErrorCodeShift) | ModuleId
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
index 86031a7073..73652da4b4 100644
--- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
@@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
                 return ResultCode.InvalidName;
             }
 
-            Logger.Info?.Print(LogClass.ServiceSm, $"Register \"{name}\".");
+            Logger.Debug?.Print(LogClass.ServiceSm, $"Register \"{name}\".");
 
             KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, null);
 
diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs
index a66d57a3b1..332e04d174 100644
--- a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs
+++ b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs
@@ -123,44 +123,51 @@ namespace Ryujinx.Horizon.Generators.Hipc
                 {
                     string[] args = new string[method.ParameterList.Parameters.Count];
 
-                    int index = 0;
-
-                    foreach (var parameter in method.ParameterList.Parameters)
+                    if (args.Length == 0)
                     {
-                        string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type);
-                        CommandArgType argType = GetCommandArgType(compilation, parameter);
+                        generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, Array.Empty<CommandArg>()) }},");
+                    }
+                    else
+                    {
+                        int index = 0;
 
-                        string arg;
-
-                        if (argType == CommandArgType.Buffer)
+                        foreach (var parameter in method.ParameterList.Parameters)
                         {
-                            string bufferFlags = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 0);
-                            string bufferFixedSize = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 1);
+                            string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type);
+                            CommandArgType argType = GetCommandArgType(compilation, parameter);
 
-                            if (bufferFixedSize != null)
+                            string arg;
+
+                            if (argType == CommandArgType.Buffer)
                             {
-                                arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})";
+                                string bufferFlags = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 0);
+                                string bufferFixedSize = GetFirstAttributeAgument(compilation, parameter, TypeBufferAttribute, 1);
+
+                                if (bufferFixedSize != null)
+                                {
+                                    arg = $"new CommandArg({bufferFlags}, {bufferFixedSize})";
+                                }
+                                else
+                                {
+                                    arg = $"new CommandArg({bufferFlags})";
+                                }
+                            }
+                            else if (argType == CommandArgType.InArgument || argType == CommandArgType.OutArgument)
+                            {
+                                string alignment = GetTypeAlignmentExpression(compilation, parameter.Type);
+
+                                arg = $"new CommandArg(CommandArgType.{argType}, Unsafe.SizeOf<{canonicalTypeName}>(), {alignment})";
                             }
                             else
                             {
-                                arg = $"new CommandArg({bufferFlags})";
+                                arg = $"new CommandArg(CommandArgType.{argType})";
                             }
-                        }
-                        else if (argType == CommandArgType.InArgument || argType == CommandArgType.OutArgument)
-                        {
-                            string alignment = GetTypeAlignmentExpression(compilation, parameter.Type);
 
-                            arg = $"new CommandArg(CommandArgType.{argType}, Unsafe.SizeOf<{canonicalTypeName}>(), {alignment})";
-                        }
-                        else
-                        {
-                            arg = $"new CommandArg(CommandArgType.{argType})";
+                            args[index++] = arg;
                         }
 
-                        args[index++] = arg;
+                        generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, {string.Join(", ", args)}) }},");
                     }
-
-                    generator.AppendLine($"{{ {commandId}, new CommandHandler({method.Identifier.Text}, {string.Join(", ", args)}) }},");
                 }
             }
 
diff --git a/Ryujinx.Horizon/HeapAllocator.cs b/Ryujinx.Horizon/HeapAllocator.cs
index 867c96770e..40ff14d095 100644
--- a/Ryujinx.Horizon/HeapAllocator.cs
+++ b/Ryujinx.Horizon/HeapAllocator.cs
@@ -12,12 +12,12 @@ namespace Ryujinx.Horizon
         private struct Range : IComparable<Range>
         {
             public ulong Offset { get; }
-            public ulong Size { get; }
+            public ulong Size   { get; }
 
             public Range(ulong offset, ulong size)
             {
                 Offset = offset;
-                Size = size;
+                Size   = size;
             }
 
             public int CompareTo(Range other)
@@ -31,7 +31,7 @@ namespace Ryujinx.Horizon
 
         public HeapAllocator()
         {
-            _freeRanges = new List<Range>();
+            _freeRanges      = new List<Range>();
             _currentHeapSize = 0;
         }
 
@@ -70,8 +70,8 @@ namespace Ryujinx.Horizon
                 var range = _freeRanges[i];
 
                 ulong alignedOffset = BitUtils.AlignUp(range.Offset, alignment);
-                ulong sizeDelta = alignedOffset - range.Offset;
-                ulong usableSize = range.Size - sizeDelta;
+                ulong sizeDelta     = alignedOffset - range.Offset;
+                ulong usableSize    = range.Size - sizeDelta;
 
                 if (sizeDelta < range.Size && usableSize >= size)
                 {
@@ -82,7 +82,7 @@ namespace Ryujinx.Horizon
                         InsertFreeRange(range.Offset, sizeDelta);
                     }
 
-                    ulong endOffset = range.Offset + range.Size;
+                    ulong endOffset     = range.Offset + range.Size;
                     ulong remainingSize = endOffset - (alignedOffset + size);
                     if (remainingSize != 0)
                     {
diff --git a/Ryujinx.Horizon/HorizonOptions.cs b/Ryujinx.Horizon/HorizonOptions.cs
index b1567c6a4b..6d580e8b93 100644
--- a/Ryujinx.Horizon/HorizonOptions.cs
+++ b/Ryujinx.Horizon/HorizonOptions.cs
@@ -2,11 +2,13 @@ namespace Ryujinx.Horizon
 {
     public struct HorizonOptions
     {
-        public bool IgnoreMissingServices { get; }
+        public bool IgnoreMissingServices    { get; }
+        public bool ThrowOnInvalidCommandIds { get; }
 
         public HorizonOptions(bool ignoreMissingServices)
         {
-            IgnoreMissingServices = ignoreMissingServices;
+            IgnoreMissingServices    = ignoreMissingServices;
+            ThrowOnInvalidCommandIds = true;
         }
     }
 }
diff --git a/Ryujinx.Horizon/HorizonStatic.cs b/Ryujinx.Horizon/HorizonStatic.cs
index 1e483cd44b..e372df699f 100644
--- a/Ryujinx.Horizon/HorizonStatic.cs
+++ b/Ryujinx.Horizon/HorizonStatic.cs
@@ -21,24 +21,24 @@ namespace Ryujinx.Horizon
         [ThreadStatic]
         private static int _threadHandle;
 
-        public static HorizonOptions Options => _options;
-        public static ISyscallApi Syscall => _syscall;
-        public static IVirtualMemoryManager AddressSpace => _addressSpace;
-        public static IThreadContext ThreadContext => _threadContext;
-        public static int CurrentThreadHandle => _threadHandle;
+        public static HorizonOptions        Options             => _options;
+        public static ISyscallApi           Syscall             => _syscall;
+        public static IVirtualMemoryManager AddressSpace        => _addressSpace;
+        public static IThreadContext        ThreadContext       => _threadContext;
+        public static int                   CurrentThreadHandle => _threadHandle;
 
         public static void Register(
-            HorizonOptions options,
-            ISyscallApi syscallApi,
+            HorizonOptions        options,
+            ISyscallApi           syscallApi,
             IVirtualMemoryManager addressSpace,
-            IThreadContext threadContext,
-            int threadHandle)
+            IThreadContext        threadContext,
+            int                   threadHandle)
         {
-            _options = options;
-            _syscall = syscallApi;
-            _addressSpace = addressSpace;
+            _options       = options;
+            _syscall       = syscallApi;
+            _addressSpace  = addressSpace;
             _threadContext = threadContext;
-            _threadHandle = threadHandle;
+            _threadHandle  = threadHandle;
         }
     }
 }
diff --git a/Ryujinx.Horizon/IService.cs b/Ryujinx.Horizon/IService.cs
index 67c12cef64..c0bc0bcb79 100644
--- a/Ryujinx.Horizon/IService.cs
+++ b/Ryujinx.Horizon/IService.cs
@@ -2,6 +2,6 @@
 {
     interface IService
     {
-        abstract static void Main();
+        abstract static void Main(ServiceTable serviceTable);
     }
 }
diff --git a/Ryujinx.Horizon/LogManager/LmLogger.cs b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs
similarity index 81%
rename from Ryujinx.Horizon/LogManager/LmLogger.cs
rename to Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs
index 461776cd89..002a59824d 100644
--- a/Ryujinx.Horizon/LogManager/LmLogger.cs
+++ b/Ryujinx.Horizon/LogManager/Ipc/LmLogger.cs
@@ -9,23 +9,23 @@ using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
 
-namespace Ryujinx.Horizon.LogManager
+namespace Ryujinx.Horizon.LogManager.Ipc
 {
-    partial class LmLogger : IServiceObject
+    partial class LmLogger : ILmLogger
     {
-        private readonly LmLog _log;
-        private readonly ulong _clientProcessId;
+        private readonly LogService _log;
+        private readonly ulong      _pid;
 
-        public LmLogger(LmLog log, ulong clientProcessId)
+        public LmLogger(LogService log, ulong pid)
         {
             _log = log;
-            _clientProcessId = clientProcessId;
+            _pid = pid;
         }
 
         [CmifCommand(0)]
         public Result Log([Buffer(HipcBufferFlags.In | HipcBufferFlags.AutoSelect)] Span<byte> message)
         {
-            if (!SetProcessId(message, _clientProcessId))
+            if (!SetProcessId(message, _pid))
             {
                 return Result.Success;
             }
@@ -35,7 +35,7 @@ namespace Ryujinx.Horizon.LogManager
             return Result.Success;
         }
 
-        [CmifCommand(1)]
+        [CmifCommand(1)] // 3.0.0+
         public Result SetDestination(LogDestination destination)
         {
             _log.LogDestination = destination;
@@ -48,7 +48,6 @@ namespace Ryujinx.Horizon.LogManager
             ref LogPacketHeader header = ref MemoryMarshal.Cast<byte, LogPacketHeader>(message)[0];
 
             uint expectedMessageSize = (uint)Unsafe.SizeOf<LogPacketHeader>() + header.PayloadSize;
-
             if (expectedMessageSize != (uint)message.Length)
             {
                 Logger.Warning?.Print(LogClass.ServiceLm, $"Invalid message size (expected 0x{expectedMessageSize:X} but got 0x{message.Length:X}).");
@@ -63,13 +62,11 @@ namespace Ryujinx.Horizon.LogManager
 
         private static string LogImpl(ReadOnlySpan<byte> message)
         {
-            SpanReader reader = new SpanReader(message);
+            SpanReader      reader  = new(message);
+            LogPacketHeader header  = reader.Read<LogPacketHeader>();
+            StringBuilder   builder = new();
 
-            LogPacketHeader header = reader.Read<LogPacketHeader>();
-
-            StringBuilder sb = new StringBuilder();
-
-            sb.AppendLine($"Guest Log:\n  Log level: {header.Severity}");
+            builder.AppendLine($"Guest Log:\n  Log level: {header.Severity}");
 
             while (reader.Length > 0)
             {
@@ -78,7 +75,7 @@ namespace Ryujinx.Horizon.LogManager
 
                 LogDataChunkKey field = (LogDataChunkKey)type;
 
-                string fieldStr = string.Empty;
+                string fieldStr;
 
                 if (field == LogDataChunkKey.Start)
                 {
@@ -111,16 +108,16 @@ namespace Ryujinx.Horizon.LogManager
                     fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'";
                 }
 
-                sb.AppendLine($"    {fieldStr}");
+                builder.AppendLine($"    {fieldStr}");
             }
 
-            return sb.ToString();
+            return builder.ToString();
         }
 
         private static int ReadUleb128(ref SpanReader reader)
         {
             int result = 0;
-            int count = 0;
+            int count  = 0;
 
             byte encoded;
 
@@ -136,4 +133,4 @@ namespace Ryujinx.Horizon.LogManager
             return result;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/LogManager/LmLog.cs b/Ryujinx.Horizon/LogManager/Ipc/LogService.cs
similarity index 55%
rename from Ryujinx.Horizon/LogManager/LmLog.cs
rename to Ryujinx.Horizon/LogManager/Ipc/LogService.cs
index 772465c459..6899739e35 100644
--- a/Ryujinx.Horizon/LogManager/LmLog.cs
+++ b/Ryujinx.Horizon/LogManager/Ipc/LogService.cs
@@ -2,18 +2,19 @@
 using Ryujinx.Horizon.Sdk.Lm;
 using Ryujinx.Horizon.Sdk.Sf;
 
-namespace Ryujinx.Horizon.LogManager
+namespace Ryujinx.Horizon.LogManager.Ipc
 {
-    partial class LmLog : IServiceObject
+    partial class LogService : ILogService
     {
         public LogDestination LogDestination { get; set; } = LogDestination.TargetManager;
 
         [CmifCommand(0)]
-        public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong clientProcessId)
+        public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong pid)
         {
-            logger = new LmLogger(this, clientProcessId);
+            // NOTE: Internal name is Logger, but we rename it LmLogger to avoid name clash with Ryujinx.Common.Logging logger.
+            logger = new LmLogger(this, pid);
 
             return Result.Success;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/LogManager/LmIpcServer.cs b/Ryujinx.Horizon/LogManager/LmIpcServer.cs
index 7b757fe988..71b844a2b4 100644
--- a/Ryujinx.Horizon/LogManager/LmIpcServer.cs
+++ b/Ryujinx.Horizon/LogManager/LmIpcServer.cs
@@ -1,6 +1,6 @@
-using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.LogManager.Ipc;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
 using Ryujinx.Horizon.Sdk.Sm;
-using Ryujinx.Horizon.Sm;
 
 namespace Ryujinx.Horizon.LogManager
 {
@@ -9,36 +9,25 @@ namespace Ryujinx.Horizon.LogManager
         private const int LogMaxSessionsCount = 42;
 
         private const int PointerBufferSize = 0x400;
-        private const int MaxDomains = 31;
-        private const int MaxDomainObjects = 61;
+        private const int MaxDomains        = 31;
+        private const int MaxDomainObjects  = 61;
+        private const int MaxPortsCount     = 1;
 
-        private const int MaxPortsCount = 1;
+        private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
-        private static readonly ManagerOptions _logManagerOptions = new ManagerOptions(
-            PointerBufferSize,
-            MaxDomains,
-            MaxDomainObjects,
-            false);
-
-        private static readonly ServiceName _logServiceName = ServiceName.Encode("lm");
-
-        private SmApi _sm;
+        private SmApi         _sm;
         private ServerManager _serverManager;
 
-        private LmLog _logServiceObject;
-
         public void Initialize()
         {
-            HeapAllocator allocator = new HeapAllocator();
+            HeapAllocator allocator = new();
 
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
             _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount);
 
-            _logServiceObject = new LmLog();
-
-            _serverManager.RegisterObjectForServer(_logServiceObject, _logServiceName, LogMaxSessionsCount);
+            _serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), LogMaxSessionsCount);
         }
 
         public void ServiceRequests()
@@ -51,4 +40,4 @@ namespace Ryujinx.Horizon.LogManager
             _serverManager.Dispose();
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/LogManager/LmMain.cs b/Ryujinx.Horizon/LogManager/LmMain.cs
index 8c0262ac67..bbe96d4c95 100644
--- a/Ryujinx.Horizon/LogManager/LmMain.cs
+++ b/Ryujinx.Horizon/LogManager/LmMain.cs
@@ -2,13 +2,16 @@
 {
     class LmMain : IService
     {
-        public static void Main()
+        public static void Main(ServiceTable serviceTable)
         {
-            LmIpcServer ipcServer = new LmIpcServer();
+            LmIpcServer ipcServer = new();
 
             ipcServer.Initialize();
+
+            serviceTable.SignalServiceReady();
+
             ipcServer.ServiceRequests();
             ipcServer.Shutdown();
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs
new file mode 100644
index 0000000000..672bba4eae
--- /dev/null
+++ b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs
@@ -0,0 +1,218 @@
+using MsgPack;
+using MsgPack.Serialization;
+using Ryujinx.Common.Logging;
+using Ryujinx.Common.Utilities;
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Prepo.Types;
+using Ryujinx.Horizon.Sdk.Account;
+using Ryujinx.Horizon.Sdk.Prepo;
+using Ryujinx.Horizon.Sdk.Sf;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using System;
+using System.Text;
+
+namespace Ryujinx.Horizon.Prepo.Ipc
+{
+    partial class PrepoService : IPrepoService
+    {
+        enum PlayReportKind
+        {
+            Normal,
+            System
+        }
+
+        private readonly PrepoServicePermissionLevel _permissionLevel;
+        private ulong _systemSessionId;
+
+        private bool _immediateTransmissionEnabled = false;
+        private bool _userAgreementCheckEnabled    = true;
+
+        public PrepoService(PrepoServicePermissionLevel permissionLevel)
+        {
+            _permissionLevel = permissionLevel;
+        }
+
+        [CmifCommand(10100)] // 1.0.0-5.1.0
+        [CmifCommand(10102)] // 6.0.0-9.2.0
+        [CmifCommand(10104)] // 10.0.0+
+        public Result SaveReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid)
+        {
+            if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0)
+            {
+                return PrepoResult.PermissionDenied;
+            }
+
+            ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, Uid.Null);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(10101)] // 1.0.0-5.1.0
+        [CmifCommand(10103)] // 6.0.0-9.2.0
+        [CmifCommand(10105)] // 10.0.0+
+        public Result SaveReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid)
+        {
+            if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0)
+            {
+                return PrepoResult.PermissionDenied;
+            }
+
+            ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, userId, true);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(10200)]
+        public Result RequestImmediateTransmission()
+        {
+            _immediateTransmissionEnabled = true;
+
+            // It signals an event of nn::prepo::detail::service::core::TransmissionStatusManager that requests the transmission of the report.
+            // Since we don't use reports, it's fine to do nothing.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(10300)]
+        public Result GetTransmissionStatus(out int status)
+        {
+            status = 0;
+
+            if (_immediateTransmissionEnabled && _userAgreementCheckEnabled)
+            {
+                status = 1;
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(10400)] // 9.0.0+
+        public Result GetSystemSessionId(out ulong systemSessionId)
+        {
+            systemSessionId = default;
+
+            if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0)
+            {
+                return PrepoResult.PermissionDenied;
+            }
+
+            if (_systemSessionId == 0)
+            {
+                _systemSessionId = (ulong)Random.Shared.NextInt64();
+            }
+
+            systemSessionId = _systemSessionId;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(20100)]
+        public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid)
+        {
+            if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0)
+            {
+                return PrepoResult.PermissionDenied;
+            }
+
+            return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, Uid.Null);
+        }
+
+        [CmifCommand(20101)]
+        public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid)
+        {
+            if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0)
+            {
+                return PrepoResult.PermissionDenied;
+            }
+
+            return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, userId, true);
+        }
+
+        [CmifCommand(40100)] // 2.0.0+
+        public Result IsUserAgreementCheckEnabled(out bool enabled)
+        {
+            enabled = false;
+
+            if (_permissionLevel == PrepoServicePermissionLevel.User || _permissionLevel == PrepoServicePermissionLevel.System)
+            {
+                enabled = _userAgreementCheckEnabled;
+
+                // If "enabled" is false, it sets some internal fields to 0.
+                // Then, it mounts "prepo-sys:/is_user_agreement_check_enabled.bin" and returns the contained bool.
+                // We can return the private bool instead, we don't care about the agreement since we don't send reports.
+
+                return Result.Success;
+            }
+
+            return PrepoResult.PermissionDenied;
+        }
+
+        [CmifCommand(40101)] // 2.0.0+
+        public Result SetUserAgreementCheckEnabled(bool enabled)
+        {
+            if (_permissionLevel == PrepoServicePermissionLevel.User || _permissionLevel == PrepoServicePermissionLevel.System)
+            {
+                _userAgreementCheckEnabled = enabled;
+
+                // If "enabled" is false, it sets some internal fields to 0.
+                // Then, it mounts "prepo-sys:/is_user_agreement_check_enabled.bin" and stores the "enabled" value.
+                // We can store in the private bool instead, we don't care about the agreement since we don't send reports.
+
+                return Result.Success;
+            }
+
+            return PrepoResult.PermissionDenied;
+        }
+
+        private static Result ProcessPlayReport(PlayReportKind playReportKind, ulong pid, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, Uid userId, bool withUserId = false)
+        {
+            if (withUserId)
+            {
+                if (userId.IsNull)
+                {
+                    return PrepoResult.InvalidArgument;
+                }
+            }
+
+            if (gameRoomBuffer.Length > 31)
+            {
+                return PrepoResult.InvalidArgument;
+            }
+
+            string gameRoom = Encoding.UTF8.GetString(gameRoomBuffer).TrimEnd();
+
+            if (gameRoom == string.Empty)
+            {
+                return PrepoResult.InvalidState;
+            }
+
+            if (reportBuffer.Length == 0)
+            {
+                return PrepoResult.InvalidBufferSize;
+            }
+
+            // NOTE: The service calls arp:r using the pid to get the application id, if it fails PrepoResult.InvalidPid is returned.
+            //       Reports are stored internally and an event is signaled to transmit them.
+
+            StringBuilder     builder            = new();
+            MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
+
+            builder.AppendLine();
+            builder.AppendLine("PlayReport log:");
+            builder.AppendLine($" Kind: {playReportKind}");
+            builder.AppendLine($" Pid: {pid}");
+
+            if (!userId.IsNull)
+            {
+                builder.AppendLine($" UserId: {userId}");
+            }
+
+            builder.AppendLine($" Room: {gameRoom}");
+            builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
+
+            Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString());
+
+            return Result.Success;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs b/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs
new file mode 100644
index 0000000000..ba19ff6f66
--- /dev/null
+++ b/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs
@@ -0,0 +1,49 @@
+using Ryujinx.Horizon.Prepo.Types;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.Sdk.Sm;
+
+namespace Ryujinx.Horizon.Prepo
+{
+    class PrepoIpcServer
+    {
+        private const int PrepoMaxSessionsCount      = 12;
+        private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6;
+
+        private const int PointerBufferSize = 0x3800;
+        private const int MaxDomains        = 64;
+        private const int MaxDomainObjects  = 16;
+        private const int MaxPortsCount     = 6;
+
+        private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+
+        private SmApi _sm;
+        private PrepoServerManager _serverManager;
+
+        public void Initialize()
+        {
+            HeapAllocator allocator = new();
+
+            _sm = new SmApi();
+            _sm.Initialize().AbortOnFailure();
+
+            _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, PrepoTotalMaxSessionsCount);
+
+            _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
+        }
+
+        public void ServiceRequests()
+        {
+            _serverManager.ServiceRequests();
+        }
+
+        public void Shutdown()
+        {
+            _serverManager.Dispose();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Prepo/PrepoMain.cs b/Ryujinx.Horizon/Prepo/PrepoMain.cs
new file mode 100644
index 0000000000..5ff0f53d94
--- /dev/null
+++ b/Ryujinx.Horizon/Prepo/PrepoMain.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.Horizon.Prepo
+{
+    class PrepoMain : IService
+    {
+        public static void Main(ServiceTable serviceTable)
+        {
+            PrepoIpcServer ipcServer = new();
+
+            ipcServer.Initialize();
+
+            serviceTable.SignalServiceReady();
+
+            ipcServer.ServiceRequests();
+            ipcServer.Shutdown();
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Prepo/PrepoResult.cs b/Ryujinx.Horizon/Prepo/PrepoResult.cs
new file mode 100644
index 0000000000..12255e3d88
--- /dev/null
+++ b/Ryujinx.Horizon/Prepo/PrepoResult.cs
@@ -0,0 +1,15 @@
+using Ryujinx.Horizon.Common;
+
+namespace Ryujinx.Horizon.Prepo
+{
+    static class PrepoResult
+    {
+        private const int ModuleId = 129;
+
+        public static Result InvalidArgument   => new(ModuleId, 1);
+        public static Result InvalidState      => new(ModuleId, 5);
+        public static Result InvalidBufferSize => new(ModuleId, 9);
+        public static Result PermissionDenied  => new(ModuleId, 90);
+        public static Result InvalidPid        => new(ModuleId, 101);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Prepo/PrepoServerManager.cs b/Ryujinx.Horizon/Prepo/PrepoServerManager.cs
new file mode 100644
index 0000000000..55e4ff7dbc
--- /dev/null
+++ b/Ryujinx.Horizon/Prepo/PrepoServerManager.cs
@@ -0,0 +1,30 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Prepo.Ipc;
+using Ryujinx.Horizon.Prepo.Types;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.Sdk.Sm;
+using System;
+
+namespace Ryujinx.Horizon.Prepo
+{
+    class PrepoServerManager : ServerManager
+    {
+        public PrepoServerManager(HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions)
+        {
+        }
+
+        protected override Result OnNeedsToAccept(int portIndex, Server server)
+        {
+            return (PrepoPortIndex)portIndex switch
+            {
+                PrepoPortIndex.Admin   => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Admin)),
+                PrepoPortIndex.Admin2  => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Admin)),
+                PrepoPortIndex.Manager => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Manager)),
+                PrepoPortIndex.User    => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.User)),
+                PrepoPortIndex.System  => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.System)),
+                PrepoPortIndex.Debug   => AcceptImpl(server, new PrepoService(PrepoServicePermissionLevel.Debug)),
+                _                      => throw new ArgumentOutOfRangeException(nameof(portIndex)),
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs b/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs
new file mode 100644
index 0000000000..f4d6b87735
--- /dev/null
+++ b/Ryujinx.Horizon/Prepo/Types/PrepoPortIndex.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Horizon.Prepo.Types
+{
+    enum PrepoPortIndex
+    {
+        Admin,
+        Admin2,
+        Manager,
+        User,
+        System,
+        Debug
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs b/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs
similarity index 52%
rename from Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs
rename to Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs
index 35774707d6..8214f4b9a5 100644
--- a/Ryujinx.HLE/HOS/Services/Prepo/Types/PrepoServicePermissionLevel.cs
+++ b/Ryujinx.Horizon/Prepo/Types/PrepoServicePermissionLevel.cs
@@ -1,10 +1,11 @@
-namespace Ryujinx.HLE.HOS.Services.Prepo
+namespace Ryujinx.Horizon.Prepo.Types
 {
     enum PrepoServicePermissionLevel
     {
         Admin   = -1,
         User    = 1,
         System  = 2,
-        Manager = 6
+        Manager = 6,
+        Debug   = unchecked((int)0x80000006)
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/Ryujinx.Horizon/Ryujinx.Horizon.csproj
index e4591c6f93..0139c367f4 100644
--- a/Ryujinx.Horizon/Ryujinx.Horizon.csproj
+++ b/Ryujinx.Horizon/Ryujinx.Horizon.csproj
@@ -11,4 +11,8 @@
     <ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
   </ItemGroup>
 
+  <ItemGroup>
+    <PackageReference Include="LibHac" />
+  </ItemGroup>
+
 </Project>
diff --git a/Ryujinx.Horizon/Sdk/Account/Uid.cs b/Ryujinx.Horizon/Sdk/Account/Uid.cs
new file mode 100644
index 0000000000..a0a3997810
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Account/Uid.cs
@@ -0,0 +1,62 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Horizon.Sdk.Account
+{
+    [StructLayout(LayoutKind.Sequential)]
+    public readonly record struct Uid
+    {
+        public readonly long High;
+        public readonly long Low;
+
+        public bool IsNull => (Low | High) == 0;
+
+        public static Uid Null => new(0, 0);
+
+        public Uid(long low, long high)
+        {
+            Low  = low;
+            High = high;
+        }
+
+        public Uid(byte[] bytes)
+        {
+            High = BitConverter.ToInt64(bytes, 0);
+            Low  = BitConverter.ToInt64(bytes, 8);
+        }
+
+        public Uid(string hex)
+        {
+            if (hex == null || hex.Length != 32 || !hex.All("0123456789abcdefABCDEF".Contains))
+            {
+                throw new ArgumentException("Invalid Hex value!", nameof(hex));
+            }
+
+            Low  = Convert.ToInt64(hex[16..], 16);
+            High = Convert.ToInt64(hex[..16], 16);
+        }
+
+        public void Write(BinaryWriter binaryWriter)
+        {
+            binaryWriter.Write(High);
+            binaryWriter.Write(Low);
+        }
+
+        public override string ToString()
+        {
+            return High.ToString("x16") + Low.ToString("x16");
+        }
+
+        public LibHac.Account.Uid ToLibHacUid()
+        {
+            return new LibHac.Account.Uid((ulong)High, (ulong)Low);
+        }
+
+        public UInt128 ToUInt128()
+        {
+            return new UInt128((ulong)High, (ulong)Low);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs b/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs
new file mode 100644
index 0000000000..bb5770cbc8
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Lm/ILmLogger.cs
@@ -0,0 +1,12 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf;
+using System;
+
+namespace Ryujinx.Horizon.Sdk.Lm
+{
+    interface ILmLogger : IServiceObject
+    {
+        Result Log(Span<byte> message);
+        Result SetDestination(LogDestination destination);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Lm/ILogService.cs b/Ryujinx.Horizon/Sdk/Lm/ILogService.cs
new file mode 100644
index 0000000000..ad6c8455a5
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Lm/ILogService.cs
@@ -0,0 +1,11 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.LogManager.Ipc;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Sdk.Lm
+{
+    interface ILogService : IServiceObject
+    {
+        Result OpenLogger(out LmLogger logger, ulong pid);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs
new file mode 100644
index 0000000000..f61ee61bd9
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Account;
+using Ryujinx.Horizon.Sdk.Sf;
+using System;
+
+namespace Ryujinx.Horizon.Sdk.Prepo
+{
+    interface IPrepoService : IServiceObject
+    {
+        Result SaveReport(ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
+        Result SaveReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
+        Result RequestImmediateTransmission();
+        Result GetTransmissionStatus(out int status);
+        Result GetSystemSessionId(out ulong systemSessionId);
+        Result SaveSystemReport(ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
+        Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
+        Result IsUserAgreementCheckEnabled(out bool enabled);
+        Result SetUserAgreementCheckEnabled(bool enabled);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/ServiceUtil.cs b/Ryujinx.Horizon/Sdk/ServiceUtil.cs
index 413ac1f6b7..fe6fcce157 100644
--- a/Ryujinx.Horizon/Sdk/ServiceUtil.cs
+++ b/Ryujinx.Horizon/Sdk/ServiceUtil.cs
@@ -10,15 +10,15 @@ namespace Ryujinx.Horizon.Sdk
         public static Result SendRequest(out CmifResponse response, int sessionHandle, uint requestId, bool sendPid, scoped ReadOnlySpan<byte> data)
         {
             ulong tlsAddress = HorizonStatic.ThreadContext.TlsAddress;
-            int tlsSize = Api.TlsMessageBufferSize;
+            int   tlsSize    = Api.TlsMessageBufferSize;
 
             using (var tlsRegion = HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize))
             {
                 CmifRequest request = CmifMessage.CreateRequest(tlsRegion.Memory.Span, new CmifRequestFormat()
                 {
-                    DataSize = data.Length,
+                    DataSize  = data.Length,
                     RequestId = requestId,
-                    SendPid = sendPid
+                    SendPid   = sendPid
                 });
 
                 data.CopyTo(request.Data);
@@ -29,10 +29,11 @@ namespace Ryujinx.Horizon.Sdk
             if (result.IsFailure)
             {
                 response = default;
+
                 return result;
             }
 
             return CmifMessage.ParseResponse(out response, HorizonStatic.AddressSpace.GetWritableRegion(tlsAddress, tlsSize).Memory.Span, false, 0);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs
index 8821150139..beaff613f5 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainInHeader.cs
@@ -3,10 +3,10 @@
     struct CmifDomainInHeader
     {
         public CmifDomainRequestType Type;
-        public byte ObjectsCount;
-        public ushort DataSize;
-        public int ObjectId;
-        public uint Padding;
-        public uint Token;
+        public byte                  ObjectsCount;
+        public ushort                DataSize;
+        public int                   ObjectId;
+        public uint                  Padding;
+        public uint                  Token;
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs
index b913db94e0..1a02e08259 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifDomainRequestType.cs
@@ -2,8 +2,8 @@
 {
     enum CmifDomainRequestType : byte
     {
-        Invalid = 0,
+        Invalid     = 0,
         SendMessage = 1,
-        Close = 2
+        Close       = 2
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs
index 781452e309..0d23d33bb7 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifMessage.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 {
     static class CmifMessage
     {
-        public const uint CmifInHeaderMagic = 0x49434653; // SFCI
+        public const uint CmifInHeaderMagic  = 0x49434653; // SFCI
         public const uint CmifOutHeaderMagic = 0x4f434653; // SFCO
 
         public static CmifRequest CreateRequest(Span<byte> output, CmifRequestFormat format)
@@ -21,27 +21,31 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
             }
 
             totalSize += Unsafe.SizeOf<CmifInHeader>() + format.DataSize;
-            totalSize = (totalSize + 1) & ~1;
+            totalSize  = (totalSize + 1) & ~1;
+
             int outPointerSizeTableOffset = totalSize;
-            int outPointerSizeTableSize = format.OutAutoBuffersCount + format.OutPointersCount;
+            int outPointerSizeTableSize   = format.OutAutoBuffersCount + format.OutPointersCount;
+
             totalSize += sizeof(ushort) * outPointerSizeTableSize;
+
             int rawDataSizeInWords = (totalSize + sizeof(uint) - 1) / sizeof(uint);
 
-            CmifRequest request = new CmifRequest();
-
-            request.Hipc = HipcMessage.WriteMessage(output, new HipcMetadata()
+            CmifRequest request = new()
             {
-                Type = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request,
-                SendStaticsCount = format.InAutoBuffersCount + format.InPointersCount,
-                SendBuffersCount = format.InAutoBuffersCount + format.InBuffersCount,
-                ReceiveBuffersCount = format.OutAutoBuffersCount + format.OutBuffersCount,
-                ExchangeBuffersCount = format.InOutBuffersCount,
-                DataWordsCount = rawDataSizeInWords,
-                ReceiveStaticsCount = outPointerSizeTableSize + format.OutFixedPointersCount,
-                SendPid = format.SendPid,
-                CopyHandlesCount = format.HandlesCount,
-                MoveHandlesCount = 0
-            });
+                Hipc = HipcMessage.WriteMessage(output, new HipcMetadata()
+                {
+                    Type                 = format.Context != 0 ? (int)CommandType.RequestWithContext : (int)CommandType.Request,
+                    SendStaticsCount     = format.InAutoBuffersCount + format.InPointersCount,
+                    SendBuffersCount     = format.InAutoBuffersCount + format.InBuffersCount,
+                    ReceiveBuffersCount  = format.OutAutoBuffersCount + format.OutBuffersCount,
+                    ExchangeBuffersCount = format.InOutBuffersCount,
+                    DataWordsCount       = rawDataSizeInWords,
+                    ReceiveStaticsCount  = outPointerSizeTableSize + format.OutFixedPointersCount,
+                    SendPid              = format.SendPid,
+                    CopyHandlesCount     = format.HandlesCount,
+                    MoveHandlesCount     = 0
+                })
+            };
 
             Span<uint> data = request.Hipc.DataWords;
 
@@ -53,35 +57,36 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 
                 domainHeader = new CmifDomainInHeader()
                 {
-                    Type = CmifDomainRequestType.SendMessage,
+                    Type         = CmifDomainRequestType.SendMessage,
                     ObjectsCount = (byte)format.ObjectsCount,
-                    DataSize = (ushort)payloadSize,
-                    ObjectId = format.ObjectId,
-                    Padding = 0,
-                    Token = format.Context
+                    DataSize     = (ushort)payloadSize,
+                    ObjectId     = format.ObjectId,
+                    Padding      = 0,
+                    Token        = format.Context
                 };
 
-                data = data.Slice(Unsafe.SizeOf<CmifDomainInHeader>() / sizeof(uint));
+                data = data[(Unsafe.SizeOf<CmifDomainInHeader>() / sizeof(uint))..];
 
-                request.Objects = data.Slice((payloadSize + sizeof(uint) - 1) / sizeof(uint));
+                request.Objects = data[((payloadSize + sizeof(uint) - 1) / sizeof(uint))..];
             }
 
             ref CmifInHeader header = ref MemoryMarshal.Cast<uint, CmifInHeader>(data)[0];
 
             header = new CmifInHeader()
             {
-                Magic = CmifInHeaderMagic,
-                Version = format.Context != 0 ? 1u : 0u,
+                Magic     = CmifInHeaderMagic,
+                Version   = format.Context != 0 ? 1u : 0u,
                 CommandId = format.RequestId,
-                Token = format.ObjectId != 0 ? 0u : format.Context
+                Token     = format.ObjectId != 0 ? 0u : format.Context
             };
 
-            request.Data = MemoryMarshal.Cast<uint, byte>(data).Slice(Unsafe.SizeOf<CmifInHeader>());
+            request.Data = MemoryMarshal.Cast<uint, byte>(data)[Unsafe.SizeOf<CmifInHeader>()..];
 
             int paddingSizeBefore = (rawDataSizeInWords - request.Hipc.DataWords.Length) * sizeof(uint);
 
-            Span<byte> outPointerTable = MemoryMarshal.Cast<uint, byte>(request.Hipc.DataWords).Slice(outPointerSizeTableOffset - paddingSizeBefore);
-            request.OutPointerSizes = MemoryMarshal.Cast<byte, ushort>(outPointerTable);
+            Span<byte> outPointerTable = MemoryMarshal.Cast<uint, byte>(request.Hipc.DataWords)[(outPointerSizeTableOffset - paddingSizeBefore)..];
+
+            request.OutPointerSizes   = MemoryMarshal.Cast<byte, ushort>(outPointerTable);
             request.ServerPointerSize = format.ServerPointerSize;
 
             return request;
@@ -89,15 +94,15 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 
         public static Result ParseResponse(out CmifResponse response, Span<byte> input, bool isDomain, int size)
         {
-            HipcMessage responseMessage = new HipcMessage(input);
+            HipcMessage responseMessage = new(input);
 
-            Span<byte> data = MemoryMarshal.Cast<uint, byte>(responseMessage.Data.DataWords);
+            Span<byte> data    = MemoryMarshal.Cast<uint, byte>(responseMessage.Data.DataWords);
             Span<uint> objects = Span<uint>.Empty;
 
             if (isDomain)
             {
-                data = data.Slice(Unsafe.SizeOf<CmifDomainOutHeader>());
-                objects = MemoryMarshal.Cast<byte, uint>(data.Slice(Unsafe.SizeOf<CmifOutHeader>() + size));
+                data    = data[Unsafe.SizeOf<CmifDomainOutHeader>()..];
+                objects = MemoryMarshal.Cast<byte, uint>(data[(Unsafe.SizeOf<CmifOutHeader>() + size)..]);
             }
 
             CmifOutHeader header = MemoryMarshal.Cast<byte, CmifOutHeader>(data)[0];
@@ -105,19 +110,21 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
             if (header.Magic != CmifOutHeaderMagic)
             {
                 response = default;
+
                 return SfResult.InvalidOutHeader;
             }
 
             if (header.Result.IsFailure)
             {
                 response = default;
+
                 return header.Result;
             }
 
             response = new CmifResponse()
             {
-                Data = data.Slice(Unsafe.SizeOf<CmifOutHeader>()),
-                Objects = objects,
+                Data        = data[Unsafe.SizeOf<CmifOutHeader>()..],
+                Objects     = objects,
                 CopyHandles = responseMessage.Data.CopyHandles,
                 MoveHandles = responseMessage.Data.MoveHandles
             };
@@ -125,4 +132,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
             return Result.Success;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs
index 2828cde5e8..00b9d2bda7 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifOutHeader.cs
@@ -5,10 +5,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
     struct CmifOutHeader
     {
 #pragma warning disable CS0649
-        public uint Magic;
-        public uint Version;
+        public uint   Magic;
+        public uint   Version;
         public Result Result;
-        public uint Token;
+        public uint   Token;
 #pragma warning restore CS0649
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs
index 80772ad334..e44a84ec17 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequest.cs
@@ -6,9 +6,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
     ref struct CmifRequest
     {
         public HipcMessageData Hipc;
-        public Span<byte> Data;
-        public Span<ushort> OutPointerSizes;
-        public Span<uint> Objects;
-        public int ServerPointerSize;
+        public Span<byte>      Data;
+        public Span<ushort>    OutPointerSizes;
+        public Span<uint>      Objects;
+        public int             ServerPointerSize;
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs
index d11545786b..592f11f421 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifRequestFormat.cs
@@ -3,21 +3,21 @@
     struct CmifRequestFormat
     {
 #pragma warning disable CS0649
-        public int ObjectId;
+        public int  ObjectId;
         public uint RequestId;
         public uint Context;
-        public int DataSize;
-        public int ServerPointerSize;
-        public int InAutoBuffersCount;
-        public int OutAutoBuffersCount;
-        public int InBuffersCount;
-        public int OutBuffersCount;
-        public int InOutBuffersCount;
-        public int InPointersCount;
-        public int OutPointersCount;
-        public int OutFixedPointersCount;
-        public int ObjectsCount;
-        public int HandlesCount;
+        public int  DataSize;
+        public int  ServerPointerSize;
+        public int  InAutoBuffersCount;
+        public int  OutAutoBuffersCount;
+        public int  InBuffersCount;
+        public int  OutBuffersCount;
+        public int  InOutBuffersCount;
+        public int  InPointersCount;
+        public int  OutPointersCount;
+        public int  OutFixedPointersCount;
+        public int  ObjectsCount;
+        public int  HandlesCount;
         public bool SendPid;
 #pragma warning restore CS0649
     }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs
index d1d8dc9c50..2ff31eb670 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CmifResponse.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
     {
         public ReadOnlySpan<byte> Data;
         public ReadOnlySpan<uint> Objects;
-        public ReadOnlySpan<int> CopyHandles;
-        public ReadOnlySpan<int> MoveHandles;
+        public ReadOnlySpan<int>  CopyHandles;
+        public ReadOnlySpan<int>  MoveHandles;
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs
index b3b05864eb..82c0648b69 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/CommandType.cs
@@ -2,12 +2,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 {
     enum CommandType
     {
-        Invalid = 0,
-        LegacyRequest = 1,
-        Close = 2,
-        LegacyControl = 3,
-        Request = 4,
-        Control = 5,
+        Invalid            = 0,
+        LegacyRequest      = 1,
+        Close              = 2,
+        LegacyControl      = 3,
+        Request            = 4,
+        Control            = 5,
         RequestWithContext = 6,
         ControlWithContext = 7
     }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs
index bcf311b2e9..b0b4498d9f 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectDispatchTable.cs
@@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 
             var inHeader = MemoryMarshal.Cast<byte, CmifDomainInHeader>(inRawData)[0];
 
-            ReadOnlySpan<byte> inDomainRawData = inRawData.Slice(Unsafe.SizeOf<CmifDomainInHeader>());
+            ReadOnlySpan<byte> inDomainRawData = inRawData[Unsafe.SizeOf<CmifDomainInHeader>()..];
 
             int targetObjectId = inHeader.ObjectId;
 
@@ -39,7 +39,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
                         return SfResult.InvalidHeaderSize;
                     }
 
-                    ReadOnlySpan<byte> inMessageRawData = inDomainRawData.Slice(0, inHeader.DataSize);
+                    ReadOnlySpan<byte> inMessageRawData = inDomainRawData[..inHeader.DataSize];
 
                     if (inHeader.ObjectsCount > DomainServiceObjectProcessor.MaximumObjects)
                     {
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs
index 92d86196a0..796b8a7898 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/DomainServiceObjectProcessor.cs
@@ -63,7 +63,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
                 return SfResult.InvalidInObjectsCount;
             }
 
-            Result result = _domain.ReserveIds(new Span<int>(_reservedObjectIds).Slice(0, OutObjectsCount));
+            Result result = _domain.ReserveIds(new Span<int>(_reservedObjectIds)[..OutObjectsCount]);
 
             if (result.IsFailure)
             {
@@ -92,7 +92,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 
             DebugUtil.Assert(outHeaderSize + implOutDataTotalSize + OutObjectsCount * sizeof(int) <= outRawData.Length);
 
-            outRawData = outRawData.Slice(outHeaderSize);
+            outRawData = outRawData[outHeaderSize..];
             _outObjectIdsOffset = (response.DataWords.Length * sizeof(uint) - outRawData.Length) + implOutDataTotalSize;
 
             return response;
@@ -107,9 +107,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 
             DebugUtil.Assert(outHeaderSize + implOutDataTotalSize <= outRawData.Length);
 
-            outRawData = outRawData.Slice(outHeaderSize);
+            outRawData = outRawData[outHeaderSize..];
 
-            _domain.UnreserveIds(new Span<int>(_reservedObjectIds).Slice(0, OutObjectsCount));
+            _domain.UnreserveIds(new Span<int>(_reservedObjectIds)[..OutObjectsCount]);
         }
 
         public override void SetOutObjects(scoped ref ServiceDispatchContext context, HipcMessageData response, Span<ServiceObjectHolder> outObjects)
@@ -129,7 +129,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
                 _domain.RegisterObject(objectIds[i], outObjects[i]);
             }
 
-            Span<int> outObjectIds = MemoryMarshal.Cast<byte, int>(MemoryMarshal.Cast<uint, byte>(response.DataWords).Slice(_outObjectIdsOffset));
+            Span<int> outObjectIds = MemoryMarshal.Cast<byte, int>(MemoryMarshal.Cast<uint, byte>(response.DataWords)[_outObjectIdsOffset..]);
 
             for (int i = 0; i < outObjectsCount; i++)
             {
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs
index 5af00077a7..ad0e182442 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/PointerAndSize.cs
@@ -2,7 +2,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 {
     struct PointerAndSize
     {
-        public static PointerAndSize Empty => new PointerAndSize(0UL, 0UL);
+        public static PointerAndSize Empty => new(0UL, 0UL);
 
         public ulong Address { get; }
         public ulong Size { get; }
@@ -11,7 +11,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
         public PointerAndSize(ulong address, ulong size)
         {
             Address = address;
-            Size = size;
+            Size    = size;
         }
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs
index 18a404302d..6a92e8d550 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerMessageRuntimeMetadata.cs
@@ -2,28 +2,29 @@
 {
     struct ServerMessageRuntimeMetadata
     {
-        public ushort InDataSize { get; }
-        public ushort OutDataSize { get; }
-        public byte InHeadersSize { get; }
-        public byte OutHeadersSize { get; }
-        public byte InObjectsCount { get; }
-        public byte OutObjectsCount { get; }
+        public ushort InDataSize      { get; }
+        public ushort OutDataSize     { get; }
+        public byte   InHeadersSize   { get; }
+        public byte   OutHeadersSize  { get; }
+        public byte   InObjectsCount  { get; }
+        public byte   OutObjectsCount { get; }
+
         public int UnfixedOutPointerSizeOffset => InDataSize + InHeadersSize + 0x10;
 
         public ServerMessageRuntimeMetadata(
             ushort inDataSize,
             ushort outDataSize,
-            byte inHeadersSize,
-            byte outHeadersSize,
-            byte inObjectsCount,
-            byte outObjectsCount)
+            byte   inHeadersSize,
+            byte   outHeadersSize,
+            byte   inObjectsCount,
+            byte   outObjectsCount)
         {
-            InDataSize = inDataSize;
-            OutDataSize = outDataSize;
-            InHeadersSize = inHeadersSize;
-            OutHeadersSize = outHeadersSize;
-            InObjectsCount = inObjectsCount;
+            InDataSize      = inDataSize;
+            OutDataSize     = outDataSize;
+            InHeadersSize   = inHeadersSize;
+            OutHeadersSize  = outHeadersSize;
+            InObjectsCount  = inObjectsCount;
             OutObjectsCount = outObjectsCount;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs
index 3339a1a609..31be810d34 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchContext.cs
@@ -5,14 +5,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 {
     ref struct ServiceDispatchContext
     {
-        public IServiceObject ServiceObject;
-        public ServerSessionManager Manager;
-        public ServerSession Session;
+        public IServiceObject         ServiceObject;
+        public ServerSessionManager   Manager;
+        public ServerSession          Session;
         public ServerMessageProcessor Processor;
-        public HandlesToClose HandlesToClose;
-        public PointerAndSize PointerBuffer;
-        public ReadOnlySpan<byte> InMessageBuffer;
-        public Span<byte> OutMessageBuffer;
-        public HipcMessage Request;
+        public HandlesToClose         HandlesToClose;
+        public PointerAndSize         PointerBuffer;
+        public ReadOnlySpan<byte>     InMessageBuffer;
+        public Span<byte>             OutMessageBuffer;
+        public HipcMessage            Request;
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs
index 145c178391..21b342dffc 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTable.cs
@@ -12,7 +12,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
         public ServiceDispatchTable(string objectName, IReadOnlyDictionary<int, CommandHandler> entries)
         {
             _objectName = objectName;
-            _entries = entries;
+            _entries    = entries;
         }
 
         public override Result ProcessMessage(ref ServiceDispatchContext context, ReadOnlySpan<byte> inRawData)
@@ -30,4 +30,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
             return new ServiceDispatchTable(instance.GetType().Name, instance.GetCommandHandlers());
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs
index a0e28ca8fd..816000675a 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Cmif/ServiceDispatchTableBase.cs
@@ -39,17 +39,21 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
 
             if (!entries.TryGetValue((int)commandId, out var commandHandler))
             {
-                Logger.Warning?.Print(LogClass.KernelIpc, $"{objectName} command ID 0x{commandId:X} is not implemented");
-
                 if (HorizonStatic.Options.IgnoreMissingServices)
                 {
                     // If ignore missing services is enabled, just pretend that everything is fine.
-                    var response = PrepareForStubReply(ref context, out Span<byte> outRawData);
+                    PrepareForStubReply(ref context, out Span<byte> outRawData);
                     CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref outRawData);
                     outHeader[0] = new CmifOutHeader() { Magic = CmifMessage.CmifOutHeaderMagic, Result = Result.Success };
 
+                    Logger.Warning?.Print(LogClass.Service, $"Missing service {objectName} (command ID: {commandId}) ignored");
+
                     return Result.Success;
                 }
+                else if (HorizonStatic.Options.ThrowOnInvalidCommandIds)
+                {
+                    throw new NotImplementedException($"{objectName} command ID: {commandId} is not implemented");
+                }
 
                 return SfResult.UnknownCommandId;
             }
@@ -72,6 +76,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
             if (outHeader.IsEmpty)
             {
                 commandResult.AbortOnSuccess();
+
                 return commandResult;
             }
 
@@ -80,11 +85,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif
             return Result.Success;
         }
 
-        private static HipcMessageData PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData)
+        private static void PrepareForStubReply(scoped ref ServiceDispatchContext context, out Span<byte> outRawData)
         {
             var response = HipcMessage.WriteResponse(context.OutMessageBuffer, 0, 0x20 / sizeof(uint), 0, 0);
             outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
-            return response;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs b/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs
index 8f367b4e01..47aedde96d 100644
--- a/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/CommandArg.cs
@@ -20,37 +20,37 @@ namespace Ryujinx.Horizon.Sdk.Sf
 
     struct CommandArg
     {
-        public CommandArgType Type { get; }
-        public HipcBufferFlags BufferFlags { get; }
-        public ushort BufferFixedSize { get; }
-        public int ArgSize { get; }
-        public int ArgAlignment { get; }
+        public CommandArgType  Type            { get; }
+        public HipcBufferFlags BufferFlags     { get; }
+        public ushort          BufferFixedSize { get; }
+        public int             ArgSize         { get; }
+        public int             ArgAlignment    { get; }
 
         public CommandArg(CommandArgType type)
         {
-            Type = type;
-            BufferFlags = default;
+            Type            = type;
+            BufferFlags     = default;
             BufferFixedSize = 0;
-            ArgSize = 0;
-            ArgAlignment = 0;
+            ArgSize         = 0;
+            ArgAlignment    = 0;
         }
 
         public CommandArg(CommandArgType type, int argSize, int argAlignment)
         {
-            Type = type;
-            BufferFlags = default;
+            Type            = type;
+            BufferFlags     = default;
             BufferFixedSize = 0;
-            ArgSize = argSize;
-            ArgAlignment = argAlignment;
+            ArgSize         = argSize;
+            ArgAlignment    = argAlignment;
         }
 
         public CommandArg(HipcBufferFlags flags, ushort fixedSize = 0)
         {
-            Type = CommandArgType.Buffer;
-            BufferFlags = flags;
+            Type            = CommandArgType.Buffer;
+            BufferFlags     = flags;
             BufferFixedSize = fixedSize;
-            ArgSize = 0;
-            ArgAlignment = 0;
+            ArgSize         = 0;
+            ArgAlignment    = 0;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs b/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs
index 5b7c302f2b..294c7d58df 100644
--- a/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/CommandArgAttributes.cs
@@ -6,8 +6,8 @@ namespace Ryujinx.Horizon.Sdk.Sf
     [AttributeUsage(AttributeTargets.Parameter)]
     class BufferAttribute : Attribute
     {
-        public HipcBufferFlags Flags { get; }
-        public ushort FixedSize { get; }
+        public HipcBufferFlags Flags     { get; }
+        public ushort          FixedSize { get; }
 
         public BufferAttribute(HipcBufferFlags flags)
         {
@@ -16,7 +16,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
 
         public BufferAttribute(HipcBufferFlags flags, ushort fixedSize)
         {
-            Flags = flags | HipcBufferFlags.FixedSize;
+            Flags     = flags | HipcBufferFlags.FixedSize;
             FixedSize = fixedSize;
         }
     }
@@ -35,4 +35,4 @@ namespace Ryujinx.Horizon.Sdk.Sf
     class MoveHandleAttribute : Attribute
     {
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs
index ae42a8ef58..fe079d47d7 100644
--- a/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/CommandHandler.cs
@@ -9,20 +9,20 @@ namespace Ryujinx.Horizon.Sdk.Sf
     class CommandHandler
     {
         public delegate Result MethodInvoke(
-            ref ServiceDispatchContext context,
-            HipcCommandProcessor processor,
+            ref ServiceDispatchContext   context,
+            HipcCommandProcessor         processor,
             ServerMessageRuntimeMetadata runtimeMetadata,
-            ReadOnlySpan<byte> inRawData,
-            ref Span<CmifOutHeader> outHeader);
+            ReadOnlySpan<byte>           inRawData,
+            ref Span<CmifOutHeader>      outHeader);
 
-        private readonly MethodInvoke _invoke;
+        private readonly MethodInvoke         _invoke;
         private readonly HipcCommandProcessor _processor;
 
         public string MethodName => _invoke.Method.Name;
 
         public CommandHandler(MethodInvoke invoke, params CommandArg[] args)
         {
-            _invoke = invoke;
+            _invoke    = invoke;
             _processor = new HipcCommandProcessor(args);
         }
 
@@ -37,8 +37,8 @@ namespace Ryujinx.Horizon.Sdk.Sf
                 context.Processor.SetImplementationProcessor(_processor);
             }
 
-            var runtimeMetadata = context.Processor.GetRuntimeMetadata();
-            Result result = context.Processor.PrepareForProcess(ref context, runtimeMetadata);
+            var    runtimeMetadata = context.Processor.GetRuntimeMetadata();
+            Result result          = context.Processor.PrepareForProcess(ref context, runtimeMetadata);
 
             if (result.IsFailure)
             {
@@ -50,8 +50,8 @@ namespace Ryujinx.Horizon.Sdk.Sf
 
         public static void GetCmifOutHeaderPointer(ref Span<CmifOutHeader> outHeader, ref Span<byte> outRawData)
         {
-            outHeader = MemoryMarshal.Cast<byte, CmifOutHeader>(outRawData).Slice(0, 1);
-            outRawData = outRawData.Slice(Unsafe.SizeOf<CmifOutHeader>());
+            outHeader  = MemoryMarshal.Cast<byte, CmifOutHeader>(outRawData)[..1];
+            outRawData = outRawData[Unsafe.SizeOf<CmifOutHeader>()..];
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs
index 9a3a511ade..feb2d666f3 100644
--- a/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/CommandSerialization.cs
@@ -22,6 +22,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
         public static ref T GetRef<T>(PointerAndSize bufferRange) where T : unmanaged
         {
             var writableRegion = GetWritableRegion(bufferRange);
+
             return ref MemoryMarshal.Cast<byte, T>(writableRegion.Memory.Span)[0];
         }
 
@@ -65,4 +66,4 @@ namespace Ryujinx.Horizon.Sdk.Sf
             response.MoveHandles[index] = value;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs
index 594af2c84a..269ab4fe58 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferFlags.cs
@@ -5,12 +5,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
     [Flags]
     enum HipcBufferFlags : byte
     {
-        In = 1 << 0,
-        Out = 1 << 1,
-        MapAlias = 1 << 2,
-        Pointer = 1 << 3,
-        FixedSize = 1 << 4,
-        AutoSelect = 1 << 5,
+        In                         = 1 << 0,
+        Out                        = 1 << 1,
+        MapAlias                   = 1 << 2,
+        Pointer                    = 1 << 3,
+        FixedSize                  = 1 << 4,
+        AutoSelect                 = 1 << 5,
         MapTransferAllowsNonSecure = 1 << 6,
         MapTransferAllowsNonDevice = 1 << 7
     }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs
index 4ef6374ba8..b1e67253cd 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcBufferMode.cs
@@ -2,9 +2,9 @@
 {
     enum HipcBufferMode
     {
-        Normal = 0,
+        Normal    = 0,
         NonSecure = 1,
-        Invalid = 2,
+        Invalid   = 2,
         NonDevice = 3
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs
index ea2ec65056..7541e2941c 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcManager.cs
@@ -61,7 +61,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
         {
             clientHandle = 0;
 
-            if (!(_session.ServiceObjectHolder.ServiceObject is DomainServiceObject domain))
+            if (_session.ServiceObjectHolder.ServiceObject is not DomainServiceObject domain)
             {
                 return HipcResult.TargetNotDomain;
             }
@@ -112,4 +112,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             return Result.Success;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs
index 3017f40419..6500d6cf7e 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessage.cs
@@ -10,9 +10,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
     {
         public const int AutoReceiveStatic = byte.MaxValue;
 
-        public HipcMetadata Meta;
+        public HipcMetadata    Meta;
         public HipcMessageData Data;
-        public ulong Pid;
+        public ulong           Pid;
 
         public HipcMessage(Span<byte> data)
         {
@@ -20,10 +20,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 
             Header header = MemoryMarshal.Cast<byte, Header>(data)[0];
 
-            data = data.Slice(Unsafe.SizeOf<Header>());
+            data = data[Unsafe.SizeOf<Header>()..];
 
-            int receiveStaticsCount = 0;
-            ulong pid = 0;
+            int   receiveStaticsCount = 0;
+            ulong pid                 = 0;
 
             if (header.ReceiveStaticMode != 0)
             {
@@ -42,46 +42,44 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             if (header.HasSpecialHeader)
             {
                 specialHeader = MemoryMarshal.Cast<byte, SpecialHeader>(data)[0];
-
-                data = data.Slice(Unsafe.SizeOf<SpecialHeader>());
+                data          = data[Unsafe.SizeOf<SpecialHeader>()..];
 
                 if (specialHeader.SendPid)
                 {
-                    pid = MemoryMarshal.Cast<byte, ulong>(data)[0];
-
-                    data = data.Slice(sizeof(ulong));
+                    pid  = MemoryMarshal.Cast<byte, ulong>(data)[0];
+                    data = data[sizeof(ulong)..];
                 }
             }
 
             Meta = new HipcMetadata()
             {
-                Type = (int)header.Type,
-                SendStaticsCount = header.SendStaticsCount,
-                SendBuffersCount = header.SendBuffersCount,
-                ReceiveBuffersCount = header.ReceiveBuffersCount,
+                Type                 = (int)header.Type,
+                SendStaticsCount     = header.SendStaticsCount,
+                SendBuffersCount     = header.SendBuffersCount,
+                ReceiveBuffersCount  = header.ReceiveBuffersCount,
                 ExchangeBuffersCount = header.ExchangeBuffersCount,
-                DataWordsCount = header.DataWordsCount,
-                ReceiveStaticsCount = receiveStaticsCount,
-                SendPid = specialHeader.SendPid,
-                CopyHandlesCount = specialHeader.CopyHandlesCount,
-                MoveHandlesCount = specialHeader.MoveHandlesCount
+                DataWordsCount       = header.DataWordsCount,
+                ReceiveStaticsCount  = receiveStaticsCount,
+                SendPid              = specialHeader.SendPid,
+                CopyHandlesCount     = specialHeader.CopyHandlesCount,
+                MoveHandlesCount     = specialHeader.MoveHandlesCount
             };
 
             Data = CreateMessageData(Meta, data, initialLength);
-            Pid = pid;
+            Pid  = pid;
         }
 
         public static HipcMessageData WriteResponse(
             Span<byte> destination,
-            int sendStaticCount,
-            int dataWordsCount,
-            int copyHandlesCount,
-            int moveHandlesCount)
+            int        sendStaticCount,
+            int        dataWordsCount,
+            int        copyHandlesCount,
+            int        moveHandlesCount)
         {
             return WriteMessage(destination, new HipcMetadata()
             {
                 SendStaticsCount = sendStaticCount,
-                DataWordsCount = dataWordsCount,
+                DataWordsCount   = dataWordsCount,
                 CopyHandlesCount = copyHandlesCount,
                 MoveHandlesCount = moveHandlesCount
             });
@@ -89,38 +87,37 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 
         public static HipcMessageData WriteMessage(Span<byte> destination, HipcMetadata meta)
         {
-            int initialLength = destination.Length;
-
+            int  initialLength    = destination.Length;
             bool hasSpecialHeader = meta.SendPid || meta.CopyHandlesCount != 0 || meta.MoveHandlesCount != 0;
 
             MemoryMarshal.Cast<byte, Header>(destination)[0] = new Header()
             {
-                Type = (CommandType)meta.Type,
-                SendStaticsCount = meta.SendStaticsCount,
-                SendBuffersCount = meta.SendBuffersCount,
-                ReceiveBuffersCount = meta.ReceiveBuffersCount,
+                Type                 = (CommandType)meta.Type,
+                SendStaticsCount     = meta.SendStaticsCount,
+                SendBuffersCount     = meta.SendBuffersCount,
+                ReceiveBuffersCount  = meta.ReceiveBuffersCount,
                 ExchangeBuffersCount = meta.ExchangeBuffersCount,
-                DataWordsCount = meta.DataWordsCount,
-                ReceiveStaticMode = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0,
-                HasSpecialHeader = hasSpecialHeader
+                DataWordsCount       = meta.DataWordsCount,
+                ReceiveStaticMode    = meta.ReceiveStaticsCount != 0 ? (meta.ReceiveStaticsCount != AutoReceiveStatic ? meta.ReceiveStaticsCount + 2 : 2) : 0,
+                HasSpecialHeader     = hasSpecialHeader
             };
 
-            destination = destination.Slice(Unsafe.SizeOf<Header>());
+            destination = destination[Unsafe.SizeOf<Header>()..];
 
             if (hasSpecialHeader)
             {
                 MemoryMarshal.Cast<byte, SpecialHeader>(destination)[0] = new SpecialHeader()
                 {
-                    SendPid = meta.SendPid,
+                    SendPid          = meta.SendPid,
                     CopyHandlesCount = meta.CopyHandlesCount,
                     MoveHandlesCount = meta.MoveHandlesCount
                 };
 
-                destination = destination.Slice(Unsafe.SizeOf<SpecialHeader>());
+                destination = destination[Unsafe.SizeOf<SpecialHeader>()..];
 
                 if (meta.SendPid)
                 {
-                    destination = destination.Slice(sizeof(ulong));
+                    destination = destination[sizeof(ulong)..];
                 }
             }
 
@@ -133,68 +130,67 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 
             if (meta.CopyHandlesCount != 0)
             {
-                copyHandles = MemoryMarshal.Cast<byte, int>(data).Slice(0, meta.CopyHandlesCount);
+                copyHandles = MemoryMarshal.Cast<byte, int>(data)[..meta.CopyHandlesCount];
 
-                data = data.Slice(meta.CopyHandlesCount * sizeof(int));
+                data = data[(meta.CopyHandlesCount * sizeof(int))..];
             }
 
             Span<int> moveHandles = Span<int>.Empty;
 
             if (meta.MoveHandlesCount != 0)
             {
-                moveHandles = MemoryMarshal.Cast<byte, int>(data).Slice(0, meta.MoveHandlesCount);
+                moveHandles = MemoryMarshal.Cast<byte, int>(data)[..meta.MoveHandlesCount];
 
-                data = data.Slice(meta.MoveHandlesCount * sizeof(int));
+                data = data[(meta.MoveHandlesCount * sizeof(int))..];
             }
 
             Span<HipcStaticDescriptor> sendStatics = Span<HipcStaticDescriptor>.Empty;
 
             if (meta.SendStaticsCount != 0)
             {
-                sendStatics = MemoryMarshal.Cast<byte, HipcStaticDescriptor>(data).Slice(0, meta.SendStaticsCount);
+                sendStatics = MemoryMarshal.Cast<byte, HipcStaticDescriptor>(data)[..meta.SendStaticsCount];
 
-                data = data.Slice(meta.SendStaticsCount * Unsafe.SizeOf<HipcStaticDescriptor>());
+                data = data[(meta.SendStaticsCount * Unsafe.SizeOf<HipcStaticDescriptor>())..];
             }
 
             Span<HipcBufferDescriptor> sendBuffers = Span<HipcBufferDescriptor>.Empty;
 
             if (meta.SendBuffersCount != 0)
             {
-                sendBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.SendBuffersCount);
+                sendBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data)[..meta.SendBuffersCount];
 
-                data = data.Slice(meta.SendBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
+                data = data[(meta.SendBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>())..];
             }
 
             Span<HipcBufferDescriptor> receiveBuffers = Span<HipcBufferDescriptor>.Empty;
 
             if (meta.ReceiveBuffersCount != 0)
             {
-                receiveBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.ReceiveBuffersCount);
+                receiveBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data)[..meta.ReceiveBuffersCount];
 
-                data = data.Slice(meta.ReceiveBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
+                data = data[(meta.ReceiveBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>())..];
             }
 
             Span<HipcBufferDescriptor> exchangeBuffers = Span<HipcBufferDescriptor>.Empty;
 
             if (meta.ExchangeBuffersCount != 0)
             {
-                exchangeBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data).Slice(0, meta.ExchangeBuffersCount);
+                exchangeBuffers = MemoryMarshal.Cast<byte, HipcBufferDescriptor>(data)[..meta.ExchangeBuffersCount];
 
-                data = data.Slice(meta.ExchangeBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>());
+                data = data[(meta.ExchangeBuffersCount * Unsafe.SizeOf<HipcBufferDescriptor>())..];
             }
 
             Span<uint> dataWords = Span<uint>.Empty;
 
             if (meta.DataWordsCount != 0)
             {
-                int dataOffset = initialLength - data.Length;
+                int dataOffset        = initialLength - data.Length;
                 int dataOffsetAligned = BitUtils.AlignUp(dataOffset, 0x10);
+                int padding           = (dataOffsetAligned - dataOffset) / sizeof(uint);
 
-                int padding = (dataOffsetAligned - dataOffset) / sizeof(uint);
+                dataWords = MemoryMarshal.Cast<byte, uint>(data)[padding..meta.DataWordsCount];
 
-                dataWords = MemoryMarshal.Cast<byte, uint>(data).Slice(padding, meta.DataWordsCount - padding);
-
-                data = data.Slice(meta.DataWordsCount * sizeof(uint));
+                data = data[(meta.DataWordsCount * sizeof(uint))..];
             }
 
             Span<HipcReceiveListEntry> receiveList = Span<HipcReceiveListEntry>.Empty;
@@ -203,19 +199,19 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             {
                 int receiveListSize = meta.ReceiveStaticsCount == AutoReceiveStatic ? 1 : meta.ReceiveStaticsCount;
 
-                receiveList = MemoryMarshal.Cast<byte, HipcReceiveListEntry>(data).Slice(0, receiveListSize);
+                receiveList = MemoryMarshal.Cast<byte, HipcReceiveListEntry>(data)[..receiveListSize];
             }
 
             return new HipcMessageData()
             {
-                SendStatics = sendStatics,
-                SendBuffers = sendBuffers,
-                ReceiveBuffers = receiveBuffers,
+                SendStatics     = sendStatics,
+                SendBuffers     = sendBuffers,
+                ReceiveBuffers  = receiveBuffers,
                 ExchangeBuffers = exchangeBuffers,
-                DataWords = dataWords,
-                ReceiveList = receiveList,
-                CopyHandles = copyHandles,
-                MoveHandles = moveHandles
+                DataWords       = dataWords,
+                ReceiveList     = receiveList,
+                CopyHandles     = copyHandles,
+                MoveHandles     = moveHandles
             };
         }
     }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs
index c83c422cab..154b8f079b 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMessageData.cs
@@ -8,9 +8,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
         public Span<HipcBufferDescriptor> SendBuffers;
         public Span<HipcBufferDescriptor> ReceiveBuffers;
         public Span<HipcBufferDescriptor> ExchangeBuffers;
-        public Span<uint> DataWords;
+        public Span<uint>                 DataWords;
         public Span<HipcReceiveListEntry> ReceiveList;
-        public Span<int> CopyHandles;
-        public Span<int> MoveHandles;
+        public Span<int>                  CopyHandles;
+        public Span<int>                  MoveHandles;
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs
index fe13137a69..10abc40060 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcMetadata.cs
@@ -2,15 +2,15 @@
 {
     struct HipcMetadata
     {
-        public int Type;
-        public int SendStaticsCount;
-        public int SendBuffersCount;
-        public int ReceiveBuffersCount;
-        public int ExchangeBuffersCount;
-        public int DataWordsCount;
-        public int ReceiveStaticsCount;
+        public int  Type;
+        public int  SendStaticsCount;
+        public int  SendBuffersCount;
+        public int  ReceiveBuffersCount;
+        public int  ExchangeBuffersCount;
+        public int  DataWordsCount;
+        public int  ReceiveStaticsCount;
         public bool SendPid;
-        public int CopyHandlesCount;
-        public int MoveHandlesCount;
+        public int  CopyHandlesCount;
+        public int  MoveHandlesCount;
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs
index 94bf096857..56cf16fb02 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcReceiveListEntry.cs
@@ -8,7 +8,7 @@
         public HipcReceiveListEntry(ulong address, ulong size)
         {
             _addressLow = (uint)address;
-            _word1 = (ushort)(address >> 32) | (uint)(size << 16);
+            _word1      = (ushort)(address >> 32) | (uint)(size << 16);
         }
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs
index ef989a9866..3b483be81e 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcResult.cs
@@ -6,17 +6,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
     {
         public const int ModuleId = 11;
 
-        public static Result OutOfSessionMemory => new Result(ModuleId, 102);
-        public static Result OutOfSessions => new Result(ModuleId, 131);
-        public static Result PointerBufferTooSmall => new Result(ModuleId, 141);
-        public static Result OutOfDomains => new Result(ModuleId, 200);
-
-        public static Result InvalidRequestSize => new Result(ModuleId, 402);
-        public static Result UnknownCommandType => new Result(ModuleId, 403);
-
-        public static Result InvalidCmifRequest => new Result(ModuleId, 420);
-
-        public static Result TargetNotDomain => new Result(ModuleId, 491);
-        public static Result DomainObjectNotFound => new Result(ModuleId, 492);
+        public static Result OutOfSessionMemory    => new(ModuleId, 102);
+        public static Result OutOfSessions         => new(ModuleId, 131);
+        public static Result PointerBufferTooSmall => new(ModuleId, 141);
+        public static Result OutOfDomains          => new(ModuleId, 200);
+        public static Result InvalidRequestSize    => new(ModuleId, 402);
+        public static Result UnknownCommandType    => new(ModuleId, 403);
+        public static Result InvalidCmifRequest    => new(ModuleId, 420);
+        public static Result TargetNotDomain       => new(ModuleId, 491);
+        public static Result DomainObjectNotFound  => new(ModuleId, 492);
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs
index 5cebf47c8c..103820a6b9 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/HipcStaticDescriptor.cs
@@ -4,9 +4,9 @@
     {
         private readonly ulong _data;
 
-        public ulong Address => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32);
-        public ushort Size => (ushort)(_data >> 16);
-        public int ReceiveIndex => (int)(_data & 0xf);
+        public ulong  Address      => ((((_data >> 2) & 0x70) | ((_data >> 12) & 0xf)) << 32) | (_data >> 32);
+        public ushort Size         => (ushort)(_data >> 16);
+        public int    ReceiveIndex => (int)(_data & 0xf);
 
         public HipcStaticDescriptor(ulong address, ushort size, int receiveIndex)
         {
@@ -19,4 +19,4 @@
             _data = data;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs
index e087cb2235..b99d63c5ec 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ManagerOptions.cs
@@ -2,19 +2,19 @@
 {
     struct ManagerOptions
     {
-        public static ManagerOptions Default => new ManagerOptions(0, 0, 0, false);
+        public static ManagerOptions Default => new(0, 0, 0, false);
 
-        public int PointerBufferSize { get; }
-        public int MaxDomains { get; }
-        public int MaxDomainObjects { get; }
+        public int PointerBufferSize      { get; }
+        public int MaxDomains             { get; }
+        public int MaxDomainObjects       { get; }
         public bool CanDeferInvokeRequest { get; }
 
         public ManagerOptions(int pointerBufferSize, int maxDomains, int maxDomainObjects, bool canDeferInvokeRequest)
         {
-            PointerBufferSize = pointerBufferSize;
-            MaxDomains = maxDomains;
-            MaxDomainObjects = maxDomainObjects;
+            PointerBufferSize     = pointerBufferSize;
+            MaxDomains            = maxDomains;
+            MaxDomainObjects      = maxDomainObjects;
             CanDeferInvokeRequest = canDeferInvokeRequest;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs
index 923f2d52d9..bbbab89855 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/Server.cs
@@ -6,22 +6,22 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 {
     class Server : MultiWaitHolderOfHandle
     {
-        public int PortIndex { get; }
-        public int PortHandle { get; }
-        public ServiceName Name { get; }
-        public bool Managed { get; }
+        public int                 PortIndex    { get; }
+        public int                 PortHandle   { get; }
+        public ServiceName         Name         { get; }
+        public bool                Managed      { get; }
         public ServiceObjectHolder StaticObject { get; }
 
         public Server(
-            int portIndex,
-            int portHandle,
-            ServiceName name,
-            bool managed,
+            int                 portIndex,
+            int                 portHandle,
+            ServiceName         name,
+            bool                managed,
             ServiceObjectHolder staticHoder) : base(portHandle)
         {
             PortHandle = portHandle;
-            Name = name;
-            Managed = managed;
+            Name       = name;
+            Managed    = managed;
 
             if (staticHoder != null)
             {
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs
index d920a65917..dda7753971 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerDomainSessionManager.cs
@@ -15,7 +15,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 
         protected override Result DispatchManagerRequest(ServerSession session, Span<byte> inMessage, Span<byte> outMessage)
         {
-            HipcManager hipcManager = new HipcManager(this, session);
+            HipcManager hipcManager = new(this, session);
 
             return DispatchRequest(new ServiceObjectHolder(hipcManager), session, inMessage, outMessage);
         }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs
index 5bb2de25dd..2ca9ceea2f 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs
@@ -80,7 +80,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
                     return null;
                 }
 
-                ServerSession session = new ServerSession(sessionIndex, sessionHandle, obj);
+                ServerSession session = new(sessionIndex, sessionHandle, obj);
 
                 _sessions.Add(session);
 
@@ -111,7 +111,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
         {
             lock (_resourceLock)
             {
-                Server server = new Server(portIndex, portHandle, name, managed, staticHoder);
+                Server server = new(portIndex, portHandle, name, managed, staticHoder);
 
                 _servers.Add(server);
 
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs
index 68cae6bc4d..9d21290d82 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs
@@ -26,7 +26,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 
         private enum UserDataTag
         {
-            Server = 1,
+            Server  = 1,
             Session = 2
         }
 
@@ -36,16 +36,17 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             _canDeferInvokeRequest = options.CanDeferInvokeRequest;
 
             _multiWait = new MultiWait();
-            _waitList = new MultiWait();
+            _waitList  = new MultiWait();
 
             _multiWaitSelectionLock = new object();
-            _waitListLock = new object();
+            _waitListLock           = new object();
 
             _requestStopEvent = new Event(EventClearMode.ManualClear);
-            _notifyEvent = new Event(EventClearMode.ManualClear);
+            _notifyEvent      = new Event(EventClearMode.ManualClear);
 
             _requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent);
             _multiWait.LinkMultiWaitHolder(_requestStopEventHolder);
+
             _notifyEventHolder = new MultiWaitHolderOfEvent(_notifyEvent);
             _multiWait.LinkMultiWaitHolder(_notifyEventHolder);
         }
@@ -73,6 +74,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
         private void RegisterServerImpl(int portIndex, ServiceObjectHolder staticHolder, int portHandle)
         {
             Server server = AllocateServer(portIndex, portHandle, ServiceName.Invalid, managed: false, staticHolder);
+
             RegisterServerImpl(server);
         }
 
@@ -86,6 +88,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             }
 
             Server server = AllocateServer(portIndex, portHandle, name, managed: true, staticHolder);
+
             RegisterServerImpl(server);
 
             return Result.Success;
@@ -103,6 +106,11 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             throw new NotSupportedException();
         }
 
+        protected Result AcceptImpl(Server server, IServiceObject obj)
+        {
+            return AcceptSession(server.PortHandle, new ServiceObjectHolder(obj));
+        }
+
         public void ServiceRequests()
         {
             while (WaitAndProcessRequestsImpl());
@@ -175,7 +183,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
         protected override void RegisterSessionToWaitList(ServerSession session)
         {
             session.HasReceived = false;
-            session.UserData = UserDataTag.Session;
+            session.UserData    = UserDataTag.Session;
+
             RegisterToWaitList(session);
         }
 
@@ -198,15 +207,12 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 
         private Result Process(MultiWaitHolder holder)
         {
-            switch ((UserDataTag)holder.UserData)
+            return (UserDataTag)holder.UserData switch
             {
-                case UserDataTag.Server:
-                    return ProcessForServer(holder);
-                case UserDataTag.Session:
-                    return ProcessForSession(holder);
-                default:
-                    throw new NotImplementedException(((UserDataTag)holder.UserData).ToString());
-            }
+                UserDataTag.Server  => ProcessForServer(holder),
+                UserDataTag.Session => ProcessForSession(holder),
+                _                   => throw new NotImplementedException(((UserDataTag)holder.UserData).ToString())
+            };
         }
 
         private Result ProcessForServer(MultiWaitHolder holder)
@@ -259,6 +265,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
                     }
 
                     session.HasReceived = true;
+
                     tlsMessage.Memory.Span.CopyTo(savedMessage.Memory.Span);
                 }
                 else
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs
index eb98fefd0e..a17300823c 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSession.cs
@@ -6,18 +6,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
     class ServerSession : MultiWaitHolderOfHandle
     {
         public ServiceObjectHolder ServiceObjectHolder { get; set; }
-        public PointerAndSize PointerBuffer { get; set; }
-        public PointerAndSize SavedMessage { get; set; }
-        public int SessionIndex { get; }
-        public int SessionHandle { get; }
-        public bool IsClosed { get; set; }
-        public bool HasReceived { get; set; }
+        public PointerAndSize      PointerBuffer       { get; set; }
+        public PointerAndSize      SavedMessage        { get; set; }
+        public int                 SessionIndex        { get; }
+        public int                 SessionHandle       { get; }
+        public bool                IsClosed            { get; set; }
+        public bool                HasReceived         { get; set; }
 
         public ServerSession(int index, int handle, ServiceObjectHolder obj) : base(handle)
         {
             ServiceObjectHolder = obj;
-            SessionIndex = index;
-            SessionHandle = handle;
+            SessionIndex        = index;
+            SessionHandle       = handle;
         }
     }
 }
diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs
index e85892f26d..6d39508130 100644
--- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerSessionManager.cs
@@ -75,9 +75,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             }
 
             session.PointerBuffer = GetSessionPointerBuffer(session);
-            session.SavedMessage = GetSessionSavedMessageBuffer(session);
+            session.SavedMessage  = GetSessionSavedMessageBuffer(session);
 
             RegisterSessionToWaitList(session);
+
             return Result.Success;
         }
 
@@ -109,10 +110,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
         }
 
         protected virtual Server AllocateServer(
-            int portIndex,
-            int portHandle,
-            ServiceName name,
-            bool managed,
+            int                 portIndex,
+            int                 portHandle,
+            ServiceName         name,
+            bool                managed,
             ServiceObjectHolder staticHoder)
         {
             throw new NotSupportedException();
@@ -141,6 +142,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
         protected void CloseSessionImpl(ServerSession session)
         {
             int sessionHandle = session.Handle;
+
             Os.FinalizeMultiWaitHolder(session);
             DestroySession(session);
             HorizonStatic.Syscall.CloseHandle(sessionHandle).AbortOnFailure();
@@ -156,6 +158,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             if (session.IsClosed || GetCmifCommandType(message) == CommandType.Close)
             {
                 CloseSessionImpl(session);
+
                 return Result.Success;
             }
             else
@@ -165,6 +168,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
                 if (result.IsSuccess)
                 {
                     RegisterSessionToWaitList(session);
+
                     return Result.Success;
                 }
                 else if (SfResult.RequestContextChanged(result))
@@ -176,6 +180,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
                     Logger.Warning?.Print(LogClass.KernelIpc, $"Request processing returned error {result}");
 
                     CloseSessionImpl(session);
+
                     return Result.Success;
                 }
             }
@@ -197,8 +202,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
                     return DispatchManagerRequest(session, inMessage, outMessage);
                 default:
                     return HipcResult.UnknownCommandType;
-            }
         }
+    }
 
         private static int GetInlineContext(CommandType commandType, ReadOnlySpan<byte> inMessage)
         {
@@ -231,7 +236,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
                 {
                     HipcMessageData messageData = HipcMessage.WriteMessage(message, new HipcMetadata()
                     {
-                        Type = (int)CommandType.Invalid,
+                        Type                = (int)CommandType.Invalid,
                         ReceiveStaticsCount = HipcMessage.AutoReceiveStatic
                     });
 
@@ -271,9 +276,9 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 
         protected virtual Result DispatchRequest(
             ServiceObjectHolder objectHolder,
-            ServerSession session,
-            Span<byte> inMessage,
-            Span<byte> outMessage)
+            ServerSession       session,
+            Span<byte>          inMessage,
+            Span<byte>          outMessage)
         {
             HipcMessage request;
 
@@ -288,14 +293,14 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
 
             var dispatchCtx = new ServiceDispatchContext()
             {
-                ServiceObject = objectHolder.ServiceObject,
-                Manager = this,
-                Session = session,
-                HandlesToClose = new HandlesToClose(),
-                PointerBuffer = session.PointerBuffer,
-                InMessageBuffer = inMessage,
+                ServiceObject    = objectHolder.ServiceObject,
+                Manager          = this,
+                Session          = session,
+                HandlesToClose   = new HandlesToClose(),
+                PointerBuffer    = session.PointerBuffer,
+                InMessageBuffer  = inMessage,
                 OutMessageBuffer = outMessage,
-                Request = request
+                Request          = request
             };
 
             ReadOnlySpan<byte> inRawData = MemoryMarshal.Cast<uint, byte>(dispatchCtx.Request.Data.DataWords);
@@ -332,4 +337,4 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
             return this;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs
index 53202edeeb..6bba49ae03 100644
--- a/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/HipcCommandProcessor.cs
@@ -136,9 +136,9 @@ namespace Ryujinx.Horizon.Sdk.Sf
             ulong pointerBufferTail = context.PointerBuffer.Address;
             ulong pointerBufferHead = pointerBufferTail + context.PointerBuffer.Size;
 
-            int sendMapAliasIndex = 0;
-            int recvMapAliasIndex = 0;
-            int sendPointerIndex = 0;
+            int sendMapAliasIndex       = 0;
+            int recvMapAliasIndex       = 0;
+            int sendPointerIndex        = 0;
             int unfixedRecvPointerIndex = 0;
 
             for (int i = 0; i < _args.Length; i++)
@@ -188,8 +188,9 @@ namespace Ryujinx.Horizon.Sdk.Sf
                     if (flags.HasFlag(HipcBufferFlags.In))
                     {
                         var descriptor = context.Request.Data.SendStatics[sendPointerIndex++];
-                        ulong address = descriptor.Address;
-                        ulong size = descriptor.Size;
+                        ulong address  = descriptor.Address;
+                        ulong size     = descriptor.Size;
+
                         _bufferRanges[i] = new PointerAndSize(address, size);
 
                         if (size != 0)
@@ -207,13 +208,14 @@ namespace Ryujinx.Horizon.Sdk.Sf
                         }
                         else
                         {
-                            var data = MemoryMarshal.Cast<uint, byte>(context.Request.Data.DataWords);
-                            var recvPointerSizes = MemoryMarshal.Cast<byte, ushort>(data.Slice(runtimeMetadata.UnfixedOutPointerSizeOffset));
+                            var data             = MemoryMarshal.Cast<uint, byte>(context.Request.Data.DataWords);
+                            var recvPointerSizes = MemoryMarshal.Cast<byte, ushort>(data[runtimeMetadata.UnfixedOutPointerSizeOffset..]);
+
                             size = recvPointerSizes[unfixedRecvPointerIndex++];
                         }
 
                         pointerBufferHead = BitUtils.AlignDown(pointerBufferHead - size, 0x10UL);
-                        _bufferRanges[i] = new PointerAndSize(pointerBufferHead, size);
+                        _bufferRanges[i]  = new PointerAndSize(pointerBufferHead, size);
                     }
                 }
             }
@@ -304,16 +306,17 @@ namespace Ryujinx.Horizon.Sdk.Sf
         {
             ref var meta = ref context.Request.Meta;
             bool requestValid = true;
-            requestValid &= meta.SendPid == _hasInProcessIdHolder;
-            requestValid &= meta.SendStaticsCount == _inPointerBuffersCount;
-            requestValid &= meta.SendBuffersCount == _inMapAliasBuffersCount;
-            requestValid &= meta.ReceiveBuffersCount == _outMapAliasBuffersCount;
+            requestValid &= meta.SendPid              == _hasInProcessIdHolder;
+            requestValid &= meta.SendStaticsCount     == _inPointerBuffersCount;
+            requestValid &= meta.SendBuffersCount     == _inMapAliasBuffersCount;
+            requestValid &= meta.ReceiveBuffersCount  == _outMapAliasBuffersCount;
             requestValid &= meta.ExchangeBuffersCount == 0;
-            requestValid &= meta.CopyHandlesCount == _inCopyHandlesCount;
-            requestValid &= meta.MoveHandlesCount == _inMoveHandlesCount;
+            requestValid &= meta.CopyHandlesCount     == _inCopyHandlesCount;
+            requestValid &= meta.MoveHandlesCount     == _inMoveHandlesCount;
 
             int rawSizeInBytes = meta.DataWordsCount * sizeof(uint);
             int commandRawSize = BitUtils.AlignUp(runtimeMetadata.UnfixedOutPointerSizeOffset + (OutUnfixedSizePointerBuffersCount * sizeof(ushort)), sizeof(uint));
+
             requestValid &= rawSizeInBytes >= commandRawSize;
 
             return requestValid ? Result.Success : HipcResult.InvalidCmifRequest;
@@ -340,7 +343,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
             {
                 if (_args[i].Type == CommandArgType.InObject)
                 {
-                    int index = inObjectIndex++;
+                    int index    = inObjectIndex++;
                     var inObject = inObjects[index];
 
                     objects[index] = inObject?.ServiceObject;
@@ -365,6 +368,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
                 _outCopyHandlesCount,
                 _outMoveHandlesCount + runtimeMetadata.OutObjectsCount);
             outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
+
             return response;
         }
 
@@ -377,6 +381,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
                 (BitUtils.AlignUp(rawDataSize, 4) + 0x10) / sizeof(uint),
                 0,
                 0);
+
             outRawData = MemoryMarshal.Cast<uint, byte>(response.DataWords);
         }
 
@@ -410,6 +415,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
             if (obj == null)
             {
                 response.MoveHandles[index] = 0;
+
                 return;
             }
 
@@ -418,4 +424,4 @@ namespace Ryujinx.Horizon.Sdk.Sf
             response.MoveHandles[index] = clientHandle;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs b/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs
index 982f454f01..10e4f90942 100644
--- a/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/RawDataOffsetCalculator.cs
@@ -12,24 +12,22 @@ namespace Ryujinx.Horizon.Sdk.Sf
             {
                 int argsCount = args.Length;
 
-                int[] sizes = new int[argsCount];
+                int[] sizes  = new int[argsCount];
                 int[] aligns = new int[argsCount];
-                int[] map = new int[argsCount];
+                int[] map    = new int[argsCount];
 
                 for (int i = 0; i < argsCount; i++)
                 {
-                    sizes[i] = args[i].ArgSize;
+                    sizes[i]  = args[i].ArgSize;
                     aligns[i] = args[i].ArgAlignment;
-                    map[i] = i;
+                    map[i]    = i;
                 }
 
                 for (int i = 1; i < argsCount; i++)
                 {
                     for (int j = i; j > 0 && aligns[map[j - 1]] > aligns[map[j]]; j--)
                     {
-                        var temp = map[j - 1];
-                        map[j - 1] = map[j];
-                        map[j] = temp;
+                        (map[j], map[j - 1]) = (map[j - 1], map[j]);
                     }
                 }
 
@@ -37,9 +35,9 @@ namespace Ryujinx.Horizon.Sdk.Sf
 
                 foreach (int i in map)
                 {
-                    offset = BitUtils.AlignUp(offset, aligns[i]);
+                    offset     = BitUtils.AlignUp(offset, aligns[i]);
                     offsets[i] = offset;
-                    offset += sizes[i];
+                    offset    += sizes[i];
                 }
 
                 offsets[argsCount] = offset;
@@ -48,4 +46,4 @@ namespace Ryujinx.Horizon.Sdk.Sf
             return offsets;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sf/SfResult.cs b/Ryujinx.Horizon/Sdk/Sf/SfResult.cs
index 6aa11ba5fd..72502d17e1 100644
--- a/Ryujinx.Horizon/Sdk/Sf/SfResult.cs
+++ b/Ryujinx.Horizon/Sdk/Sf/SfResult.cs
@@ -6,26 +6,22 @@ namespace Ryujinx.Horizon.Sdk.Sf
     {
         public const int ModuleId = 10;
 
-        public static Result NotSupported => new Result(ModuleId, 1);
-        public static Result InvalidHeaderSize => new Result(ModuleId, 202);
-        public static Result InvalidInHeader => new Result(ModuleId, 211);
-        public static Result InvalidOutHeader => new Result(ModuleId, 212);
-        public static Result UnknownCommandId => new Result(ModuleId, 221);
-        public static Result InvalidOutRawSize => new Result(ModuleId, 232);
-        public static Result InvalidInObjectsCount => new Result(ModuleId, 235);
-        public static Result InvalidOutObjectsCount => new Result(ModuleId, 236);
-        public static Result InvalidInObject => new Result(ModuleId, 239);
-
-        public static Result TargetNotFound => new Result(ModuleId, 261);
-
-        public static Result OutOfDomainEntries => new Result(ModuleId, 301);
-
-        public static Result InvalidatedByUser => new Result(ModuleId, 802);
-        public static Result RequestDeferredByUser => new Result(ModuleId, 812);
+        public static Result NotSupported           => new(ModuleId, 1);
+        public static Result InvalidHeaderSize      => new(ModuleId, 202);
+        public static Result InvalidInHeader        => new(ModuleId, 211);
+        public static Result InvalidOutHeader       => new(ModuleId, 212);
+        public static Result UnknownCommandId       => new(ModuleId, 221);
+        public static Result InvalidOutRawSize      => new(ModuleId, 232);
+        public static Result InvalidInObjectsCount  => new(ModuleId, 235);
+        public static Result InvalidOutObjectsCount => new(ModuleId, 236);
+        public static Result InvalidInObject        => new(ModuleId, 239);
+        public static Result TargetNotFound         => new(ModuleId, 261);
+        public static Result OutOfDomainEntries     => new(ModuleId, 301);
+        public static Result InvalidatedByUser      => new(ModuleId, 802);
+        public static Result RequestDeferredByUser  => new(ModuleId, 812);
 
         public static bool RequestContextChanged(Result result) => result.InRange(800, 899);
-        public static bool Invalidated(Result result) => result.InRange(801, 809);
-
-        public static bool RequestDeferred(Result result) => result.InRange(811, 819);
+        public static bool Invalidated(Result result)           => result.InRange(801, 809);
+        public static bool RequestDeferred(Result result)       => result.InRange(811, 819);
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs b/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs
new file mode 100644
index 0000000000..644285834a
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Sm/IManagerService.cs
@@ -0,0 +1,8 @@
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Sdk.Sm
+{
+    interface IManagerService : IServiceObject
+    {
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sm/IUserService.cs b/Ryujinx.Horizon/Sdk/Sm/IUserService.cs
new file mode 100644
index 0000000000..ad9bc9d7bf
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Sm/IUserService.cs
@@ -0,0 +1,13 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Sdk.Sm
+{
+    interface IUserService : IServiceObject
+    {
+        Result Initialize(ulong clientProcessId);
+        Result GetService(out int handle, ServiceName name);
+        Result RegisterService(out int handle, ServiceName name, int maxSessions, bool isLight);
+        Result UnregisterService(ServiceName name);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Sm/SmApi.cs b/Ryujinx.Horizon/Sdk/Sm/SmApi.cs
index e4b0eea1cd..533e68d9c5 100644
--- a/Ryujinx.Horizon/Sdk/Sm/SmApi.cs
+++ b/Ryujinx.Horizon/Sdk/Sm/SmApi.cs
@@ -7,16 +7,18 @@ namespace Ryujinx.Horizon.Sdk.Sm
 {
     class SmApi
     {
+        private const string SmName = "sm:";
+
         private int _portHandle;
 
         public Result Initialize()
         {
-            Result result = HorizonStatic.Syscall.ConnectToNamedPort(out int portHandle, "sm:");
+            Result result = HorizonStatic.Syscall.ConnectToNamedPort(out int portHandle, SmName);
 
             while (result == KernelResult.NotFound)
             {
                 HorizonStatic.Syscall.SleepThread(50000000L);
-                result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, "sm:");
+                result = HorizonStatic.Syscall.ConnectToNamedPort(out portHandle, SmName);
             }
 
             if (result.IsFailure)
@@ -33,7 +35,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
         {
             Span<byte> data = stackalloc byte[8];
 
-            SpanWriter writer = new SpanWriter(data);
+            SpanWriter writer = new(data);
 
             writer.Write(0UL);
 
@@ -44,7 +46,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
         {
             Span<byte> data = stackalloc byte[8];
 
-            SpanWriter writer = new SpanWriter(data);
+            SpanWriter writer = new(data);
 
             writer.Write(name);
 
@@ -53,10 +55,12 @@ namespace Ryujinx.Horizon.Sdk.Sm
             if (result.IsFailure)
             {
                 handle = 0;
+
                 return result;
             }
 
             handle = response.MoveHandles[0];
+
             return Result.Success;
         }
 
@@ -64,7 +68,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
         {
             Span<byte> data = stackalloc byte[16];
 
-            SpanWriter writer = new SpanWriter(data);
+            SpanWriter writer = new(data);
 
             writer.Write(name);
             writer.Write(isLight ? 1 : 0);
@@ -75,10 +79,12 @@ namespace Ryujinx.Horizon.Sdk.Sm
             if (result.IsFailure)
             {
                 handle = 0;
+
                 return result;
             }
 
             handle = response.MoveHandles[0];
+
             return Result.Success;
         }
 
@@ -86,7 +92,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
         {
             Span<byte> data = stackalloc byte[8];
 
-            SpanWriter writer = new SpanWriter(data);
+            SpanWriter writer = new(data);
 
             writer.Write(name);
 
@@ -97,11 +103,11 @@ namespace Ryujinx.Horizon.Sdk.Sm
         {
             Span<byte> data = stackalloc byte[8];
 
-            SpanWriter writer = new SpanWriter(data);
+            SpanWriter writer = new(data);
 
             writer.Write(0UL);
 
             return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/ServiceEntry.cs b/Ryujinx.Horizon/ServiceEntry.cs
index 3fea46c193..06152d9ff3 100644
--- a/Ryujinx.Horizon/ServiceEntry.cs
+++ b/Ryujinx.Horizon/ServiceEntry.cs
@@ -6,20 +6,22 @@ namespace Ryujinx.Horizon
 {
     public struct ServiceEntry
     {
-        private readonly Action _entrypoint;
-        private readonly HorizonOptions _options;
+        private readonly Action<ServiceTable> _entrypoint;
+        private readonly ServiceTable         _serviceTable;
+        private readonly HorizonOptions       _options;
 
-        internal ServiceEntry(Action entrypoint, HorizonOptions options)
+        internal ServiceEntry(Action<ServiceTable> entrypoint, ServiceTable serviceTable, HorizonOptions options)
         {
-            _entrypoint = entrypoint;
-            _options = options;
+            _entrypoint   = entrypoint;
+            _serviceTable = serviceTable;
+            _options      = options;
         }
 
         public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext)
         {
             HorizonStatic.Register(_options, syscallApi, addressSpace, threadContext, (int)threadContext.GetX(1));
 
-            _entrypoint();
+            _entrypoint(_serviceTable);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/ServiceTable.cs b/Ryujinx.Horizon/ServiceTable.cs
index 933b6a59b6..2edc6ea106 100644
--- a/Ryujinx.Horizon/ServiceTable.cs
+++ b/Ryujinx.Horizon/ServiceTable.cs
@@ -1,22 +1,58 @@
 using Ryujinx.Horizon.LogManager;
+using Ryujinx.Horizon.Prepo;
 using System.Collections.Generic;
+using System.Threading;
 
 namespace Ryujinx.Horizon
 {
-    public static class ServiceTable
+    public class ServiceTable
     {
-        public static IEnumerable<ServiceEntry> GetServices(HorizonOptions options)
+        private int _readyServices;
+        private int _totalServices;
+
+        private readonly ManualResetEvent _servicesReadyEvent = new(false);
+
+        public IEnumerable<ServiceEntry> GetServices(HorizonOptions options)
         {
-            List<ServiceEntry> entries = new List<ServiceEntry>();
+            List<ServiceEntry> entries = new();
 
             void RegisterService<T>() where T : IService
             {
-                entries.Add(new ServiceEntry(T.Main, options));
+                entries.Add(new ServiceEntry(T.Main, this, options));
             }
 
             RegisterService<LmMain>();
+            RegisterService<PrepoMain>();
+
+            _totalServices = entries.Count;
 
             return entries;
         }
+
+        internal void SignalServiceReady()
+        {
+            if (Interlocked.Increment(ref _readyServices) == _totalServices)
+            {
+                _servicesReadyEvent.Set();
+            }
+        }
+
+        public void WaitServicesReady()
+        {
+            _servicesReadyEvent.WaitOne();
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                _servicesReadyEvent.Dispose();
+            }
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs
index fed420aa85..50c18a2c97 100644
--- a/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs
+++ b/Ryujinx.Horizon/Sm/Impl/ServiceInfo.cs
@@ -5,16 +5,16 @@ namespace Ryujinx.Horizon.Sm.Impl
     struct ServiceInfo
     {
         public ServiceName Name;
-        public ulong OwnerProcessId;
-        public int PortHandle;
+        public ulong       OwnerProcessId;
+        public int         PortHandle;
 
         public void Free()
         {
             HorizonStatic.Syscall.CloseHandle(PortHandle);
 
-            Name = ServiceName.Invalid;
+            Name           = ServiceName.Invalid;
             OwnerProcessId = 0L;
-            PortHandle = 0;
+            PortHandle     = 0;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs
index cdf2d17f1b..44a1ec46e4 100644
--- a/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs
+++ b/Ryujinx.Horizon/Sm/Impl/ServiceManager.cs
@@ -107,8 +107,8 @@ namespace Ryujinx.Horizon.Sm.Impl
                 return result;
             }
 
-            freeService.PortHandle = clientPort;
-            freeService.Name = name;
+            freeService.PortHandle     = clientPort;
+            freeService.Name           = name;
             freeService.OwnerProcessId = processId;
 
             return Result.Success;
@@ -126,20 +126,19 @@ namespace Ryujinx.Horizon.Sm.Impl
             // TODO: Validation with GetProcessInfo etc.
 
             int serviceIndex = GetServiceInfo(name);
-
             if (serviceIndex < 0)
             {
                 return SmResult.NotRegistered;
             }
 
             ref var serviceInfo = ref _services[serviceIndex];
-
             if (serviceInfo.OwnerProcessId != processId)
             {
                 return SmResult.NotAllowed;
             }
 
             serviceInfo.Free();
+
             return Result.Success;
         }
 
@@ -194,4 +193,4 @@ namespace Ryujinx.Horizon.Sm.Impl
             return -1;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs b/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs
new file mode 100644
index 0000000000..c7dcddc912
--- /dev/null
+++ b/Ryujinx.Horizon/Sm/Ipc/ManagerService.cs
@@ -0,0 +1,8 @@
+using Ryujinx.Horizon.Sdk.Sm;
+
+namespace Ryujinx.Horizon.Sm.Ipc
+{
+    partial class ManagerService : IManagerService
+    {
+    }
+}
diff --git a/Ryujinx.Horizon/Sm/UserService.cs b/Ryujinx.Horizon/Sm/Ipc/UserService.cs
similarity index 91%
rename from Ryujinx.Horizon/Sm/UserService.cs
rename to Ryujinx.Horizon/Sm/Ipc/UserService.cs
index d3b4537bf9..d093913a98 100644
--- a/Ryujinx.Horizon/Sm/UserService.cs
+++ b/Ryujinx.Horizon/Sm/Ipc/UserService.cs
@@ -3,14 +3,14 @@ using Ryujinx.Horizon.Sdk.Sf;
 using Ryujinx.Horizon.Sdk.Sm;
 using Ryujinx.Horizon.Sm.Impl;
 
-namespace Ryujinx.Horizon.Sm
+namespace Ryujinx.Horizon.Sm.Ipc
 {
-    partial class UserService : IServiceObject
+    partial class UserService : IUserService
     {
         private readonly ServiceManager _serviceManager;
 
         private ulong _clientProcessId;
-        private bool _initialized;
+        private bool  _initialized;
 
         public UserService(ServiceManager serviceManager)
         {
@@ -21,7 +21,7 @@ namespace Ryujinx.Horizon.Sm
         public Result Initialize([ClientProcessId] ulong clientProcessId)
         {
             _clientProcessId = clientProcessId;
-            _initialized = true;
+            _initialized     = true;
 
             return Result.Success;
         }
@@ -63,4 +63,4 @@ namespace Ryujinx.Horizon.Sm
             return _serviceManager.UnregisterService(_clientProcessId, name);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sm/ManagerService.cs b/Ryujinx.Horizon/Sm/ManagerService.cs
deleted file mode 100644
index 1719dcfd5f..0000000000
--- a/Ryujinx.Horizon/Sm/ManagerService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Ryujinx.Horizon.Sdk.Sf;
-
-namespace Ryujinx.Horizon.Sm
-{
-    partial class ManagerService : IServiceObject
-    {
-    }
-}
diff --git a/Ryujinx.Horizon/Sm/SmMain.cs b/Ryujinx.Horizon/Sm/SmMain.cs
index 8c37bece57..5656d464fb 100644
--- a/Ryujinx.Horizon/Sm/SmMain.cs
+++ b/Ryujinx.Horizon/Sm/SmMain.cs
@@ -1,30 +1,34 @@
-using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.Prepo.Types;
+using Ryujinx.Horizon.Prepo;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
 using Ryujinx.Horizon.Sdk.Sm;
 using Ryujinx.Horizon.Sm.Impl;
+using Ryujinx.Horizon.Sm.Types;
 
 namespace Ryujinx.Horizon.Sm
 {
     public class SmMain
     {
-        private enum PortIndex
-        {
-            User,
-            Manager
-        }
+        private const int SmMaxSessionsCount      = 64;
+        private const int SmmMaxSessionsCount     = 1;
+        private const int SmTotalMaxSessionsCount = SmMaxSessionsCount + SmmMaxSessionsCount;
 
         private const int MaxPortsCount = 2;
 
-        private readonly ServerManager _serverManager = new ServerManager(null, null, MaxPortsCount, ManagerOptions.Default, 0);
-        private readonly ServiceManager _serviceManager = new ServiceManager();
+        private SmServerManager _serverManager;
+
+        private readonly ServiceManager _serviceManager = new();
 
         public void Main()
         {
-            HorizonStatic.Syscall.ManageNamedPort(out int smHandle, "sm:", 64).AbortOnFailure();
+            HorizonStatic.Syscall.ManageNamedPort(out int smHandle, "sm:", SmMaxSessionsCount).AbortOnFailure();
 
-            _serverManager.RegisterServer((int)PortIndex.User, smHandle);
-            _serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), 1).AbortOnFailure();
-            _serverManager.RegisterServer((int)PortIndex.Manager, smmHandle);
+            _serverManager = new SmServerManager(_serviceManager, null, null, MaxPortsCount, ManagerOptions.Default, SmTotalMaxSessionsCount);
+
+            _serverManager.RegisterServer((int)SmPortIndex.User, smHandle);
+            _serviceManager.RegisterServiceForSelf(out int smmHandle, ServiceName.Encode("sm:m"), SmmMaxSessionsCount).AbortOnFailure();
+            _serverManager.RegisterServer((int)SmPortIndex.Manager, smmHandle);
             _serverManager.ServiceRequests();
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sm/SmResult.cs b/Ryujinx.Horizon/Sm/SmResult.cs
index 3063445dc8..2d503a4f80 100644
--- a/Ryujinx.Horizon/Sm/SmResult.cs
+++ b/Ryujinx.Horizon/Sm/SmResult.cs
@@ -6,14 +6,14 @@ namespace Ryujinx.Horizon.Sm
     {
         private const int ModuleId = 21;
 
-        public static Result OutOfProcess => new Result(ModuleId, 1);
-        public static Result InvalidClient => new Result(ModuleId, 2);
-        public static Result OutOfSessions => new Result(ModuleId, 3);
-        public static Result AlreadyRegistered => new Result(ModuleId, 4);
-        public static Result OutOfServices => new Result(ModuleId, 5);
-        public static Result InvalidServiceName => new Result(ModuleId, 6);
-        public static Result NotRegistered => new Result(ModuleId, 7);
-        public static Result NotAllowed => new Result(ModuleId, 8);
-        public static Result TooLargeAccessControl => new Result(ModuleId, 9);
+        public static Result OutOfProcess          => new(ModuleId, 1);
+        public static Result InvalidClient         => new(ModuleId, 2);
+        public static Result OutOfSessions         => new(ModuleId, 3);
+        public static Result AlreadyRegistered     => new(ModuleId, 4);
+        public static Result OutOfServices         => new(ModuleId, 5);
+        public static Result InvalidServiceName    => new(ModuleId, 6);
+        public static Result NotRegistered         => new(ModuleId, 7);
+        public static Result NotAllowed            => new(ModuleId, 8);
+        public static Result TooLargeAccessControl => new(ModuleId, 9);
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sm/SmServerManager.cs b/Ryujinx.Horizon/Sm/SmServerManager.cs
new file mode 100644
index 0000000000..dc8dc5b675
--- /dev/null
+++ b/Ryujinx.Horizon/Sm/SmServerManager.cs
@@ -0,0 +1,30 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.Sdk.Sm;
+using Ryujinx.Horizon.Sm.Impl;
+using Ryujinx.Horizon.Sm.Ipc;
+using Ryujinx.Horizon.Sm.Types;
+using System;
+
+namespace Ryujinx.Horizon.Sm
+{
+    class SmServerManager : ServerManager
+    {
+        private readonly ServiceManager _serviceManager;
+
+        public SmServerManager(ServiceManager serviceManager, HeapAllocator allocator, SmApi sm, int maxPorts, ManagerOptions options, int maxSessions) : base(allocator, sm, maxPorts, options, maxSessions)
+        {
+            _serviceManager = serviceManager;
+        }
+
+        protected override Result OnNeedsToAccept(int portIndex, Server server)
+        {
+            return (SmPortIndex)portIndex switch
+            {
+                SmPortIndex.User    => AcceptImpl(server, new UserService(_serviceManager)),
+                SmPortIndex.Manager => AcceptImpl(server, new ManagerService()),
+                _                   => throw new ArgumentOutOfRangeException(nameof(portIndex)),
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs b/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs
new file mode 100644
index 0000000000..5325558b8f
--- /dev/null
+++ b/Ryujinx.Horizon/Sm/Types/SmPortIndex.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Horizon.Sm.Types
+{
+    enum SmPortIndex
+    {
+        User,
+        Manager
+    }
+}
\ No newline at end of file