VirtualFileSystem.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. using LibHac;
  2. using LibHac.Fs;
  3. using LibHac.FsService;
  4. using LibHac.FsSystem;
  5. using Ryujinx.HLE.FileSystem.Content;
  6. using Ryujinx.HLE.HOS;
  7. using System;
  8. using System.IO;
  9. namespace Ryujinx.HLE.FileSystem
  10. {
  11. public class VirtualFileSystem : IDisposable
  12. {
  13. public const string BasePath = "Ryujinx";
  14. public const string NandPath = "bis";
  15. public const string SdCardPath = "sdcard";
  16. public const string SystemPath = "system";
  17. public static string SafeNandPath = Path.Combine(NandPath, "safe");
  18. public static string SystemNandPath = Path.Combine(NandPath, "system");
  19. public static string UserNandPath = Path.Combine(NandPath, "user");
  20. public Keyset KeySet { get; private set; }
  21. public FileSystemServer FsServer { get; private set; }
  22. public FileSystemClient FsClient { get; private set; }
  23. public EmulatedGameCard GameCard { get; private set; }
  24. public VirtualFileSystem()
  25. {
  26. Reload();
  27. }
  28. public Stream RomFs { get; private set; }
  29. public void LoadRomFs(string fileName)
  30. {
  31. RomFs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
  32. }
  33. public void SetRomFs(Stream romfsStream)
  34. {
  35. RomFs?.Close();
  36. RomFs = romfsStream;
  37. }
  38. public string GetFullPath(string basePath, string fileName)
  39. {
  40. if (fileName.StartsWith("//"))
  41. {
  42. fileName = fileName.Substring(2);
  43. }
  44. else if (fileName.StartsWith('/'))
  45. {
  46. fileName = fileName.Substring(1);
  47. }
  48. else
  49. {
  50. return null;
  51. }
  52. string fullPath = Path.GetFullPath(Path.Combine(basePath, fileName));
  53. if (!fullPath.StartsWith(GetBasePath()))
  54. {
  55. return null;
  56. }
  57. return fullPath;
  58. }
  59. public string GetSdCardPath() => MakeFullPath(SdCardPath);
  60. public string GetNandPath() => MakeFullPath(NandPath);
  61. public string GetSystemPath() => MakeFullPath(SystemPath);
  62. internal string GetSavePath(ServiceCtx context, SaveInfo saveInfo, bool isDirectory = true)
  63. {
  64. string saveUserPath = "";
  65. string baseSavePath = NandPath;
  66. ulong currentTitleId = saveInfo.TitleId;
  67. switch (saveInfo.SaveSpaceId)
  68. {
  69. case SaveSpaceId.NandUser: baseSavePath = UserNandPath; break;
  70. case SaveSpaceId.NandSystem: baseSavePath = SystemNandPath; break;
  71. case SaveSpaceId.SdCard: baseSavePath = Path.Combine(SdCardPath, "Nintendo"); break;
  72. }
  73. baseSavePath = Path.Combine(baseSavePath, "save");
  74. if (saveInfo.TitleId == 0 && saveInfo.SaveDataType == SaveDataType.SaveData)
  75. {
  76. currentTitleId = context.Process.TitleId;
  77. }
  78. if (saveInfo.SaveSpaceId == SaveSpaceId.NandUser)
  79. {
  80. saveUserPath = saveInfo.UserId.IsNull ? "savecommon" : saveInfo.UserId.ToString();
  81. }
  82. string savePath = Path.Combine(baseSavePath,
  83. saveInfo.SaveId.ToString("x16"),
  84. saveUserPath,
  85. saveInfo.SaveDataType == SaveDataType.SaveData ? currentTitleId.ToString("x16") : string.Empty);
  86. return MakeFullPath(savePath, isDirectory);
  87. }
  88. public string GetFullPartitionPath(string partitionPath)
  89. {
  90. return MakeFullPath(partitionPath);
  91. }
  92. public string SwitchPathToSystemPath(string switchPath)
  93. {
  94. string[] parts = switchPath.Split(":");
  95. if (parts.Length != 2)
  96. {
  97. return null;
  98. }
  99. return GetFullPath(MakeFullPath(parts[0]), parts[1]);
  100. }
  101. public string SystemPathToSwitchPath(string systemPath)
  102. {
  103. string baseSystemPath = GetBasePath() + Path.DirectorySeparatorChar;
  104. if (systemPath.StartsWith(baseSystemPath))
  105. {
  106. string rawPath = systemPath.Replace(baseSystemPath, "");
  107. int firstSeparatorOffset = rawPath.IndexOf(Path.DirectorySeparatorChar);
  108. if (firstSeparatorOffset == -1)
  109. {
  110. return $"{rawPath}:/";
  111. }
  112. string basePath = rawPath.Substring(0, firstSeparatorOffset);
  113. string fileName = rawPath.Substring(firstSeparatorOffset + 1);
  114. return $"{basePath}:/{fileName}";
  115. }
  116. return null;
  117. }
  118. private string MakeFullPath(string path, bool isDirectory = true)
  119. {
  120. // Handles Common Switch Content Paths
  121. switch (path)
  122. {
  123. case ContentPath.SdCard:
  124. case "@Sdcard":
  125. path = SdCardPath;
  126. break;
  127. case ContentPath.User:
  128. path = UserNandPath;
  129. break;
  130. case ContentPath.System:
  131. path = SystemNandPath;
  132. break;
  133. case ContentPath.SdCardContent:
  134. path = Path.Combine(SdCardPath, "Nintendo", "Contents");
  135. break;
  136. case ContentPath.UserContent:
  137. path = Path.Combine(UserNandPath, "Contents");
  138. break;
  139. case ContentPath.SystemContent:
  140. path = Path.Combine(SystemNandPath, "Contents");
  141. break;
  142. }
  143. string fullPath = Path.Combine(GetBasePath(), path);
  144. if (isDirectory)
  145. {
  146. if (!Directory.Exists(fullPath))
  147. {
  148. Directory.CreateDirectory(fullPath);
  149. }
  150. }
  151. return fullPath;
  152. }
  153. public DriveInfo GetDrive()
  154. {
  155. return new DriveInfo(Path.GetPathRoot(GetBasePath()));
  156. }
  157. public string GetBasePath()
  158. {
  159. string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
  160. return Path.Combine(appDataPath, BasePath);
  161. }
  162. public void Reload()
  163. {
  164. ReloadKeySet();
  165. LocalFileSystem serverBaseFs = new LocalFileSystem(GetBasePath());
  166. DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet);
  167. GameCard = fsServerObjects.GameCard;
  168. FileSystemServerConfig fsServerConfig = new FileSystemServerConfig
  169. {
  170. FsCreators = fsServerObjects.FsCreators,
  171. DeviceOperator = fsServerObjects.DeviceOperator,
  172. ExternalKeySet = KeySet.ExternalKeySet
  173. };
  174. FsServer = new FileSystemServer(fsServerConfig);
  175. FsClient = FsServer.CreateFileSystemClient();
  176. }
  177. private void ReloadKeySet()
  178. {
  179. string keyFile = null;
  180. string titleKeyFile = null;
  181. string consoleKeyFile = null;
  182. string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
  183. LoadSetAtPath(Path.Combine(home, ".switch"));
  184. LoadSetAtPath(GetSystemPath());
  185. void LoadSetAtPath(string basePath)
  186. {
  187. string localKeyFile = Path.Combine(basePath, "prod.keys");
  188. string localTitleKeyFile = Path.Combine(basePath, "title.keys");
  189. string localConsoleKeyFile = Path.Combine(basePath, "console.keys");
  190. if (File.Exists(localKeyFile))
  191. {
  192. keyFile = localKeyFile;
  193. }
  194. if (File.Exists(localTitleKeyFile))
  195. {
  196. titleKeyFile = localTitleKeyFile;
  197. }
  198. if (File.Exists(localConsoleKeyFile))
  199. {
  200. consoleKeyFile = localConsoleKeyFile;
  201. }
  202. }
  203. KeySet = ExternalKeyReader.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile);
  204. }
  205. public void Unload()
  206. {
  207. RomFs?.Dispose();
  208. }
  209. public void Dispose()
  210. {
  211. Dispose(true);
  212. }
  213. protected virtual void Dispose(bool disposing)
  214. {
  215. if (disposing)
  216. {
  217. Unload();
  218. }
  219. }
  220. }
  221. }