From 40d1acd1982705224413bc882f6ae25d4bf8ee1a Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Sun, 19 Sep 2021 12:57:39 +0200
Subject: [PATCH] vi: Unify resolutions values and accurate implementation of
 them. (#2640)

* vi: Unify resolutions values and accurate implementation of them.

To continue what was made in #2618, I've REd `vi` service a bit. Now values and checks related to displays are more accurate.

- `am`  GetDefaultDisplayResolution / GetDefaultDisplayResolutionChangeEvent have more informations on what the service does.
- `vi:u/vi:m/vi:s` GetDisplayService are now accurate.
- `IApplicationDisplay` GetRelayService, GetSystemDisplayService, GetManagerDisplayService, GetIndirectDisplayTransactionService, ListDisplays, OpenDisplay, OpenDefaultDisplay, CloseDisplay, GetDisplayResolution are now properly implemented.
- Some other calls are cleaned or have extra checks accordingly to RE.

Additionnaly, `IFriendService` have some wrong aligned things, and `pm:info` service placeholder was missing.

* just use _openedDisplayInfo.Remove()

* use context.Memory.Fill()

* fix some casting

* remove unneeded comment

* cleanup

* uses TryAdd

* displayId > ulong

* GetDisplayResolution > ulong

* UL
---
 .../SystemAppletProxy/ICommonStateGetter.cs   |  18 +-
 .../Friend/ServiceCreator/IFriendService.cs   |   4 +-
 .../HOS/Services/Pm/IInformationInterface.cs  |   8 +
 .../Services/Vi/IApplicationRootService.cs    |  10 +-
 .../HOS/Services/Vi/IManagerRootService.cs    |  10 +-
 .../HOS/Services/Vi/ISystemRootService.cs     |  10 +-
 Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs     |   5 +-
 .../AndroidSurfaceComposerClient.cs           |  19 ++
 .../ApplicationDisplayService/Display.cs      |  12 -
 .../IManagerDisplayService.cs                 |  14 ++
 .../ISystemDisplayService.cs                  |  11 +-
 .../Types/DisplayInfo.cs                      |  16 ++
 .../RootService/IApplicationDisplayService.cs | 219 ++++++++++++------
 .../HOS/Services/Vi/Types/ViServiceType.cs    |   9 +
 14 files changed, 265 insertions(+), 100 deletions(-)
 create mode 100644 Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs
 delete mode 100644 Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Display.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs

diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
index a042b2e893..f420b46249 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
@@ -3,6 +3,7 @@ using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.HLE.HOS.Services.Settings.Types;
+using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService;
 using Ryujinx.HLE.HOS.SystemState;
 using System;
 
@@ -193,16 +194,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         // GetDefaultDisplayResolution() -> (u32, u32)
         public ResultCode GetDefaultDisplayResolution(ServiceCtx context)
         {
-            if (context.Device.System.State.DockedMode)
-            {
-                context.ResponseData.Write(1920);
-                context.ResponseData.Write(1080);
-            }
-            else
-            {
-                context.ResponseData.Write(1280);
-                context.ResponseData.Write(720);
-            }
+            // NOTE: Original service calls IOperationModeManager::GetDefaultDisplayResolution of omm service.
+            //       IOperationModeManager::GetDefaultDisplayResolution of omm service call IManagerDisplayService::GetDisplayResolution of vi service.
+            (ulong width, ulong height) = AndroidSurfaceComposerClient.GetDisplayInfo(context);
+
+            context.ResponseData.Write((uint)width);
+            context.ResponseData.Write((uint)height);
 
             return ResultCode.Success;
         }
@@ -211,6 +208,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         // GetDefaultDisplayResolutionChangeEvent() -> handle<copy>
         public ResultCode GetDefaultDisplayResolutionChangeEvent(ServiceCtx context)
         {
+            // NOTE: Original service calls IOperationModeManager::GetDefaultDisplayResolutionChangeEvent of omm service.
             if (_displayResolutionChangedEventHandle == 0)
             {
                 if (context.Process.HandleTable.GenerateHandle(context.Device.System.DisplayResolutionChangeEvent.ReadableEvent, out _displayResolutionChangedEventHandle) != KernelResult.Success)
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
index f8a7b33719..c3e1d967ae 100644
--- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
@@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
             // Padding
             context.RequestData.ReadInt32();
 
-            UserId       userId   = context.RequestData.ReadStruct<UserId>();
+            UserId       userId = context.RequestData.ReadStruct<UserId>();
             FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>();
 
             // Pid placeholder
@@ -217,7 +217,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
 
             context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40UL);
 
-            ulong bufferPosition  = context.Request.RecvListBuff[0].Position;
+            ulong bufferPosition = context.Request.RecvListBuff[0].Position;
 
             if (userId.IsNull)
             {
diff --git a/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs
new file mode 100644
index 0000000000..0be85c4f61
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Pm/IInformationInterface.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Pm
+{
+    [Service("pm:info")]
+    class IInformationInterface : IpcService
+    {
+        public IInformationInterface(ServiceCtx context) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
index 7a2939635a..9fddde577e 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs
@@ -1,4 +1,5 @@
 using Ryujinx.HLE.HOS.Services.Vi.RootService;
+using Ryujinx.HLE.HOS.Services.Vi.Types;
 
 namespace Ryujinx.HLE.HOS.Services.Vi
 {
@@ -11,9 +12,14 @@ namespace Ryujinx.HLE.HOS.Services.Vi
         // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService>
         public ResultCode GetDisplayService(ServiceCtx context)
         {
-            int serviceType = context.RequestData.ReadInt32();
+            ViServiceType serviceType = (ViServiceType)context.RequestData.ReadInt32();
 
-            MakeObject(context, new IApplicationDisplayService());
+            if (serviceType != ViServiceType.Application)
+            {
+                return ResultCode.InvalidRange;
+            }
+
+            MakeObject(context, new IApplicationDisplayService(serviceType));
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs
index 28414ad66b..6676b629cd 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs
@@ -1,4 +1,5 @@
 using Ryujinx.HLE.HOS.Services.Vi.RootService;
+using Ryujinx.HLE.HOS.Services.Vi.Types;
 
 namespace Ryujinx.HLE.HOS.Services.Vi
 {
@@ -12,9 +13,14 @@ namespace Ryujinx.HLE.HOS.Services.Vi
         // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService>
         public ResultCode GetDisplayService(ServiceCtx context)
         {
-            int serviceType = context.RequestData.ReadInt32();
+            ViServiceType serviceType = (ViServiceType)context.RequestData.ReadInt32();
 
-            MakeObject(context, new IApplicationDisplayService());
+            if (serviceType != ViServiceType.Manager)
+            {
+                return ResultCode.InvalidRange;
+            }
+
+            MakeObject(context, new IApplicationDisplayService(serviceType));
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs
index 1faf98c0fe..b63f15c3b5 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs
@@ -1,4 +1,5 @@
 using Ryujinx.HLE.HOS.Services.Vi.RootService;
+using Ryujinx.HLE.HOS.Services.Vi.Types;
 
 namespace Ryujinx.HLE.HOS.Services.Vi
 {
@@ -12,9 +13,14 @@ namespace Ryujinx.HLE.HOS.Services.Vi
         // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService>
         public ResultCode GetDisplayService(ServiceCtx context)
         {
-            int serviceType = context.RequestData.ReadInt32();
+            ViServiceType serviceType = (ViServiceType)context.RequestData.ReadInt32();
 
-            MakeObject(context, new IApplicationDisplayService());
+            if (serviceType != ViServiceType.System)
+            {
+                return ResultCode.InvalidRange;
+            }
+
+            MakeObject(context, new IApplicationDisplayService(serviceType));
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs
index 2c78c1a3d6..d888e64453 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs
@@ -9,6 +9,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi
 
         InvalidArguments   = (1 << ErrorCodeShift) | ModuleId,
         InvalidLayerSize   = (4 << ErrorCodeShift) | ModuleId,
-        InvalidScalingMode = (6 << ErrorCodeShift) | ModuleId
+        InvalidRange       = (5 << ErrorCodeShift) | ModuleId,
+        InvalidScalingMode = (6 << ErrorCodeShift) | ModuleId,
+        InvalidValue       = (7 << ErrorCodeShift) | ModuleId,
+        AlreadyOpened      = (9 << ErrorCodeShift) | ModuleId
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs
new file mode 100644
index 0000000000..1fa99e65a4
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
+{
+    static class AndroidSurfaceComposerClient
+    {
+        // NOTE: This is android::SurfaceComposerClient::getDisplayInfo.
+        public static (ulong, ulong) GetDisplayInfo(ServiceCtx context, ulong displayId = 0)
+        {
+            // TODO: This need to be REd, it should returns the driver resolution and more.
+            if (context.Device.System.State.DockedMode)
+            {
+                return (1920, 1080);
+            }
+            else
+            {
+                return (1280, 720);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Display.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Display.cs
deleted file mode 100644
index 47c7b2aeac..0000000000
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Display.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Vi
-{
-    class Display
-    {
-        public string Name { get; private set; }
-
-        public Display(string name)
-        {
-            Name = name;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs
index 6cc103a04d..fdab0f1b71 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs
@@ -11,6 +11,20 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
             _applicationDisplayService = applicationDisplayService;
         }
 
+        [CommandHipc(1102)]
+        // GetDisplayResolution(u64 display_id) -> (u64 width, u64 height)
+        public ResultCode GetDisplayResolution(ServiceCtx context)
+        {
+            ulong displayId = context.RequestData.ReadUInt64();
+
+            (ulong width, ulong height) = AndroidSurfaceComposerClient.GetDisplayInfo(context, displayId);
+
+            context.ResponseData.Write(width);
+            context.ResponseData.Write(height);
+
+            return ResultCode.Success;
+        }
+
         [CommandHipc(2010)]
         // CreateManagedLayer(u32, u64, nn::applet::AppletResourceUserId) -> u64
         public ResultCode CreateManagedLayer(ServiceCtx context)
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs
index e82099b194..82dd6af63c 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs
@@ -42,12 +42,17 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
         // GetDisplayMode(u64) -> nn::vi::DisplayModeInfo
         public ResultCode GetDisplayMode(ServiceCtx context)
         {
-            // TODO: De-hardcode resolution.
-            context.ResponseData.Write(1280);
-            context.ResponseData.Write(720);
+            ulong displayId = context.RequestData.ReadUInt64();
+
+            (ulong width, ulong height) = AndroidSurfaceComposerClient.GetDisplayInfo(context, displayId);
+
+            context.ResponseData.Write((uint)width);
+            context.ResponseData.Write((uint)height);
             context.ResponseData.Write(60.0f);
             context.ResponseData.Write(0);
 
+            Logger.Stub?.PrintStub(LogClass.ServiceVi);
+
             return ResultCode.Success;
         }
     }
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs
new file mode 100644
index 0000000000..06309da0bb
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x60)]
+    struct DisplayInfo
+    {
+        public Array40<byte> Name;
+        public bool          LayerLimitEnabled;
+        public Array7<byte>  Padding;
+        public ulong         LayerLimitMax;
+        public ulong         Width;
+        public ulong         Height;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
index 5f161bee95..3008bf2c81 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs
@@ -1,30 +1,69 @@
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
 using Ryujinx.Cpu;
 using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
 using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService;
+using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types;
+using Ryujinx.HLE.HOS.Services.Vi.Types;
 using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using System.Text;
 
 namespace Ryujinx.HLE.HOS.Services.Vi.RootService
 {
     class IApplicationDisplayService : IpcService
     {
-        private readonly IdDictionary _displays;
+        private readonly ViServiceType _serviceType;
+
+        private readonly List<DisplayInfo>              _displayInfo;
+        private readonly Dictionary<ulong, DisplayInfo> _openDisplayInfo;
 
         private int _vsyncEventHandle;
 
-        public IApplicationDisplayService()
+        public IApplicationDisplayService(ViServiceType serviceType)
         {
-            _displays = new IdDictionary();
+            _serviceType     = serviceType;
+            _displayInfo     = new List<DisplayInfo>();
+            _openDisplayInfo = new Dictionary<ulong, DisplayInfo>();
+
+            void AddDisplayInfo(string name, bool layerLimitEnabled, ulong layerLimitMax, ulong width, ulong height)
+            {
+                DisplayInfo displayInfo = new DisplayInfo()
+                {
+                    Name              = new Array40<byte>(),
+                    LayerLimitEnabled = layerLimitEnabled,
+                    Padding           = new Array7<byte>(),
+                    LayerLimitMax     = layerLimitMax,
+                    Width             = width,
+                    Height            = height
+                };
+
+                Encoding.ASCII.GetBytes(name).AsSpan().CopyTo(displayInfo.Name.ToSpan());
+
+                _displayInfo.Add(displayInfo);
+            }
+
+            AddDisplayInfo("Default",  true,  1, 1920, 1080);
+            AddDisplayInfo("External", true,  1, 1920, 1080);
+            AddDisplayInfo("Edid",     true,  1, 0,    0);
+            AddDisplayInfo("Internal", true,  1, 1920, 1080);
+            AddDisplayInfo("Null",     false, 0, 1920, 1080);
         }
 
         [CommandHipc(100)]
         // GetRelayService() -> object<nns::hosbinder::IHOSBinderDriver>
         public ResultCode GetRelayService(ServiceCtx context)
         {
+            // FIXME: Should be _serviceType != ViServiceType.Application but guests crashes if we do this check.
+            if (_serviceType > ViServiceType.System)
+            {
+                return ResultCode.InvalidRange;
+            }
+
             MakeObject(context, new HOSBinderDriverServer());
 
             return ResultCode.Success;
@@ -34,6 +73,12 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
         // GetSystemDisplayService() -> object<nn::visrv::sf::ISystemDisplayService>
         public ResultCode GetSystemDisplayService(ServiceCtx context)
         {
+            // FIXME: Should be _serviceType == ViServiceType.System but guests crashes if we do this check.
+            if (_serviceType > ViServiceType.System)
+            {
+                return ResultCode.InvalidRange;
+            }
+
             MakeObject(context, new ISystemDisplayService(this));
 
             return ResultCode.Success;
@@ -43,6 +88,11 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
         // GetManagerDisplayService() -> object<nn::visrv::sf::IManagerDisplayService>
         public ResultCode GetManagerDisplayService(ServiceCtx context)
         {
+            if (_serviceType > ViServiceType.System)
+            {
+                return ResultCode.InvalidRange;
+            }
+
             MakeObject(context, new IManagerDisplayService(this));
 
             return ResultCode.Success;
@@ -52,63 +102,120 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
         // GetIndirectDisplayTransactionService() -> object<nns::hosbinder::IHOSBinderDriver>
         public ResultCode GetIndirectDisplayTransactionService(ServiceCtx context)
         {
+            if (_serviceType > ViServiceType.System)
+            {
+                return ResultCode.InvalidRange;
+            }
+
             MakeObject(context, new HOSBinderDriverServer());
 
             return ResultCode.Success;
         }
 
         [CommandHipc(1000)]
-        // ListDisplays() -> (u64, buffer<nn::vi::DisplayInfo, 6>)
+        // ListDisplays() -> (u64 count, buffer<nn::vi::DisplayInfo, 6>)
         public ResultCode ListDisplays(ServiceCtx context)
         {
-            ulong recBuffPtr = context.Request.ReceiveBuff[0].Position;
+            ulong displayInfoBuffer = context.Request.ReceiveBuff[0].Position;
 
-            MemoryHelper.FillWithZeros(context.Memory, recBuffPtr, 0x60);
+            // TODO: Determine when more than one display is needed.
+            ulong displayCount = 1;
 
-            // Add only the default display to buffer
-            context.Memory.Write(recBuffPtr, Encoding.ASCII.GetBytes("Default"));
-            context.Memory.Write(recBuffPtr + 0x40, 0x1UL);
-            context.Memory.Write(recBuffPtr + 0x48, 0x1UL);
-            context.Memory.Write(recBuffPtr + 0x50, 1280UL);
-            context.Memory.Write(recBuffPtr + 0x58, 720UL);
+            for (int i = 0; i < (int)displayCount; i++)
+            {
+                context.Memory.Fill(displayInfoBuffer + (ulong)(i * Unsafe.SizeOf<DisplayInfo>()), (ulong)(Unsafe.SizeOf<DisplayInfo>()), 0x00);
+                context.Memory.Write(displayInfoBuffer, _displayInfo[i]);
+            }
 
-            context.ResponseData.Write(1L);
+            context.ResponseData.Write(displayCount);
 
             return ResultCode.Success;
         }
 
         [CommandHipc(1010)]
-        // OpenDisplay(nn::vi::DisplayName) -> u64
+        // OpenDisplay(nn::vi::DisplayName) -> u64 display_id
         public ResultCode OpenDisplay(ServiceCtx context)
         {
-            string name = GetDisplayName(context);
+            string name = "";
 
-            long displayId = _displays.Add(new Display(name));
+            for (int index = 0; index < 8 && context.RequestData.BaseStream.Position < context.RequestData.BaseStream.Length; index++)
+            {
+                byte chr = context.RequestData.ReadByte();
 
-            context.ResponseData.Write(displayId);
+                if (chr >= 0x20 && chr < 0x7f)
+                {
+                    name += (char)chr;
+                }
+            }
+
+            return OpenDisplayImpl(context, name);
+        }
+
+        [CommandHipc(1011)]
+        // OpenDefaultDisplay() -> u64 display_id
+        public ResultCode OpenDefaultDisplay(ServiceCtx context)
+        {
+            return OpenDisplayImpl(context, "Default");
+        }
+
+        private ResultCode OpenDisplayImpl(ServiceCtx context, string name)
+        {
+            if (name == "")
+            {
+                return ResultCode.InvalidValue;
+            }
+
+            int displayId = _displayInfo.FindIndex(display => Encoding.ASCII.GetString(display.Name.ToSpan()).Trim('\0') == name);
+
+            if (displayId == -1)
+            {
+                return ResultCode.InvalidValue;
+            }
+
+            if (!_openDisplayInfo.TryAdd((ulong)displayId, _displayInfo[displayId]))
+            {
+                return ResultCode.AlreadyOpened;
+            }
+
+            context.ResponseData.Write((ulong)displayId);
 
             return ResultCode.Success;
         }
 
         [CommandHipc(1020)]
-        // CloseDisplay(u64)
+        // CloseDisplay(u64 display_id)
         public ResultCode CloseDisplay(ServiceCtx context)
         {
-            int displayId = context.RequestData.ReadInt32();
+            ulong displayId = context.RequestData.ReadUInt64();
 
-            _displays.Delete(displayId);
+            if (!_openDisplayInfo.Remove(displayId))
+            {
+                return ResultCode.InvalidValue;
+            }
 
             return ResultCode.Success;
         }
 
+        [CommandHipc(1101)]
+        // SetDisplayEnabled(u32 enabled_bool, u64 display_id)
+        public ResultCode SetDisplayEnabled(ServiceCtx context)
+        {
+            // NOTE: Stubbed in original service.
+            return ResultCode.Success;
+        }
+
         [CommandHipc(1102)]
-        // GetDisplayResolution(u64) -> (u64, u64)
+        // GetDisplayResolution(u64 display_id) -> (u64 width, u64 height)
         public ResultCode GetDisplayResolution(ServiceCtx context)
         {
-            long displayId = context.RequestData.ReadInt32();
+            // NOTE: Not used in original service.
+            // ulong displayId = context.RequestData.ReadUInt64();
 
-            context.ResponseData.Write(1280);
-            context.ResponseData.Write(720);
+            // NOTE: Returns ResultCode.InvalidArguments if width and height pointer are null, doesn't occur in our case.
+
+            // NOTE: Values are hardcoded in original service.
+            context.ResponseData.Write(1280UL); // Width
+            context.ResponseData.Write(720UL);  // Height
 
             return ResultCode.Success;
         }
@@ -162,8 +269,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
             ulong parcelPtr = context.Request.ReceiveBuff[0].Position;
 
             // TODO: support multi display.
-            Display disp = _displays.GetData<Display>((int)displayId);
-
             IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(0, out long layerId);
 
             context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
@@ -197,19 +302,30 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
         // SetLayerScalingMode(u32, u64)
         public ResultCode SetLayerScalingMode(ServiceCtx context)
         {
-            int  scalingMode = context.RequestData.ReadInt32();
-            long layerId     = context.RequestData.ReadInt64();
+            /*
+            uint  sourceScalingMode = context.RequestData.ReadUInt32();
+            ulong layerId           = context.RequestData.ReadUInt64();
+            */
+            // NOTE: Original service converts SourceScalingMode to DestinationScalingMode but does nothing with the converted value.
 
             return ResultCode.Success;
         }
 
         [CommandHipc(2102)] // 5.0.0+
-        // ConvertScalingMode(unknown) -> unknown
+        // ConvertScalingMode(u32 source_scaling_mode) -> u64 destination_scaling_mode
         public ResultCode ConvertScalingMode(ServiceCtx context)
         {
             SourceScalingMode scalingMode = (SourceScalingMode)context.RequestData.ReadInt32();
 
-            DestinationScalingMode? convertedScalingMode = ConvertScalingMode(scalingMode);
+            DestinationScalingMode? convertedScalingMode = scalingMode switch
+            {
+                SourceScalingMode.None                => DestinationScalingMode.None,
+                SourceScalingMode.Freeze              => DestinationScalingMode.Freeze,
+                SourceScalingMode.ScaleAndCrop        => DestinationScalingMode.ScaleAndCrop,
+                SourceScalingMode.ScaleToWindow       => DestinationScalingMode.ScaleToWindow,
+                SourceScalingMode.PreserveAspectRatio => DestinationScalingMode.PreserveAspectRatio,
+                _ => null,
+            };
 
             if (!convertedScalingMode.HasValue)
             {
@@ -217,8 +333,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
                 return ResultCode.InvalidArguments;
             }
 
-            if (scalingMode != SourceScalingMode.ScaleToWindow &&
-                scalingMode != SourceScalingMode.PreserveAspectRatio)
+            if (scalingMode != SourceScalingMode.ScaleToWindow && scalingMode != SourceScalingMode.PreserveAspectRatio)
             {
                 // Invalid scaling mode specified.
                 return ResultCode.InvalidScalingMode;
@@ -229,20 +344,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
             return ResultCode.Success;
         }
 
-        private DestinationScalingMode? ConvertScalingMode(SourceScalingMode source)
-        {
-            switch (source)
-            {
-                case SourceScalingMode.None:                return DestinationScalingMode.None;
-                case SourceScalingMode.Freeze:              return DestinationScalingMode.Freeze;
-                case SourceScalingMode.ScaleAndCrop:        return DestinationScalingMode.ScaleAndCrop;
-                case SourceScalingMode.ScaleToWindow:       return DestinationScalingMode.ScaleToWindow;
-                case SourceScalingMode.PreserveAspectRatio: return DestinationScalingMode.PreserveAspectRatio;
-            }
-
-            return null;
-        }
-
         [CommandHipc(2450)]
         // GetIndirectLayerImageMap(s64 width, s64 height, u64 handle, nn::applet::AppletResourceUserId, pid) -> (s64, s64, buffer<bytes, 0x46>)
         public ResultCode GetIndirectLayerImageMap(ServiceCtx context)
@@ -312,7 +413,12 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
         // GetDisplayVsyncEvent(u64) -> handle<copy>
         public ResultCode GetDisplayVSyncEvent(ServiceCtx context)
         {
-            string name = GetDisplayName(context);
+            ulong displayId = context.RequestData.ReadUInt64();
+
+            if (!_openDisplayInfo.ContainsKey(displayId))
+            {
+                return ResultCode.InvalidValue;
+            }
 
             if (_vsyncEventHandle == 0)
             {
@@ -326,24 +432,5 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
 
             return ResultCode.Success;
         }
-
-        private string GetDisplayName(ServiceCtx context)
-        {
-            string name = string.Empty;
-
-            for (int index = 0; index < 8 &&
-                context.RequestData.BaseStream.Position <
-                context.RequestData.BaseStream.Length; index++)
-            {
-                byte chr = context.RequestData.ReadByte();
-
-                if (chr >= 0x20 && chr < 0x7f)
-                {
-                    name += (char)chr;
-                }
-            }
-
-            return name;
-        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs b/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs
new file mode 100644
index 0000000000..ba6f8e5f8b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Vi.Types
+{
+    enum ViServiceType
+    {
+        Application,
+        Manager,
+        System
+    }
+}
\ No newline at end of file