Migration.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using Gtk;
  2. using LibHac;
  3. using Ryujinx.Common.Configuration;
  4. using Ryujinx.HLE.FileSystem;
  5. using System;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Reflection;
  9. namespace Ryujinx.Ui
  10. {
  11. internal class Migration
  12. {
  13. private VirtualFileSystem _virtualFileSystem;
  14. public Migration(VirtualFileSystem virtualFileSystem)
  15. {
  16. _virtualFileSystem = virtualFileSystem;
  17. }
  18. public static bool PromptIfMigrationNeededForStartup(Window parentWindow, out bool isMigrationNeeded)
  19. {
  20. if (!IsMigrationNeeded())
  21. {
  22. isMigrationNeeded = false;
  23. return true;
  24. }
  25. isMigrationNeeded = true;
  26. int dialogResponse;
  27. using (MessageDialog dialog = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Question,
  28. ButtonsType.YesNo, "What's this?"))
  29. {
  30. dialog.Title = "Data Migration Needed";
  31. dialog.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
  32. dialog.Text =
  33. "The folder structure of Ryujinx's RyuFs folder has been updated and renamed to \"Ryujinx\". " +
  34. "Your RyuFs folder must be copied and migrated to the new \"Ryujinx\" structure. Would you like to do the migration now?\n\n" +
  35. "Select \"Yes\" to automatically perform the migration. Your old RyuFs folder will remain as it is.\n\n" +
  36. "Selecting \"No\" will exit Ryujinx without changing anything.";
  37. dialogResponse = dialog.Run();
  38. }
  39. return dialogResponse == (int)ResponseType.Yes;
  40. }
  41. public static bool DoMigrationForStartup(MainWindow parentWindow, VirtualFileSystem virtualFileSystem)
  42. {
  43. try
  44. {
  45. Migration migration = new Migration(virtualFileSystem);
  46. int saveCount = migration.Migrate();
  47. using MessageDialog dialogSuccess = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, null)
  48. {
  49. Title = "Migration Success",
  50. Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
  51. Text = $"Data migration was successful. {saveCount} saves were migrated.",
  52. };
  53. dialogSuccess.Run();
  54. return true;
  55. }
  56. catch (HorizonResultException ex)
  57. {
  58. GtkDialog.CreateErrorDialog(ex.Message);
  59. return false;
  60. }
  61. }
  62. // Returns the number of saves migrated
  63. public int Migrate()
  64. {
  65. // Make sure FsClient is initialized
  66. _virtualFileSystem.Reload();
  67. string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
  68. string oldBasePath = Path.Combine(appDataPath, "RyuFs");
  69. string newBasePath = Path.Combine(appDataPath, "Ryujinx");
  70. string oldSaveDir = Path.Combine(oldBasePath, "nand/user/save");
  71. CopyRyuFs(oldBasePath, newBasePath);
  72. SaveImporter importer = new SaveImporter(oldSaveDir, _virtualFileSystem.FsClient);
  73. return importer.Import();
  74. }
  75. private static void CopyRyuFs(string oldPath, string newPath)
  76. {
  77. Directory.CreateDirectory(newPath);
  78. CopyExcept(oldPath, newPath, "nand", "bis", "sdmc", "sdcard");
  79. string oldNandPath = Path.Combine(oldPath, "nand");
  80. string newNandPath = Path.Combine(newPath, "bis");
  81. CopyExcept(oldNandPath, newNandPath, "system", "user");
  82. string oldSdPath = Path.Combine(oldPath, "sdmc");
  83. string newSdPath = Path.Combine(newPath, "sdcard");
  84. CopyDirectory(oldSdPath, newSdPath);
  85. string oldSystemPath = Path.Combine(oldNandPath, "system");
  86. string newSystemPath = Path.Combine(newNandPath, "system");
  87. CopyExcept(oldSystemPath, newSystemPath, "save");
  88. string oldUserPath = Path.Combine(oldNandPath, "user");
  89. string newUserPath = Path.Combine(newNandPath, "user");
  90. CopyExcept(oldUserPath, newUserPath, "save");
  91. }
  92. private static void CopyExcept(string srcPath, string dstPath, params string[] exclude)
  93. {
  94. exclude = exclude.Select(x => x.ToLowerInvariant()).ToArray();
  95. DirectoryInfo srcDir = new DirectoryInfo(srcPath);
  96. if (!srcDir.Exists)
  97. {
  98. return;
  99. }
  100. Directory.CreateDirectory(dstPath);
  101. foreach (DirectoryInfo subDir in srcDir.EnumerateDirectories())
  102. {
  103. if (exclude.Contains(subDir.Name.ToLowerInvariant()))
  104. {
  105. continue;
  106. }
  107. CopyDirectory(subDir.FullName, Path.Combine(dstPath, subDir.Name));
  108. }
  109. foreach (FileInfo file in srcDir.EnumerateFiles())
  110. {
  111. file.CopyTo(Path.Combine(dstPath, file.Name));
  112. }
  113. }
  114. private static void CopyDirectory(string srcPath, string dstPath)
  115. {
  116. Directory.CreateDirectory(dstPath);
  117. DirectoryInfo srcDir = new DirectoryInfo(srcPath);
  118. if (!srcDir.Exists)
  119. {
  120. return;
  121. }
  122. Directory.CreateDirectory(dstPath);
  123. foreach (DirectoryInfo subDir in srcDir.EnumerateDirectories())
  124. {
  125. CopyDirectory(subDir.FullName, Path.Combine(dstPath, subDir.Name));
  126. }
  127. foreach (FileInfo file in srcDir.EnumerateFiles())
  128. {
  129. file.CopyTo(Path.Combine(dstPath, file.Name));
  130. }
  131. }
  132. public static bool IsMigrationNeeded()
  133. {
  134. if (AppDataManager.IsCustomBasePath) return false;
  135. string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
  136. string oldBasePath = Path.Combine(appDataPath, "RyuFs");
  137. string newBasePath = Path.Combine(appDataPath, "Ryujinx");
  138. return Directory.Exists(oldBasePath) && !Directory.Exists(newBasePath);
  139. }
  140. }
  141. }