From 1bd99e559785ecf6a40e1bcb4a717642014c078f Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Tue, 20 Mar 2018 12:18:25 -0300
Subject: [PATCH] Support different framebuffer offsets (fixes #59)

---
 Ryujinx.Core/OsHle/GlobalStateTable.cs        | 11 ++++-
 Ryujinx.Core/OsHle/IdDictionary.cs            |  5 +++
 Ryujinx.Core/OsHle/Process.cs                 |  4 +-
 Ryujinx.Core/OsHle/Services/Nv/NvMap.cs       |  2 +-
 Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs     | 40 +++++++++++++++++++
 .../OsHle/Services/Nv/ServiceNvDrv.cs         | 17 +++++---
 Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs   | 17 ++++++--
 7 files changed, 82 insertions(+), 14 deletions(-)
 create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs

diff --git a/Ryujinx.Core/OsHle/GlobalStateTable.cs b/Ryujinx.Core/OsHle/GlobalStateTable.cs
index ffc9f26270..2a5714ad09 100644
--- a/Ryujinx.Core/OsHle/GlobalStateTable.cs
+++ b/Ryujinx.Core/OsHle/GlobalStateTable.cs
@@ -12,11 +12,18 @@ namespace Ryujinx.Core.OsHle
             DictByProcess = new ConcurrentDictionary<Process, IdDictionary>();
         }
 
-        public int Add(Process Process, object Obj)
+        public bool Add(Process Process, int Id, object Data)
         {
             IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
 
-            return Dict.Add(Obj);
+            return Dict.Add(Id, Data);
+        }
+
+        public int Add(Process Process, object Data)
+        {
+            IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
+
+            return Dict.Add(Data);
         }
 
         public object GetData(Process Process, int Id)
diff --git a/Ryujinx.Core/OsHle/IdDictionary.cs b/Ryujinx.Core/OsHle/IdDictionary.cs
index 0746ae81b1..2a498e7f67 100644
--- a/Ryujinx.Core/OsHle/IdDictionary.cs
+++ b/Ryujinx.Core/OsHle/IdDictionary.cs
@@ -15,6 +15,11 @@ namespace Ryujinx.Core.OsHle
             Objs = new ConcurrentDictionary<int, object>();
         }
 
+        public bool Add(int Id, object Data)
+        {
+            return Objs.TryAdd(Id, Data);
+        }
+
         public int Add(object Data)
         {
             if (Objs.TryAdd(FreeIdHint, Data))
diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs
index 1846e576d3..25c10a4353 100644
--- a/Ryujinx.Core/OsHle/Process.cs
+++ b/Ryujinx.Core/OsHle/Process.cs
@@ -356,9 +356,9 @@ namespace Ryujinx.Core.OsHle
 
                 ServiceNvDrv.Fds.DeleteProcess(this);
 
-                ServiceNvDrv.NvMaps.DeleteProcess(this);
-
+                ServiceNvDrv.NvMaps    .DeleteProcess(this);
                 ServiceNvDrv.NvMapsById.DeleteProcess(this);
+                ServiceNvDrv.NvMapsFb  .DeleteProcess(this);
 
                 Scheduler.Dispose();
 
diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
index ca844f9f28..d5a5a80082 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
@@ -7,6 +7,6 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
         public int  Size;
         public int  Align;
         public int  Kind;
-        public long Address;
+        public long CpuAddress;
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs
new file mode 100644
index 0000000000..1066cc9530
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.IpcServices.NvServices
+{
+    class NvMapFb
+    {
+        private List<long> BufferOffs;
+
+        public NvMapFb()
+        {
+            BufferOffs = new List<long>();
+        }
+
+        public void AddBufferOffset(long Offset)
+        {
+            BufferOffs.Add(Offset);
+        }
+
+        public bool HasBufferOffset(int Index)
+        {
+            if ((uint)Index >= BufferOffs.Count)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        public long GetBufferOffset(int Index)
+        {
+            if ((uint)Index >= BufferOffs.Count)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            return BufferOffs[Index];
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs
index ef223772f5..a1e0e5321a 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs
@@ -22,6 +22,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
         public static GlobalStateTable NvMaps     { get; private set; }
         public static GlobalStateTable NvMapsById { get; private set; }
+        public static GlobalStateTable NvMapsFb   { get; private set; }
 
         private KEvent Event;
 
@@ -76,6 +77,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             NvMaps     = new GlobalStateTable();
             NvMapsById = new GlobalStateTable();
+            NvMapsFb   = new GlobalStateTable();
         }
 
         public long Open(ServiceCtx Context)
@@ -131,6 +133,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             Context.ResponseData.Write(0);
 
+            NvMapsFb.Add(Context.Process, 0, new NvMapFb());
+
             return 0;
         }
 
@@ -209,8 +213,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             if (Handle == 0)
             {
-                //Handle 0 is valid here, but it refers to something else.
-                //TODO: Figure out what, for now just return success.
+                //This is used to store offsets for the Framebuffer(s);
+                NvMapFb MapFb = (NvMapFb)NvMapsFb.GetData(Context.Process, 0);
+
+                MapFb.AddBufferOffset(BuffAddr);
+
                 return 0;
             }
 
@@ -225,11 +232,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             if ((Flags & 1) != 0)
             {
-                Offset = Context.Ns.Gpu.MapMemory(Map.Address, Offset, Map.Size);
+                Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Offset, Map.Size);
             }
             else
             {
-                Offset = Context.Ns.Gpu.MapMemory(Map.Address, Map.Size);
+                Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Map.Size);
             }
 
             Context.Memory.WriteInt64(Position + 0x20, Offset);
@@ -614,7 +621,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
                 return -1; //TODO: Corrent error code.
             }
 
-            Map.Address = Addr;
+            Map.CpuAddress = Addr;
             Map.Align   = Align;
             Map.Kind    = Kind;
 
diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
index 3a7a2ee62d..550260bb8e 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
@@ -289,11 +289,20 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
 
             long FbSize = (uint)FbWidth * FbHeight * 4;
 
-            NvMap NvMap = GetNvMap(Context, Slot);
+            NvMap Map = GetNvMap(Context, Slot);
 
-            if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize)
+            NvMapFb MapFb = (NvMapFb)ServiceNvDrv.NvMapsFb.GetData(Context.Process, 0);
+
+            long Address = Map.CpuAddress;
+            
+            if (MapFb.HasBufferOffset(Slot))
             {
-                Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!");
+                Address += MapFb.GetBufferOffset(Slot);
+            }
+
+            if ((ulong)(Address + FbSize) > AMemoryMgr.AddrSize)
+            {
+                Logging.Error($"Frame buffer address {Address:x16} is invalid!");
 
                 BufferQueue[Slot].State = BufferState.Free;
 
@@ -365,7 +374,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
                 Interlocked.Increment(ref RenderQueueCount);
             }
 
-            byte* Fb = (byte*)Context.Memory.Ram + NvMap.Address;
+            byte* Fb = (byte*)Context.Memory.Ram + Address;
 
             Context.Ns.Gpu.Renderer.QueueAction(delegate()
             {