AppDataManager.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Common.Utilities;
  3. using System;
  4. using System.IO;
  5. namespace Ryujinx.Common.Configuration
  6. {
  7. public static class AppDataManager
  8. {
  9. private const string DefaultBaseDir = "Ryujinx";
  10. private const string DefaultPortableDir = "portable";
  11. // The following 3 are always part of Base Directory
  12. private const string GamesDir = "games";
  13. private const string ProfilesDir = "profiles";
  14. private const string KeysDir = "system";
  15. public enum LaunchMode
  16. {
  17. UserProfile,
  18. Portable,
  19. Custom,
  20. }
  21. public static LaunchMode Mode { get; private set; }
  22. public static string BaseDirPath { get; private set; }
  23. public static string GamesDirPath { get; private set; }
  24. public static string ProfilesDirPath { get; private set; }
  25. public static string KeysDirPath { get; private set; }
  26. public static string KeysDirPathUser { get; }
  27. public static string LogsDirPath { get; private set; }
  28. public const string DefaultNandDir = "bis";
  29. public const string DefaultSdcardDir = "sdcard";
  30. private const string DefaultModsDir = "mods";
  31. public static string CustomModsPath { get; set; }
  32. public static string CustomSdModsPath { get; set; }
  33. public static string CustomNandPath { get; set; } // TODO: Actually implement this into VFS
  34. public static string CustomSdCardPath { get; set; } // TODO: Actually implement this into VFS
  35. static AppDataManager()
  36. {
  37. KeysDirPathUser = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch");
  38. }
  39. public static void Initialize(string baseDirPath)
  40. {
  41. string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
  42. if (appDataPath.Length == 0)
  43. {
  44. appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
  45. }
  46. string userProfilePath = Path.Combine(appDataPath, DefaultBaseDir);
  47. string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir);
  48. // On macOS, check for a portable directory next to the app bundle as well.
  49. if (OperatingSystem.IsMacOS() && !Directory.Exists(portablePath))
  50. {
  51. string bundlePath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", ".."));
  52. // Make sure we're actually running within an app bundle.
  53. if (bundlePath.EndsWith(".app"))
  54. {
  55. portablePath = Path.GetFullPath(Path.Combine(bundlePath, "..", DefaultPortableDir));
  56. }
  57. }
  58. if (Directory.Exists(portablePath))
  59. {
  60. BaseDirPath = portablePath;
  61. Mode = LaunchMode.Portable;
  62. }
  63. else
  64. {
  65. BaseDirPath = userProfilePath;
  66. Mode = LaunchMode.UserProfile;
  67. }
  68. if (baseDirPath != null && baseDirPath != userProfilePath)
  69. {
  70. if (!Directory.Exists(baseDirPath))
  71. {
  72. Logger.Error?.Print(LogClass.Application, $"Custom Data Directory '{baseDirPath}' does not exist. Falling back to {Mode}...");
  73. }
  74. else
  75. {
  76. BaseDirPath = baseDirPath;
  77. Mode = LaunchMode.Custom;
  78. }
  79. }
  80. BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths
  81. // NOTE: Moves the Ryujinx folder in `~/.config` to `~/Library/Application Support` if one is found
  82. // and a Ryujinx folder does not already exist in Application Support.
  83. // Also creates a symlink from `~/.config/Ryujinx` to `~/Library/Application Support/Ryujinx` to preserve backwards compatibility.
  84. // This should be removed in the future.
  85. if (OperatingSystem.IsMacOS() && Mode == LaunchMode.UserProfile)
  86. {
  87. string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
  88. if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
  89. {
  90. FileSystemUtils.MoveDirectory(oldConfigPath, BaseDirPath);
  91. Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
  92. }
  93. }
  94. SetupBasePaths();
  95. }
  96. public static string GetOrCreateLogsDir()
  97. {
  98. if (Directory.Exists(LogsDirPath))
  99. {
  100. return LogsDirPath;
  101. }
  102. Logger.Notice.Print(LogClass.Application, "Logging directory not found; attempting to create new logging directory.");
  103. LogsDirPath = SetUpLogsDir();
  104. return LogsDirPath;
  105. }
  106. private static string SetUpLogsDir()
  107. {
  108. string logDir = "";
  109. if (Mode == LaunchMode.Portable)
  110. {
  111. logDir = Path.Combine(BaseDirPath, "Logs");
  112. try
  113. {
  114. Directory.CreateDirectory(logDir);
  115. }
  116. catch
  117. {
  118. Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
  119. return null;
  120. }
  121. }
  122. else
  123. {
  124. if (OperatingSystem.IsMacOS())
  125. {
  126. // NOTE: Should evaluate to "~/Library/Logs/Ryujinx/".
  127. logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Logs", DefaultBaseDir);
  128. try
  129. {
  130. Directory.CreateDirectory(logDir);
  131. }
  132. catch
  133. {
  134. Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
  135. logDir = "";
  136. }
  137. if (string.IsNullOrEmpty(logDir))
  138. {
  139. // NOTE: Should evaluate to "~/Library/Application Support/Ryujinx/Logs".
  140. logDir = Path.Combine(BaseDirPath, "Logs");
  141. try
  142. {
  143. Directory.CreateDirectory(logDir);
  144. }
  145. catch
  146. {
  147. Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
  148. return null;
  149. }
  150. }
  151. }
  152. else if (OperatingSystem.IsWindows())
  153. {
  154. // NOTE: Should evaluate to a "Logs" directory in whatever directory Ryujinx was launched from.
  155. logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
  156. try
  157. {
  158. Directory.CreateDirectory(logDir);
  159. }
  160. catch
  161. {
  162. Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
  163. logDir = "";
  164. }
  165. if (string.IsNullOrEmpty(logDir))
  166. {
  167. // NOTE: Should evaluate to "C:\Users\user\AppData\Roaming\Ryujinx\Logs".
  168. logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir, "Logs");
  169. try
  170. {
  171. Directory.CreateDirectory(logDir);
  172. }
  173. catch
  174. {
  175. Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
  176. return null;
  177. }
  178. }
  179. }
  180. else if (OperatingSystem.IsLinux())
  181. {
  182. // NOTE: Should evaluate to "~/.config/Ryujinx/Logs".
  183. logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir, "Logs");
  184. try
  185. {
  186. Directory.CreateDirectory(logDir);
  187. }
  188. catch
  189. {
  190. Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
  191. return null;
  192. }
  193. }
  194. }
  195. return logDir;
  196. }
  197. private static void SetupBasePaths()
  198. {
  199. Directory.CreateDirectory(BaseDirPath);
  200. LogsDirPath = SetUpLogsDir();
  201. Directory.CreateDirectory(GamesDirPath = Path.Combine(BaseDirPath, GamesDir));
  202. Directory.CreateDirectory(ProfilesDirPath = Path.Combine(BaseDirPath, ProfilesDir));
  203. Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir));
  204. }
  205. // Check if existing old baseDirPath is a symlink, to prevent possible errors.
  206. // Should be removed, when the existence of the old directory isn't checked anymore.
  207. private static bool IsPathSymlink(string path)
  208. {
  209. FileAttributes attributes = File.GetAttributes(path);
  210. return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
  211. }
  212. public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
  213. public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName;
  214. }
  215. }