2020-08-30 22:21:53 +05:30
|
|
|
using Ryujinx.Common.Logging;
|
2024-01-29 19:58:18 +01:00
|
|
|
using Ryujinx.Common.Utilities;
|
2020-08-30 22:21:53 +05:30
|
|
|
using System;
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
namespace Ryujinx.Common.Configuration
|
|
|
|
{
|
|
|
|
public static class AppDataManager
|
|
|
|
{
|
2024-01-29 19:58:18 +01:00
|
|
|
private const string DefaultBaseDir = "Ryujinx";
|
|
|
|
private const string DefaultPortableDir = "portable";
|
2020-08-30 22:21:53 +05:30
|
|
|
|
|
|
|
// The following 3 are always part of Base Directory
|
|
|
|
private const string GamesDir = "games";
|
|
|
|
private const string ProfilesDir = "profiles";
|
|
|
|
private const string KeysDir = "system";
|
|
|
|
|
2021-03-16 02:40:36 +05:30
|
|
|
public enum LaunchMode
|
|
|
|
{
|
|
|
|
UserProfile,
|
|
|
|
Portable,
|
2023-06-28 18:41:38 +02:00
|
|
|
Custom,
|
2021-03-16 02:40:36 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
public static LaunchMode Mode { get; private set; }
|
|
|
|
|
2020-08-30 22:21:53 +05:30
|
|
|
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; }
|
2021-03-16 02:40:36 +05:30
|
|
|
public static string KeysDirPathUser { get; }
|
2020-08-30 22:21:53 +05:30
|
|
|
|
2024-02-10 19:17:19 -06:00
|
|
|
public static string LogsDirPath { get; private set; }
|
|
|
|
|
2020-08-30 22:21:53 +05:30
|
|
|
public const string DefaultNandDir = "bis";
|
|
|
|
public const string DefaultSdcardDir = "sdcard";
|
|
|
|
private const string DefaultModsDir = "mods";
|
|
|
|
|
|
|
|
public static string CustomModsPath { get; set; }
|
2023-06-28 18:41:38 +02:00
|
|
|
public static string CustomSdModsPath { get; set; }
|
2020-08-30 22:21:53 +05:30
|
|
|
public static string CustomNandPath { get; set; } // TODO: Actually implement this into VFS
|
|
|
|
public static string CustomSdCardPath { get; set; } // TODO: Actually implement this into VFS
|
|
|
|
|
|
|
|
static AppDataManager()
|
|
|
|
{
|
2021-03-16 02:40:36 +05:30
|
|
|
KeysDirPathUser = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch");
|
2020-08-30 22:21:53 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
public static void Initialize(string baseDirPath)
|
|
|
|
{
|
2024-02-10 19:17:19 -06:00
|
|
|
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
2022-12-06 22:00:25 +00:00
|
|
|
|
|
|
|
if (appDataPath.Length == 0)
|
|
|
|
{
|
|
|
|
appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
string userProfilePath = Path.Combine(appDataPath, DefaultBaseDir);
|
2021-03-16 02:40:36 +05:30
|
|
|
string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir);
|
2020-08-30 22:21:53 +05:30
|
|
|
|
2024-01-19 18:09:51 -08:00
|
|
|
// On macOS, check for a portable directory next to the app bundle as well.
|
|
|
|
if (OperatingSystem.IsMacOS() && !Directory.Exists(portablePath))
|
|
|
|
{
|
|
|
|
string bundlePath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", ".."));
|
|
|
|
// Make sure we're actually running within an app bundle.
|
|
|
|
if (bundlePath.EndsWith(".app"))
|
|
|
|
{
|
|
|
|
portablePath = Path.GetFullPath(Path.Combine(bundlePath, "..", DefaultPortableDir));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-16 02:40:36 +05:30
|
|
|
if (Directory.Exists(portablePath))
|
|
|
|
{
|
|
|
|
BaseDirPath = portablePath;
|
|
|
|
Mode = LaunchMode.Portable;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BaseDirPath = userProfilePath;
|
|
|
|
Mode = LaunchMode.UserProfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (baseDirPath != null && baseDirPath != userProfilePath)
|
2020-08-30 22:21:53 +05:30
|
|
|
{
|
|
|
|
if (!Directory.Exists(baseDirPath))
|
|
|
|
{
|
2021-03-16 02:40:36 +05:30
|
|
|
Logger.Error?.Print(LogClass.Application, $"Custom Data Directory '{baseDirPath}' does not exist. Falling back to {Mode}...");
|
2020-08-30 22:21:53 +05:30
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BaseDirPath = baseDirPath;
|
2021-03-16 02:40:36 +05:30
|
|
|
Mode = LaunchMode.Custom;
|
2020-08-30 22:21:53 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-16 02:40:36 +05:30
|
|
|
BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths
|
|
|
|
|
2023-02-21 06:14:31 -05:00
|
|
|
// NOTE: Moves the Ryujinx folder in `~/.config` to `~/Library/Application Support` if one is found
|
|
|
|
// and a Ryujinx folder does not already exist in Application Support.
|
|
|
|
// Also creates a symlink from `~/.config/Ryujinx` to `~/Library/Application Support/Ryujinx` to preserve backwards compatibility.
|
|
|
|
// This should be removed in the future.
|
|
|
|
if (OperatingSystem.IsMacOS() && Mode == LaunchMode.UserProfile)
|
|
|
|
{
|
|
|
|
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
|
2023-06-09 15:31:19 +02:00
|
|
|
if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
|
2023-02-21 06:14:31 -05:00
|
|
|
{
|
2024-01-29 19:58:18 +01:00
|
|
|
FileSystemUtils.MoveDirectory(oldConfigPath, BaseDirPath);
|
2023-02-21 06:14:31 -05:00
|
|
|
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-30 22:21:53 +05:30
|
|
|
SetupBasePaths();
|
|
|
|
}
|
|
|
|
|
2024-02-10 19:17:19 -06:00
|
|
|
public static string GetOrCreateLogsDir()
|
|
|
|
{
|
|
|
|
if (Directory.Exists(LogsDirPath))
|
|
|
|
{
|
|
|
|
return LogsDirPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
Logger.Notice.Print(LogClass.Application, "Logging directory not found; attempting to create new logging directory.");
|
|
|
|
LogsDirPath = SetUpLogsDir();
|
|
|
|
|
|
|
|
return LogsDirPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static string SetUpLogsDir()
|
|
|
|
{
|
|
|
|
string logDir = "";
|
|
|
|
|
|
|
|
if (Mode == LaunchMode.Portable)
|
|
|
|
{
|
|
|
|
logDir = Path.Combine(BaseDirPath, "Logs");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(logDir);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (OperatingSystem.IsMacOS())
|
|
|
|
{
|
|
|
|
// NOTE: Should evaluate to "~/Library/Logs/Ryujinx/".
|
|
|
|
logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Logs", DefaultBaseDir);
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(logDir);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
|
|
|
logDir = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(logDir))
|
|
|
|
{
|
|
|
|
// NOTE: Should evaluate to "~/Library/Application Support/Ryujinx/Logs".
|
|
|
|
logDir = Path.Combine(BaseDirPath, "Logs");
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(logDir);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (OperatingSystem.IsWindows())
|
|
|
|
{
|
|
|
|
// NOTE: Should evaluate to a "Logs" directory in whatever directory Ryujinx was launched from.
|
|
|
|
logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(logDir);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
|
|
|
logDir = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(logDir))
|
|
|
|
{
|
|
|
|
// NOTE: Should evaluate to "C:\Users\user\AppData\Roaming\Ryujinx\Logs".
|
|
|
|
logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir, "Logs");
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(logDir);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (OperatingSystem.IsLinux())
|
|
|
|
{
|
|
|
|
// NOTE: Should evaluate to "~/.config/Ryujinx/Logs".
|
|
|
|
logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir, "Logs");
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(logDir);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return logDir;
|
|
|
|
}
|
|
|
|
|
2020-08-30 22:21:53 +05:30
|
|
|
private static void SetupBasePaths()
|
|
|
|
{
|
|
|
|
Directory.CreateDirectory(BaseDirPath);
|
2024-02-10 19:17:19 -06:00
|
|
|
LogsDirPath = SetUpLogsDir();
|
2020-08-30 22:21:53 +05:30
|
|
|
Directory.CreateDirectory(GamesDirPath = Path.Combine(BaseDirPath, GamesDir));
|
|
|
|
Directory.CreateDirectory(ProfilesDirPath = Path.Combine(BaseDirPath, ProfilesDir));
|
|
|
|
Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir));
|
|
|
|
}
|
|
|
|
|
2023-06-09 15:31:19 +02:00
|
|
|
// Check if existing old baseDirPath is a symlink, to prevent possible errors.
|
2024-01-29 19:58:18 +01:00
|
|
|
// Should be removed, when the existence of the old directory isn't checked anymore.
|
2023-06-09 15:31:19 +02:00
|
|
|
private static bool IsPathSymlink(string path)
|
|
|
|
{
|
|
|
|
FileAttributes attributes = File.GetAttributes(path);
|
|
|
|
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
|
|
|
|
}
|
|
|
|
|
2023-06-28 18:41:38 +02:00
|
|
|
public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
|
2022-03-06 21:12:01 +00:00
|
|
|
public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName;
|
2020-08-30 22:21:53 +05:30
|
|
|
}
|
2023-06-28 18:41:38 +02:00
|
|
|
}
|