GameTableContextMenu.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. using Gtk;
  2. using LibHac;
  3. using LibHac.Fs;
  4. using LibHac.Fs.Shim;
  5. using LibHac.Ncm;
  6. using Ryujinx.HLE.FileSystem;
  7. using System;
  8. using System.Diagnostics;
  9. using System.Globalization;
  10. using System.IO;
  11. using System.Reflection;
  12. using GUI = Gtk.Builder.ObjectAttribute;
  13. namespace Ryujinx.Ui
  14. {
  15. public class GameTableContextMenu : Menu
  16. {
  17. private static ListStore _gameTableStore;
  18. private static TreeIter _rowIter;
  19. private FileSystemClient _fsClient;
  20. #pragma warning disable CS0649
  21. #pragma warning disable IDE0044
  22. [GUI] MenuItem _openSaveDir;
  23. #pragma warning restore CS0649
  24. #pragma warning restore IDE0044
  25. public GameTableContextMenu(ListStore gameTableStore, TreeIter rowIter, FileSystemClient fsClient)
  26. : this(new Builder("Ryujinx.Ui.GameTableContextMenu.glade"), gameTableStore, rowIter, fsClient) { }
  27. private GameTableContextMenu(Builder builder, ListStore gameTableStore, TreeIter rowIter, FileSystemClient fsClient) : base(builder.GetObject("_contextMenu").Handle)
  28. {
  29. builder.Autoconnect(this);
  30. _openSaveDir.Activated += OpenSaveDir_Clicked;
  31. _gameTableStore = gameTableStore;
  32. _rowIter = rowIter;
  33. _fsClient = fsClient;
  34. }
  35. //Events
  36. private void OpenSaveDir_Clicked(object sender, EventArgs args)
  37. {
  38. string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
  39. string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
  40. if (!TryFindSaveData(titleName, titleId, out ulong saveDataId))
  41. {
  42. return;
  43. }
  44. string saveDir = GetSaveDataDirectory(saveDataId);
  45. Process.Start(new ProcessStartInfo()
  46. {
  47. FileName = saveDir,
  48. UseShellExecute = true,
  49. Verb = "open"
  50. });
  51. }
  52. private bool TryFindSaveData(string titleName, string titleIdText, out ulong saveDataId)
  53. {
  54. saveDataId = default;
  55. if (!ulong.TryParse(titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleId))
  56. {
  57. GtkDialog.CreateErrorDialog("UI error: The selected game did not have a valid title ID");
  58. return false;
  59. }
  60. SaveDataFilter filter = new SaveDataFilter();
  61. filter.SetUserId(new UserId(1, 0));
  62. filter.SetTitleId(new TitleId(titleId));
  63. Result result = _fsClient.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
  64. if (result == ResultFs.TargetNotFound)
  65. {
  66. // Savedata was not found. Ask the user if they want to create it
  67. using MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.YesNo, null)
  68. {
  69. Title = "Ryujinx",
  70. Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
  71. Text = $"There is no savedata for {titleName} [{titleId:x16}]",
  72. SecondaryText = "Would you like to create savedata for this game?",
  73. WindowPosition = WindowPosition.Center
  74. };
  75. if (messageDialog.Run() != (int)ResponseType.Yes)
  76. {
  77. return false;
  78. }
  79. result = _fsClient.CreateSaveData(new TitleId(titleId), new UserId(1, 0), new TitleId(titleId), 0, 0, 0);
  80. if (result.IsFailure())
  81. {
  82. GtkDialog.CreateErrorDialog($"There was an error creating the specified savedata: {result.ToStringWithName()}");
  83. return false;
  84. }
  85. // Try to find the savedata again after creating it
  86. result = _fsClient.FindSaveDataWithFilter(out saveDataInfo, SaveDataSpaceId.User, ref filter);
  87. }
  88. if (result.IsSuccess())
  89. {
  90. saveDataId = saveDataInfo.SaveDataId;
  91. return true;
  92. }
  93. GtkDialog.CreateErrorDialog($"There was an error finding the specified savedata: {result.ToStringWithName()}");
  94. return false;
  95. }
  96. private string GetSaveDataDirectory(ulong saveDataId)
  97. {
  98. string saveRootPath = System.IO.Path.Combine(new VirtualFileSystem().GetNandPath(), $"user/save/{saveDataId:x16}");
  99. if (!Directory.Exists(saveRootPath))
  100. {
  101. // Inconsistent state. Create the directory
  102. Directory.CreateDirectory(saveRootPath);
  103. }
  104. string committedPath = System.IO.Path.Combine(saveRootPath, "0");
  105. string workingPath = System.IO.Path.Combine(saveRootPath, "1");
  106. // If the committed directory exists, that path will be loaded the next time the savedata is mounted
  107. if (Directory.Exists(committedPath))
  108. {
  109. return committedPath;
  110. }
  111. // If the working directory exists and the committed directory doesn't,
  112. // the working directory will be loaded the next time the savedata is mounted
  113. if (!Directory.Exists(workingPath))
  114. {
  115. Directory.CreateDirectory(workingPath);
  116. }
  117. return workingPath;
  118. }
  119. }
  120. }