2019-12-22 02:49:51 +00:00
|
|
|
|
using Gtk;
|
2020-01-05 11:49:44 +00:00
|
|
|
|
using LibHac;
|
|
|
|
|
using LibHac.Fs;
|
|
|
|
|
using LibHac.Fs.Shim;
|
|
|
|
|
using LibHac.Ncm;
|
2019-12-22 02:49:51 +00:00
|
|
|
|
using Ryujinx.HLE.FileSystem;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Diagnostics;
|
2020-01-05 11:49:44 +00:00
|
|
|
|
using System.Globalization;
|
2019-12-22 02:49:51 +00:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
|
|
|
|
using GUI = Gtk.Builder.ObjectAttribute;
|
|
|
|
|
|
|
|
|
|
namespace Ryujinx.Ui
|
|
|
|
|
{
|
|
|
|
|
public class GameTableContextMenu : Menu
|
|
|
|
|
{
|
|
|
|
|
private static ListStore _gameTableStore;
|
|
|
|
|
private static TreeIter _rowIter;
|
2020-01-24 16:01:21 +00:00
|
|
|
|
private VirtualFileSystem _virtualFileSystem;
|
2019-12-22 02:49:51 +00:00
|
|
|
|
|
|
|
|
|
#pragma warning disable CS0649
|
|
|
|
|
#pragma warning disable IDE0044
|
|
|
|
|
[GUI] MenuItem _openSaveDir;
|
|
|
|
|
#pragma warning restore CS0649
|
|
|
|
|
#pragma warning restore IDE0044
|
|
|
|
|
|
2020-01-24 16:01:21 +00:00
|
|
|
|
public GameTableContextMenu(ListStore gameTableStore, TreeIter rowIter, VirtualFileSystem virtualFileSystem)
|
|
|
|
|
: this(new Builder("Ryujinx.Ui.GameTableContextMenu.glade"), gameTableStore, rowIter, virtualFileSystem) { }
|
2019-12-22 02:49:51 +00:00
|
|
|
|
|
2020-01-24 16:01:21 +00:00
|
|
|
|
private GameTableContextMenu(Builder builder, ListStore gameTableStore, TreeIter rowIter, VirtualFileSystem virtualFileSystem) : base(builder.GetObject("_contextMenu").Handle)
|
2019-12-22 02:49:51 +00:00
|
|
|
|
{
|
|
|
|
|
builder.Autoconnect(this);
|
|
|
|
|
|
|
|
|
|
_openSaveDir.Activated += OpenSaveDir_Clicked;
|
|
|
|
|
|
2020-01-24 16:01:21 +00:00
|
|
|
|
_gameTableStore = gameTableStore;
|
|
|
|
|
_rowIter = rowIter;
|
|
|
|
|
_virtualFileSystem = virtualFileSystem;
|
2019-12-22 02:49:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Events
|
|
|
|
|
private void OpenSaveDir_Clicked(object sender, EventArgs args)
|
|
|
|
|
{
|
|
|
|
|
string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
|
|
|
|
|
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
|
|
|
|
|
2020-01-05 11:49:44 +00:00
|
|
|
|
if (!TryFindSaveData(titleName, titleId, out ulong saveDataId))
|
2019-12-22 02:49:51 +00:00
|
|
|
|
{
|
2020-01-05 11:49:44 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string saveDir = GetSaveDataDirectory(saveDataId);
|
|
|
|
|
|
|
|
|
|
Process.Start(new ProcessStartInfo()
|
|
|
|
|
{
|
|
|
|
|
FileName = saveDir,
|
|
|
|
|
UseShellExecute = true,
|
|
|
|
|
Verb = "open"
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool TryFindSaveData(string titleName, string titleIdText, out ulong saveDataId)
|
|
|
|
|
{
|
|
|
|
|
saveDataId = default;
|
|
|
|
|
|
|
|
|
|
if (!ulong.TryParse(titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleId))
|
|
|
|
|
{
|
|
|
|
|
GtkDialog.CreateErrorDialog("UI error: The selected game did not have a valid title ID");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SaveDataFilter filter = new SaveDataFilter();
|
|
|
|
|
filter.SetUserId(new UserId(1, 0));
|
|
|
|
|
filter.SetTitleId(new TitleId(titleId));
|
|
|
|
|
|
2020-01-24 16:01:21 +00:00
|
|
|
|
Result result = _virtualFileSystem.FsClient.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
|
2020-01-05 11:49:44 +00:00
|
|
|
|
|
|
|
|
|
if (result == ResultFs.TargetNotFound)
|
|
|
|
|
{
|
|
|
|
|
// Savedata was not found. Ask the user if they want to create it
|
|
|
|
|
using MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.YesNo, null)
|
2019-12-22 02:49:51 +00:00
|
|
|
|
{
|
|
|
|
|
Title = "Ryujinx",
|
|
|
|
|
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
|
2020-01-05 11:49:44 +00:00
|
|
|
|
Text = $"There is no savedata for {titleName} [{titleId:x16}]",
|
|
|
|
|
SecondaryText = "Would you like to create savedata for this game?",
|
2019-12-22 02:49:51 +00:00
|
|
|
|
WindowPosition = WindowPosition.Center
|
|
|
|
|
};
|
|
|
|
|
|
2020-01-05 11:49:44 +00:00
|
|
|
|
if (messageDialog.Run() != (int)ResponseType.Yes)
|
2019-12-22 02:49:51 +00:00
|
|
|
|
{
|
2020-01-05 11:49:44 +00:00
|
|
|
|
return false;
|
2019-12-22 02:49:51 +00:00
|
|
|
|
}
|
2020-01-05 11:49:44 +00:00
|
|
|
|
|
2020-01-24 16:01:21 +00:00
|
|
|
|
result = _virtualFileSystem.FsClient.CreateSaveData(new TitleId(titleId), new UserId(1, 0), new TitleId(titleId), 0, 0, 0);
|
2020-01-05 11:49:44 +00:00
|
|
|
|
|
|
|
|
|
if (result.IsFailure())
|
2019-12-22 02:49:51 +00:00
|
|
|
|
{
|
2020-01-05 11:49:44 +00:00
|
|
|
|
GtkDialog.CreateErrorDialog($"There was an error creating the specified savedata: {result.ToStringWithName()}");
|
2019-12-22 02:49:51 +00:00
|
|
|
|
|
2020-01-05 11:49:44 +00:00
|
|
|
|
return false;
|
2019-12-22 02:49:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-05 11:49:44 +00:00
|
|
|
|
// Try to find the savedata again after creating it
|
2020-01-24 16:01:21 +00:00
|
|
|
|
result = _virtualFileSystem.FsClient.FindSaveDataWithFilter(out saveDataInfo, SaveDataSpaceId.User, ref filter);
|
2019-12-22 02:49:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-05 11:49:44 +00:00
|
|
|
|
if (result.IsSuccess())
|
2019-12-22 02:49:51 +00:00
|
|
|
|
{
|
2020-01-05 11:49:44 +00:00
|
|
|
|
saveDataId = saveDataInfo.SaveDataId;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GtkDialog.CreateErrorDialog($"There was an error finding the specified savedata: {result.ToStringWithName()}");
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetSaveDataDirectory(ulong saveDataId)
|
|
|
|
|
{
|
2020-01-24 16:01:21 +00:00
|
|
|
|
string saveRootPath = System.IO.Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
|
2020-01-05 11:49:44 +00:00
|
|
|
|
|
|
|
|
|
if (!Directory.Exists(saveRootPath))
|
|
|
|
|
{
|
|
|
|
|
// Inconsistent state. Create the directory
|
|
|
|
|
Directory.CreateDirectory(saveRootPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string committedPath = System.IO.Path.Combine(saveRootPath, "0");
|
|
|
|
|
string workingPath = System.IO.Path.Combine(saveRootPath, "1");
|
|
|
|
|
|
|
|
|
|
// If the committed directory exists, that path will be loaded the next time the savedata is mounted
|
|
|
|
|
if (Directory.Exists(committedPath))
|
|
|
|
|
{
|
|
|
|
|
return committedPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the working directory exists and the committed directory doesn't,
|
|
|
|
|
// the working directory will be loaded the next time the savedata is mounted
|
|
|
|
|
if (!Directory.Exists(workingPath))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(workingPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return workingPath;
|
2019-12-22 02:49:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|