From ee174be57c69d5620f200103a403b42d7f69ed5f Mon Sep 17 00:00:00 2001
From: MutantAura <44103205+MutantAura@users.noreply.github.com>
Date: Sun, 6 Mar 2022 21:12:01 +0000
Subject: [PATCH] Mod loading from atmosphere SD directories (#3176)

* initial sd support

* GUI option

* alignment

* review changes
---
 Ryujinx.Common/Configuration/AppDataManager.cs      |  4 +++-
 Ryujinx.HLE/HOS/ApplicationLoader.cs                | 10 ++++++++--
 Ryujinx.HLE/HOS/ModLoader.cs                        |  3 ++-
 Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs | 11 +++++++++++
 Ryujinx/Ui/Widgets/GameTableContextMenu.cs          |  8 ++++++++
 5 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/Ryujinx.Common/Configuration/AppDataManager.cs b/Ryujinx.Common/Configuration/AppDataManager.cs
index 99373570de..1d217f5876 100644
--- a/Ryujinx.Common/Configuration/AppDataManager.cs
+++ b/Ryujinx.Common/Configuration/AppDataManager.cs
@@ -34,6 +34,7 @@ namespace Ryujinx.Common.Configuration
         private const string DefaultModsDir = "mods";
 
         public static string CustomModsPath { get; set; }
+        public static string CustomSdModsPath {get; set; }
         public static string CustomNandPath { get; set; } // TODO: Actually implement this into VFS
         public static string CustomSdCardPath { get; set; } // TODO: Actually implement this into VFS
 
@@ -84,6 +85,7 @@ namespace Ryujinx.Common.Configuration
             Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir));
         }
 
-        public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
+        public static string GetModsPath()   => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
+        public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName;
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs
index cf92abda26..1c177d20f3 100644
--- a/Ryujinx.HLE/HOS/ApplicationLoader.cs
+++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs
@@ -84,7 +84,10 @@ namespace Ryujinx.HLE.HOS
 
             MetaLoader metaData = ReadNpdm(codeFs);
 
-            _device.Configuration.VirtualFileSystem.ModLoader.CollectMods(new[] { TitleId }, _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath());
+            _device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
+                new[] { TitleId }, 
+                _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), 
+                _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
 
             if (TitleId != 0)
             {
@@ -388,7 +391,10 @@ namespace Ryujinx.HLE.HOS
 
             MetaLoader metaData = ReadNpdm(codeFs);
 
-            _device.Configuration.VirtualFileSystem.ModLoader.CollectMods(_device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId), _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath());
+            _device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
+                _device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId), 
+                _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), 
+                _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
 
             if (controlNca != null)
             {
diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/Ryujinx.HLE/HOS/ModLoader.cs
index 27c0022fa7..4f37903366 100644
--- a/Ryujinx.HLE/HOS/ModLoader.cs
+++ b/Ryujinx.HLE/HOS/ModLoader.cs
@@ -136,7 +136,8 @@ namespace Ryujinx.HLE.HOS
 
         private static bool StrEquals(string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase);
 
-        public string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath());
+        public string GetModsBasePath()   => EnsureBaseDirStructure(AppDataManager.GetModsPath());
+        public string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath());
 
         private string EnsureBaseDirStructure(string modsBasePath)
         {
diff --git a/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs b/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs
index 190efd4947..f1b5ce34a5 100644
--- a/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs
+++ b/Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs
@@ -11,6 +11,7 @@ namespace Ryujinx.Ui.Widgets
         private MenuItem _manageDlcMenuItem;
         private MenuItem _manageCheatMenuItem;
         private MenuItem _openTitleModDirMenuItem;
+        private MenuItem _openTitleSdModDirMenuItem;
         private Menu     _extractSubMenu;
         private MenuItem _extractMenuItem;
         private MenuItem _extractRomFsMenuItem;
@@ -88,6 +89,15 @@ namespace Ryujinx.Ui.Widgets
             };
             _openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked;
 
+            //
+            // _openTitleSdModDirMenuItem
+            //
+            _openTitleSdModDirMenuItem = new MenuItem("Open Atmosphere Mods Directory")
+            {
+                TooltipText = "Open the alternative SD card atmosphere directory which contains the Application's Mods."
+            };
+            _openTitleSdModDirMenuItem.Activated += OpenTitleSdModDir_Clicked;
+
             //
             // _extractSubMenu
             //
@@ -199,6 +209,7 @@ namespace Ryujinx.Ui.Widgets
             Add(_manageDlcMenuItem);
             Add(_manageCheatMenuItem);
             Add(_openTitleModDirMenuItem);
+            Add(_openTitleSdModDirMenuItem);
             Add(new SeparatorMenuItem());
             Add(_manageCacheMenuItem);
             Add(_extractMenuItem);
diff --git a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
index ef8fca341f..306033d2d2 100644
--- a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
+++ b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
@@ -477,6 +477,14 @@ namespace Ryujinx.Ui.Widgets
             OpenHelper.OpenFolder(titleModsPath);
         }
 
+        private void OpenTitleSdModDir_Clicked(object sender, EventArgs args)
+        {
+            string sdModsBasePath  = _virtualFileSystem.ModLoader.GetSdModsBasePath();
+            string titleModsPath   = _virtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, _titleIdText);
+
+            OpenHelper.OpenFolder(titleModsPath);
+        }
+
         private void ExtractRomFs_Clicked(object sender, EventArgs args)
         {
             ExtractSection(NcaSectionType.Data);