From e44850fed42489f96aaa53b4a56020b8d7f9f736 Mon Sep 17 00:00:00 2001
From: mageven <62494521+mageven@users.noreply.github.com>
Date: Tue, 16 Mar 2021 02:40:36 +0530
Subject: [PATCH] Implement friendlier portable mode (#1885)

* Implement friendlier portable mode

* Remove first run dialog

* Disable updates in portable mode for now

* Convert relative custom paths to absolute ones

Also, fix a regression when custom path doesn't exist
---
 .../Configuration/AppDataManager.cs           | 42 ++++++++++++++-----
 Ryujinx.HLE/FileSystem/VirtualFileSystem.cs   |  4 +-
 Ryujinx/Modules/Updater/Updater.cs            | 11 +++++
 Ryujinx/Program.cs                            | 14 ++++---
 4 files changed, 53 insertions(+), 18 deletions(-)

diff --git a/Ryujinx.Common/Configuration/AppDataManager.cs b/Ryujinx.Common/Configuration/AppDataManager.cs
index 6630026f04..99373570de 100644
--- a/Ryujinx.Common/Configuration/AppDataManager.cs
+++ b/Ryujinx.Common/Configuration/AppDataManager.cs
@@ -6,21 +6,28 @@ namespace Ryujinx.Common.Configuration
 {
     public static class AppDataManager
     {
-        private static readonly string _defaultBaseDirPath;
-
-        private const string DefaultBaseDir = "Ryujinx";
+        public const string DefaultBaseDir = "Ryujinx";
+        public const string DefaultPortableDir = "portable";
 
         // The following 3 are always part of Base Directory
         private const string GamesDir = "games";
         private const string ProfilesDir = "profiles";
         private const string KeysDir = "system";
 
-        public static bool IsCustomBasePath { get; private set; }
+        public enum LaunchMode
+        {
+            UserProfile,
+            Portable,
+            Custom
+        }
+
+        public static LaunchMode Mode { get; private set; }
+
         public static string BaseDirPath { get; private set; }
         public static string GamesDirPath { get; private set; }
         public static string ProfilesDirPath { get; private set; }
         public static string KeysDirPath { get; private set; }
-        public static string KeysDirPathAlt { get; }
+        public static string KeysDirPathUser { get; }
 
         public const string DefaultNandDir = "bis";
         public const string DefaultSdcardDir = "sdcard";
@@ -32,27 +39,40 @@ namespace Ryujinx.Common.Configuration
 
         static AppDataManager()
         {
-            _defaultBaseDirPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
-            KeysDirPathAlt = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch");
+            KeysDirPathUser = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch");
         }
 
         public static void Initialize(string baseDirPath)
         {
-            BaseDirPath = _defaultBaseDirPath;
+            string userProfilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
+            string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir);
 
-            if (baseDirPath != null && baseDirPath != _defaultBaseDirPath)
+            if (Directory.Exists(portablePath))
+            {
+                BaseDirPath = portablePath;
+                Mode = LaunchMode.Portable;
+            }
+            else
+            {
+                BaseDirPath = userProfilePath;
+                Mode = LaunchMode.UserProfile;
+            }
+
+            if (baseDirPath != null && baseDirPath != userProfilePath)
             {
                 if (!Directory.Exists(baseDirPath))
                 {
-                    Logger.Error?.Print(LogClass.Application, $"Custom Data Directory '{baseDirPath}' does not exist. Using defaults...");
+                    Logger.Error?.Print(LogClass.Application, $"Custom Data Directory '{baseDirPath}' does not exist. Falling back to {Mode}...");
                 }
                 else
                 {
                     BaseDirPath = baseDirPath;
-                    IsCustomBasePath = true;
+                    Mode = LaunchMode.Custom;
                 }
             }
 
+            BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths
+
             SetupBasePaths();
         }
 
diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
index 4ba5df00e7..ff3232c263 100644
--- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
+++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
@@ -227,9 +227,9 @@ namespace Ryujinx.HLE.FileSystem
             string titleKeyFile   = null;
             string consoleKeyFile = null;
 
-            if (!AppDataManager.IsCustomBasePath)
+            if (AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile)
             {
-                LoadSetAtPath(AppDataManager.KeysDirPathAlt);
+                LoadSetAtPath(AppDataManager.KeysDirPathUser);
             }
 
             LoadSetAtPath(AppDataManager.KeysDirPath);
diff --git a/Ryujinx/Modules/Updater/Updater.cs b/Ryujinx/Modules/Updater/Updater.cs
index 9170787f0d..daf5e3a205 100644
--- a/Ryujinx/Modules/Updater/Updater.cs
+++ b/Ryujinx/Modules/Updater/Updater.cs
@@ -4,6 +4,7 @@ using ICSharpCode.SharpZipLib.Tar;
 using ICSharpCode.SharpZipLib.Zip;
 using Mono.Unix;
 using Newtonsoft.Json.Linq;
+using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
 using Ryujinx.Ui;
 using Ryujinx.Ui.Widgets;
@@ -490,6 +491,16 @@ namespace Ryujinx.Modules
                 return false;
             }
 
+            if (AppDataManager.Mode == AppDataManager.LaunchMode.Portable)
+            {
+                if (showWarnings)
+                {
+                    GtkDialog.CreateWarningDialog("You cannot update a portable version of Ryujinx!", "Please use a non-portable configuration to enable updates.");
+                }
+
+                return false;
+            }
+
             if (Program.Version.Contains("dirty"))
             {
                 if (showWarnings)
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index a3137cc1a8..36f379fc5a 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -135,9 +135,9 @@ namespace Ryujinx
             Application.Init();
 
             // Check if keys exists.
-            bool hasGlobalProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"));
-            bool hasAltProdKeys    = !AppDataManager.IsCustomBasePath && File.Exists(Path.Combine(AppDataManager.KeysDirPathAlt, "prod.keys"));
-            if (!hasGlobalProdKeys && !hasAltProdKeys)
+            bool hasSystemProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"));
+            bool hasCommonProdKeys = AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"));
+            if (!hasSystemProdKeys && !hasCommonProdKeys)
             {
                 UserErrorDialog.CreateUserErrorDialog(UserError.NoKeys);
             }
@@ -173,9 +173,13 @@ namespace Ryujinx
             var enabledLogs = Logger.GetEnabledLevels();
             Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogs.Count == 0 ? "<None>" : string.Join(", ", enabledLogs))}");
 
-            if (AppDataManager.IsCustomBasePath)
+            if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom)
             {
-                Logger.Notice.Print(LogClass.Application, $"Custom Data Directory: {AppDataManager.BaseDirPath}");
+                Logger.Notice.Print(LogClass.Application, $"Launch Mode: Custom Path {AppDataManager.BaseDirPath}");
+            }
+            else
+            {
+                Logger.Notice.Print(LogClass.Application, $"Launch Mode: {AppDataManager.Mode}");
             }
         }