Migration.cs 6.2 KB

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