gui: Refactoring Part 1 (#1859)
* gui: Refactoring Part 1 * Fix ProfileDialog.glade path * Fix Application.Quit assert * Fix TitleUpdateWindow parent * Fix TitleUpdate selected item * Remove extra line in TitleUpdateWindow * Fix empty assign of Enum.TryParse * Add Patrons list in the About Window * update about error messages
16
Ryujinx.Common/System/ForceDedicatedGpu.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.System
|
||||||
|
{
|
||||||
|
public static class ForceDedicatedGpu
|
||||||
|
{
|
||||||
|
public static void Nvidia()
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
// NOTE: If the DLL exists, we can load it to force the usage of the dedicated Nvidia Gpu.
|
||||||
|
NativeLibrary.TryLoad("nvapi64.dll", out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,14 +17,14 @@ namespace Ryujinx.Common.System
|
||||||
public uint wPeriodMax;
|
public uint wPeriodMax;
|
||||||
};
|
};
|
||||||
|
|
||||||
[DllImport("winmm.dll", SetLastError = true)]
|
[DllImport("winmm.dll", EntryPoint = "timeGetDevCaps", SetLastError = true)]
|
||||||
private static extern uint timeGetDevCaps(ref TimeCaps timeCaps, uint sizeTimeCaps);
|
private static extern uint TimeGetDevCaps(ref TimeCaps timeCaps, uint sizeTimeCaps);
|
||||||
|
|
||||||
[DllImport("winmm.dll")]
|
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
|
||||||
private static extern uint timeBeginPeriod(uint uMilliseconds);
|
private static extern uint TimeBeginPeriod(uint uMilliseconds);
|
||||||
|
|
||||||
[DllImport("winmm.dll")]
|
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
|
||||||
private static extern uint timeEndPeriod(uint uMilliseconds);
|
private static extern uint TimeEndPeriod(uint uMilliseconds);
|
||||||
|
|
||||||
private uint _targetResolutionInMilliseconds;
|
private uint _targetResolutionInMilliseconds;
|
||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
|
@ -45,7 +45,7 @@ namespace Ryujinx.Common.System
|
||||||
{
|
{
|
||||||
TimeCaps timeCaps = default;
|
TimeCaps timeCaps = default;
|
||||||
|
|
||||||
uint result = timeGetDevCaps(ref timeCaps, (uint)Unsafe.SizeOf<TimeCaps>());
|
uint result = TimeGetDevCaps(ref timeCaps, (uint)Unsafe.SizeOf<TimeCaps>());
|
||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
|
@ -66,7 +66,7 @@ namespace Ryujinx.Common.System
|
||||||
|
|
||||||
private void Activate()
|
private void Activate()
|
||||||
{
|
{
|
||||||
uint result = timeBeginPeriod(_targetResolutionInMilliseconds);
|
uint result = TimeBeginPeriod(_targetResolutionInMilliseconds);
|
||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
|
@ -82,7 +82,7 @@ namespace Ryujinx.Common.System
|
||||||
{
|
{
|
||||||
if (_isActive)
|
if (_isActive)
|
||||||
{
|
{
|
||||||
uint result = timeEndPeriod(_targetResolutionInMilliseconds);
|
uint result = TimeEndPeriod(_targetResolutionInMilliseconds);
|
||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
|
@ -98,6 +98,7 @@ namespace Ryujinx.Common.System
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
|
@ -108,4 +109,4 @@ namespace Ryujinx.Common.System
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
using DiscordRPC;
|
using DiscordRPC;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Configuration;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Configuration
|
namespace Ryujinx.Modules
|
||||||
{
|
{
|
||||||
static class DiscordIntegrationModule
|
static class DiscordIntegrationModule
|
||||||
{
|
{
|
||||||
private static DiscordRpcClient _discordClient;
|
private static DiscordRpcClient _discordClient;
|
||||||
|
|
||||||
private static string LargeDescription = "Ryujinx is a Nintendo Switch emulator.";
|
private const string LargeDescription = "Ryujinx is a Nintendo Switch emulator.";
|
||||||
|
|
||||||
public static RichPresence DiscordPresence { get; private set; }
|
public static RichPresence DiscordPresence { get; private set; }
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ namespace Ryujinx.Configuration
|
||||||
{
|
{
|
||||||
DiscordPresence = new RichPresence
|
DiscordPresence = new RichPresence
|
||||||
{
|
{
|
||||||
Assets = new Assets
|
Assets = new Assets
|
||||||
{
|
{
|
||||||
LargeImageKey = "ryujinx",
|
LargeImageKey = "ryujinx",
|
||||||
LargeImageText = LargeDescription
|
LargeImageText = LargeDescription
|
||||||
|
@ -169,4 +170,4 @@ namespace Ryujinx.Configuration
|
||||||
"051337133769a000", // RGB-Seizure
|
"051337133769a000", // RGB-Seizure
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,7 +11,7 @@ using System.Net.Sockets;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
public class Client : IDisposable
|
public class Client : IDisposable
|
||||||
{
|
{
|
||||||
|
@ -396,7 +396,7 @@ namespace Ryujinx.Motion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void RequestData(int clientId, int slot)
|
public unsafe void RequestData(int clientId, int slot)
|
||||||
{
|
{
|
||||||
if (!_active)
|
if (!_active)
|
||||||
{
|
{
|
||||||
|
@ -439,7 +439,7 @@ namespace Ryujinx.Motion
|
||||||
{
|
{
|
||||||
Header header = new Header()
|
Header header = new Header()
|
||||||
{
|
{
|
||||||
ID = (uint)clientId,
|
Id = (uint)clientId,
|
||||||
MagicString = Magic,
|
MagicString = Magic,
|
||||||
Version = Version,
|
Version = Version,
|
||||||
Length = 0,
|
Length = 0,
|
||||||
|
@ -456,4 +456,4 @@ namespace Ryujinx.Motion
|
||||||
CloseClients();
|
CloseClients();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ using Ryujinx.Configuration;
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
public class MotionDevice
|
public class MotionDevice
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@ namespace Ryujinx.Motion
|
||||||
public Vector3 Rotation { get; private set; }
|
public Vector3 Rotation { get; private set; }
|
||||||
public float[] Orientation { get; private set; }
|
public float[] Orientation { get; private set; }
|
||||||
|
|
||||||
private Client _motionSource;
|
private readonly Client _motionSource;
|
||||||
|
|
||||||
public MotionDevice(Client motionSource)
|
public MotionDevice(Client motionSource)
|
||||||
{
|
{
|
||||||
|
@ -51,8 +51,8 @@ namespace Ryujinx.Motion
|
||||||
}
|
}
|
||||||
|
|
||||||
Gyroscope = Truncate(input.Gyroscrope * 0.0027f, 3);
|
Gyroscope = Truncate(input.Gyroscrope * 0.0027f, 3);
|
||||||
Accelerometer = Truncate(input.Accelerometer, 3);
|
Accelerometer = Truncate(input.Accelerometer, 3);
|
||||||
Rotation = Truncate(input.Rotation * 0.0027f, 3);
|
Rotation = Truncate(input.Rotation * 0.0027f, 3);
|
||||||
|
|
||||||
Matrix4x4 orientation = input.GetOrientation();
|
Matrix4x4 orientation = input.GetOrientation();
|
||||||
|
|
||||||
|
@ -78,4 +78,4 @@ namespace Ryujinx.Motion
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
public class MotionInput
|
public class MotionInput
|
||||||
{
|
{
|
||||||
|
@ -82,4 +82,4 @@ namespace Ryujinx.Motion
|
||||||
return degree * (MathF.PI / 180);
|
return degree * (MathF.PI / 180);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
// MahonyAHRS class. Madgwick's implementation of Mayhony's AHRS algorithm.
|
// MahonyAHRS class. Madgwick's implementation of Mayhony's AHRS algorithm.
|
||||||
// See: https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/
|
// See: https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/
|
||||||
|
@ -43,9 +43,7 @@ namespace Ryujinx.Motion
|
||||||
/// <param name="samplePeriod">
|
/// <param name="samplePeriod">
|
||||||
/// Sample period.
|
/// Sample period.
|
||||||
/// </param>
|
/// </param>
|
||||||
public MotionSensorFilter(float samplePeriod) : this(samplePeriod, 1f, 0f)
|
public MotionSensorFilter(float samplePeriod) : this(samplePeriod, 1f, 0f) { }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
|
/// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
|
||||||
|
@ -56,9 +54,7 @@ namespace Ryujinx.Motion
|
||||||
/// <param name="kp">
|
/// <param name="kp">
|
||||||
/// Algorithm proportional gain.
|
/// Algorithm proportional gain.
|
||||||
/// </param>
|
/// </param>
|
||||||
public MotionSensorFilter(float samplePeriod, float kp) : this(samplePeriod, kp, 0f)
|
public MotionSensorFilter(float samplePeriod, float kp) : this(samplePeriod, kp, 0f) { }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
|
/// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
|
||||||
|
@ -163,4 +159,4 @@ namespace Ryujinx.Motion
|
||||||
Quaternion = Quaternion.Identity;
|
Quaternion = Quaternion.Identity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
struct ControllerDataRequest
|
struct ControllerDataRequest
|
||||||
{
|
{
|
||||||
public MessageType Type;
|
public MessageType Type;
|
||||||
public SubscriberType SubscriberType;
|
public SubscriberType SubscriberType;
|
||||||
public byte Slot;
|
public byte Slot;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||||
public byte[] MacAddress;
|
public byte[] MacAddress;
|
||||||
|
@ -17,21 +17,22 @@ namespace Ryujinx.Motion
|
||||||
public struct ControllerDataResponse
|
public struct ControllerDataResponse
|
||||||
{
|
{
|
||||||
public SharedResponse Shared;
|
public SharedResponse Shared;
|
||||||
public byte Connected;
|
public byte Connected;
|
||||||
public uint PacketID;
|
public uint PacketId;
|
||||||
public byte ExtraButtons;
|
public byte ExtraButtons;
|
||||||
public byte MainButtons;
|
public byte MainButtons;
|
||||||
public ushort PSExtraInput;
|
public ushort PSExtraInput;
|
||||||
public ushort LeftStickXY;
|
public ushort LeftStickXY;
|
||||||
public ushort RightStickXY;
|
public ushort RightStickXY;
|
||||||
public uint DPadAnalog;
|
public uint DPadAnalog;
|
||||||
public ulong MainButtonsAnalog;
|
public ulong MainButtonsAnalog;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||||
public byte[] Touch1;
|
public byte[] Touch1;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||||
public byte[] Touch2;
|
public byte[] Touch2;
|
||||||
|
|
||||||
public ulong MotionTimestamp;
|
public ulong MotionTimestamp;
|
||||||
public float AccelerometerX;
|
public float AccelerometerX;
|
||||||
public float AccelerometerY;
|
public float AccelerometerY;
|
||||||
|
@ -43,8 +44,8 @@ namespace Ryujinx.Motion
|
||||||
|
|
||||||
enum SubscriberType : byte
|
enum SubscriberType : byte
|
||||||
{
|
{
|
||||||
All = 0,
|
All,
|
||||||
Slot,
|
Slot,
|
||||||
Mac
|
Mac
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,21 +1,21 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public struct ControllerInfoResponse
|
public struct ControllerInfoResponse
|
||||||
{
|
{
|
||||||
public SharedResponse Shared;
|
public SharedResponse Shared;
|
||||||
private byte _zero;
|
private byte _zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public struct ControllerInfoRequest
|
public struct ControllerInfoRequest
|
||||||
{
|
{
|
||||||
public MessageType Type;
|
public MessageType Type;
|
||||||
public int PortsCount;
|
public int PortsCount;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
public byte[] PortIndices;
|
public byte[] PortIndices;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public struct Header
|
public struct Header
|
||||||
{
|
{
|
||||||
public uint MagicString;
|
public uint MagicString;
|
||||||
public ushort Version;
|
public ushort Version;
|
||||||
public ushort Length;
|
public ushort Length;
|
||||||
public uint Crc32;
|
public uint Crc32;
|
||||||
public uint ID;
|
public uint Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
public enum MessageType : uint
|
public enum MessageType : uint
|
||||||
{
|
{
|
||||||
|
@ -6,4 +6,4 @@
|
||||||
Info,
|
Info,
|
||||||
Data
|
Data
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,45 +1,45 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Motion
|
namespace Ryujinx.Modules.Motion
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
public struct SharedResponse
|
public struct SharedResponse
|
||||||
{
|
{
|
||||||
public MessageType Type;
|
public MessageType Type;
|
||||||
public byte Slot;
|
public byte Slot;
|
||||||
public SlotState State;
|
public SlotState State;
|
||||||
public DeviceModelType ModelType;
|
public DeviceModelType ModelType;
|
||||||
public ConnectionType ConnectionType;
|
public ConnectionType ConnectionType;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||||
public byte[] MacAddress;
|
public byte[] MacAddress;
|
||||||
public BatteryStatus BatteryStatus;
|
public BatteryStatus BatteryStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SlotState : byte
|
public enum SlotState : byte
|
||||||
{
|
{
|
||||||
Disconnected = 0,
|
Disconnected,
|
||||||
Reserved,
|
Reserved,
|
||||||
Connected
|
Connected
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DeviceModelType : byte
|
public enum DeviceModelType : byte
|
||||||
{
|
{
|
||||||
None = 0,
|
None,
|
||||||
PartialGyro,
|
PartialGyro,
|
||||||
FullGyro
|
FullGyro
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ConnectionType : byte
|
public enum ConnectionType : byte
|
||||||
{
|
{
|
||||||
None = 0,
|
None,
|
||||||
USB,
|
USB,
|
||||||
Bluetooth
|
Bluetooth
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum BatteryStatus : byte
|
public enum BatteryStatus : byte
|
||||||
{
|
{
|
||||||
NA = 0,
|
NA,
|
||||||
Dying,
|
Dying,
|
||||||
Low,
|
Low,
|
||||||
Medium,
|
Medium,
|
||||||
|
@ -48,4 +48,4 @@ namespace Ryujinx.Motion
|
||||||
Charging,
|
Charging,
|
||||||
Charged
|
Charged
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,28 +1,29 @@
|
||||||
using Gdk;
|
using Gdk;
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using Mono.Unix;
|
using Mono.Unix;
|
||||||
|
using Ryujinx.Ui;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Modules
|
||||||
{
|
{
|
||||||
public class UpdateDialog : Gtk.Window
|
public class UpdateDialog : Gtk.Window
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649, IDE0044
|
#pragma warning disable CS0649, IDE0044
|
||||||
[Builder.Object] public Label MainText;
|
[Builder.Object] public Label MainText;
|
||||||
[Builder.Object] public Label SecondaryText;
|
[Builder.Object] public Label SecondaryText;
|
||||||
[Builder.Object] public LevelBar ProgressBar;
|
[Builder.Object] public LevelBar ProgressBar;
|
||||||
[Builder.Object] public Button YesButton;
|
[Builder.Object] public Button YesButton;
|
||||||
[Builder.Object] public Button NoButton;
|
[Builder.Object] public Button NoButton;
|
||||||
#pragma warning restore CS0649, IDE0044
|
#pragma warning restore CS0649, IDE0044
|
||||||
|
|
||||||
private readonly MainWindow _mainWindow;
|
private readonly MainWindow _mainWindow;
|
||||||
private readonly string _buildUrl;
|
private readonly string _buildUrl;
|
||||||
private bool _restartQuery;
|
private bool _restartQuery;
|
||||||
|
|
||||||
public UpdateDialog(MainWindow mainWindow, Version newVersion, string buildUrl) : this(new Builder("Ryujinx.Updater.UpdateDialog.glade"), mainWindow, newVersion, buildUrl) { }
|
public UpdateDialog(MainWindow mainWindow, Version newVersion, string buildUrl) : this(new Builder("Ryujinx..Modules.Updater.UpdateDialog.glade"), mainWindow, newVersion, buildUrl) { }
|
||||||
|
|
||||||
private UpdateDialog(Builder builder, MainWindow mainWindow, Version newVersion, string buildUrl) : base(builder.GetObject("UpdateDialog").Handle)
|
private UpdateDialog(Builder builder, MainWindow mainWindow, Version newVersion, string buildUrl) : base(builder.GetObject("UpdateDialog").Handle)
|
||||||
{
|
{
|
||||||
|
@ -36,17 +37,17 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
ProgressBar.Hide();
|
ProgressBar.Hide();
|
||||||
|
|
||||||
YesButton.Clicked += YesButton_Pressed;
|
YesButton.Clicked += YesButton_Clicked;
|
||||||
NoButton.Clicked += NoButton_Pressed;
|
NoButton.Clicked += NoButton_Clicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void YesButton_Pressed(object sender, EventArgs args)
|
private void YesButton_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
if (_restartQuery)
|
if (_restartQuery)
|
||||||
{
|
{
|
||||||
string ryuName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Ryujinx.exe" : "Ryujinx";
|
string ryuName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Ryujinx.exe" : "Ryujinx";
|
||||||
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
||||||
string ryuArg = String.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());
|
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());
|
||||||
|
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
|
@ -60,7 +61,7 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.Window.Functions = _mainWindow.Window.Functions = WMFunction.All & WMFunction.Close;
|
Window.Functions = _mainWindow.Window.Functions = WMFunction.All & WMFunction.Close;
|
||||||
_mainWindow.ExitMenuItem.Sensitive = false;
|
_mainWindow.ExitMenuItem.Sensitive = false;
|
||||||
|
|
||||||
YesButton.Hide();
|
YesButton.Hide();
|
||||||
|
@ -74,7 +75,7 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NoButton_Pressed(object sender, EventArgs args)
|
private void NoButton_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
Updater.Running = false;
|
Updater.Running = false;
|
||||||
_mainWindow.Window.Functions = WMFunction.All;
|
_mainWindow.Window.Functions = WMFunction.All;
|
||||||
|
@ -82,7 +83,7 @@ namespace Ryujinx.Ui
|
||||||
_mainWindow.ExitMenuItem.Sensitive = true;
|
_mainWindow.ExitMenuItem.Sensitive = true;
|
||||||
_mainWindow.UpdateMenuItem.Sensitive = true;
|
_mainWindow.UpdateMenuItem.Sensitive = true;
|
||||||
|
|
||||||
this.Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ using ICSharpCode.SharpZipLib.Zip;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Ui;
|
using Ryujinx.Ui;
|
||||||
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
@ -13,7 +14,7 @@ using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx
|
namespace Ryujinx.Modules
|
||||||
{
|
{
|
||||||
public static class Updater
|
public static class Updater
|
||||||
{
|
{
|
||||||
|
@ -84,7 +85,7 @@ namespace Ryujinx
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
GtkDialog.CreateInfoDialog("Ryujinx - Updater", "You are already using the most updated version of Ryujinx!", "");
|
GtkDialog.CreateUpdaterInfoDialog("You are already using the most updated version of Ryujinx!", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -115,7 +116,7 @@ namespace Ryujinx
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
GtkDialog.CreateInfoDialog("Ryujinx - Updater", "You are already using the most updated version of Ryujinx!", "");
|
GtkDialog.CreateUpdaterInfoDialog("You are already using the most updated version of Ryujinx!", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
Running = false;
|
Running = false;
|
|
@ -3,10 +3,12 @@ using Gtk;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.System;
|
||||||
using Ryujinx.Common.SystemInfo;
|
using Ryujinx.Common.SystemInfo;
|
||||||
using Ryujinx.Configuration;
|
using Ryujinx.Configuration;
|
||||||
|
using Ryujinx.Modules;
|
||||||
using Ryujinx.Ui;
|
using Ryujinx.Ui;
|
||||||
using Ryujinx.Ui.Diagnostic;
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
@ -22,10 +24,11 @@ namespace Ryujinx
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
// Parse Arguments
|
// Parse Arguments.
|
||||||
string launchPath = null;
|
string launchPathArg = null;
|
||||||
string baseDirPath = null;
|
string baseDirPathArg = null;
|
||||||
bool startFullscreen = false;
|
bool startFullscreenArg = false;
|
||||||
|
|
||||||
for (int i = 0; i < args.Length; ++i)
|
for (int i = 0; i < args.Length; ++i)
|
||||||
{
|
{
|
||||||
string arg = args[i];
|
string arg = args[i];
|
||||||
|
@ -35,28 +38,28 @@ namespace Ryujinx
|
||||||
if (i + 1 >= args.Length)
|
if (i + 1 >= args.Length)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
baseDirPath = args[++i];
|
baseDirPathArg = args[++i];
|
||||||
}
|
}
|
||||||
else if (arg == "-f" || arg == "--fullscreen")
|
else if (arg == "-f" || arg == "--fullscreen")
|
||||||
{
|
{
|
||||||
startFullscreen = true;
|
startFullscreenArg = true;
|
||||||
}
|
}
|
||||||
else if (launchPath == null)
|
else if (launchPathArg == null)
|
||||||
{
|
{
|
||||||
launchPath = arg;
|
launchPathArg = arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete backup files after updating
|
// Delete backup files after updating.
|
||||||
Task.Run(Updater.CleanupUpdate);
|
Task.Run(Updater.CleanupUpdate);
|
||||||
|
|
||||||
Toolkit.Init(new ToolkitOptions
|
Toolkit.Init(new ToolkitOptions
|
||||||
{
|
{
|
||||||
Backend = PlatformBackend.PreferNative,
|
Backend = PlatformBackend.PreferNative
|
||||||
EnableHighResolution = true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
Version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
||||||
|
@ -66,27 +69,27 @@ namespace Ryujinx
|
||||||
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
|
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
|
||||||
Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");
|
Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");
|
||||||
|
|
||||||
// Hook unhandled exception and process exit events
|
// Hook unhandled exception and process exit events.
|
||||||
GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
|
GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
|
||||||
AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
|
AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
|
||||||
AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => ProgramExit();
|
AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit();
|
||||||
|
|
||||||
// Setup base data directory
|
// Setup base data directory.
|
||||||
AppDataManager.Initialize(baseDirPath);
|
AppDataManager.Initialize(baseDirPathArg);
|
||||||
|
|
||||||
// Initialize the configuration
|
// Initialize the configuration.
|
||||||
ConfigurationState.Initialize();
|
ConfigurationState.Initialize();
|
||||||
|
|
||||||
// Initialize the logger system
|
// Initialize the logger system.
|
||||||
LoggerModule.Initialize();
|
LoggerModule.Initialize();
|
||||||
|
|
||||||
// Initialize Discord integration
|
// Initialize Discord integration.
|
||||||
DiscordIntegrationModule.Initialize();
|
DiscordIntegrationModule.Initialize();
|
||||||
|
|
||||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
|
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
|
||||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
|
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
|
||||||
|
|
||||||
// Now load the configuration as the other subsystems are now registered
|
// Now load the configuration as the other subsystems are now registered.
|
||||||
if (File.Exists(localConfigurationPath))
|
if (File.Exists(localConfigurationPath))
|
||||||
{
|
{
|
||||||
ConfigurationPath = localConfigurationPath;
|
ConfigurationPath = localConfigurationPath;
|
||||||
|
@ -105,35 +108,42 @@ namespace Ryujinx
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No configuration, we load the default values and save it on disk
|
// No configuration, we load the default values and save it on disk.
|
||||||
ConfigurationPath = appDataConfigurationPath;
|
ConfigurationPath = appDataConfigurationPath;
|
||||||
|
|
||||||
ConfigurationState.Instance.LoadDefault();
|
ConfigurationState.Instance.LoadDefault();
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(appDataConfigurationPath);
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(appDataConfigurationPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startFullscreen)
|
if (startFullscreenArg)
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.Ui.StartFullscreen.Value = true;
|
ConfigurationState.Instance.Ui.StartFullscreen.Value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logging system informations.
|
||||||
PrintSystemInfo();
|
PrintSystemInfo();
|
||||||
|
|
||||||
|
// Initialize Gtk.
|
||||||
Application.Init();
|
Application.Init();
|
||||||
|
|
||||||
|
// Check if keys exists.
|
||||||
bool hasGlobalProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"));
|
bool hasGlobalProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"));
|
||||||
bool hasAltProdKeys = !AppDataManager.IsCustomBasePath && File.Exists(Path.Combine(AppDataManager.KeysDirPathAlt, "prod.keys"));
|
bool hasAltProdKeys = !AppDataManager.IsCustomBasePath && File.Exists(Path.Combine(AppDataManager.KeysDirPathAlt, "prod.keys"));
|
||||||
if (!hasGlobalProdKeys && !hasAltProdKeys && !Migration.IsMigrationNeeded())
|
if (!hasGlobalProdKeys && !hasAltProdKeys)
|
||||||
{
|
{
|
||||||
UserErrorDialog.CreateUserErrorDialog(UserError.NoKeys);
|
UserErrorDialog.CreateUserErrorDialog(UserError.NoKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force dedicated GPU if we can.
|
||||||
|
ForceDedicatedGpu.Nvidia();
|
||||||
|
|
||||||
|
// Show the main window UI.
|
||||||
MainWindow mainWindow = new MainWindow();
|
MainWindow mainWindow = new MainWindow();
|
||||||
mainWindow.Show();
|
mainWindow.Show();
|
||||||
|
|
||||||
if (launchPath != null)
|
if (launchPathArg != null)
|
||||||
{
|
{
|
||||||
mainWindow.LoadApplication(launchPath);
|
mainWindow.LoadApplication(launchPathArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
||||||
|
@ -147,7 +157,6 @@ namespace Ryujinx
|
||||||
private static void PrintSystemInfo()
|
private static void PrintSystemInfo()
|
||||||
{
|
{
|
||||||
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
|
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
|
||||||
|
|
||||||
Logger.Notice.Print(LogClass.Application, $"Operating System: {SystemInfo.Instance.OsDescription}");
|
Logger.Notice.Print(LogClass.Application, $"Operating System: {SystemInfo.Instance.OsDescription}");
|
||||||
Logger.Notice.Print(LogClass.Application, $"CPU: {SystemInfo.Instance.CpuName}");
|
Logger.Notice.Print(LogClass.Application, $"CPU: {SystemInfo.Instance.CpuName}");
|
||||||
Logger.Notice.Print(LogClass.Application, $"Total RAM: {SystemInfo.Instance.RamSizeInMB}");
|
Logger.Notice.Print(LogClass.Application, $"Total RAM: {SystemInfo.Instance.RamSizeInMB}");
|
||||||
|
@ -161,25 +170,30 @@ namespace Ryujinx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessUnhandledException(Exception e, bool isTerminating)
|
private static void ProcessUnhandledException(Exception ex, bool isTerminating)
|
||||||
{
|
{
|
||||||
Ptc.Close();
|
Ptc.Close();
|
||||||
PtcProfiler.Stop();
|
PtcProfiler.Stop();
|
||||||
|
|
||||||
string message = $"Unhandled exception caught: {e}";
|
string message = $"Unhandled exception caught: {ex}";
|
||||||
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Application, message);
|
Logger.Error?.PrintMsg(LogClass.Application, message);
|
||||||
|
|
||||||
if (Logger.Error == null) Logger.Notice.PrintMsg(LogClass.Application, message);
|
if (Logger.Error == null)
|
||||||
|
{
|
||||||
|
Logger.Notice.PrintMsg(LogClass.Application, message);
|
||||||
|
}
|
||||||
|
|
||||||
if (isTerminating)
|
if (isTerminating)
|
||||||
{
|
{
|
||||||
ProgramExit();
|
Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProgramExit()
|
public static void Exit()
|
||||||
{
|
{
|
||||||
|
DiscordIntegrationModule.Exit();
|
||||||
|
|
||||||
Ptc.Dispose();
|
Ptc.Dispose();
|
||||||
PtcProfiler.Dispose();
|
PtcProfiler.Dispose();
|
||||||
|
|
||||||
|
|
|
@ -55,53 +55,51 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Ui\AboutWindow.glade" />
|
|
||||||
<None Remove="Ui\assets\JoyConLeft.svg" />
|
|
||||||
<None Remove="Ui\assets\JoyConPair.svg" />
|
|
||||||
<None Remove="Ui\assets\JoyConRight.svg" />
|
|
||||||
<None Remove="Ui\assets\ProCon.svg" />
|
|
||||||
<None Remove="Ui\assets\NCAIcon.png" />
|
|
||||||
<None Remove="Ui\assets\NROIcon.png" />
|
|
||||||
<None Remove="Ui\assets\NSOIcon.png" />
|
|
||||||
<None Remove="Ui\assets\NSPIcon.png" />
|
|
||||||
<None Remove="Ui\assets\XCIIcon.png" />
|
|
||||||
<None Remove="Ui\assets\DiscordLogo.png" />
|
|
||||||
<None Remove="Ui\assets\GitHubLogo.png" />
|
|
||||||
<None Remove="Ui\assets\PatreonLogo.png" />
|
|
||||||
<None Remove="Ui\assets\Icon.png" />
|
|
||||||
<None Remove="Ui\assets\TwitterLogo.png" />
|
|
||||||
<None Remove="Ui\ControllerWindow.glade" />
|
|
||||||
<None Remove="Ui\DlcWindow.glade" />
|
|
||||||
<None Remove="Ui\MainWindow.glade" />
|
<None Remove="Ui\MainWindow.glade" />
|
||||||
<None Remove="Ui\ProfileDialog.glade" />
|
<None Remove="Ui\Resources\Controller_JoyConLeft.svg" />
|
||||||
<None Remove="Ui\SettingsWindow.glade" />
|
<None Remove="Ui\Resources\Controller_JoyConPair.svg" />
|
||||||
<None Remove="Ui\TitleUpdateWindow.glade" />
|
<None Remove="Ui\Resources\Controller_JoyConRight.svg" />
|
||||||
<None Remove="Ui\UpdateDialog.glade" />
|
<None Remove="Ui\Resources\Controller_ProCon.svg" />
|
||||||
|
<None Remove="Ui\Resources\Icon_NCA.png" />
|
||||||
|
<None Remove="Ui\Resources\Icon_NRO.png" />
|
||||||
|
<None Remove="Ui\Resources\Icon_NSO.png" />
|
||||||
|
<None Remove="Ui\Resources\Icon_NSP.png" />
|
||||||
|
<None Remove="Ui\Resources\Icon_XCI.png" />
|
||||||
|
<None Remove="Ui\Resources\Logo_Discord.png" />
|
||||||
|
<None Remove="Ui\Resources\Logo_GitHub.png" />
|
||||||
|
<None Remove="Ui\Resources\Logo_Patreon.png" />
|
||||||
|
<None Remove="Ui\Resources\Logo_Ryujinx.png" />
|
||||||
|
<None Remove="Ui\Resources\Logo_Twitter.png" />
|
||||||
|
<None Remove="Ui\Widgets\ProfileDialog.glade" />
|
||||||
|
<None Remove="Ui\Windows\ControllerWindow.glade" />
|
||||||
|
<None Remove="Ui\Windows\DlcWindow.glade" />
|
||||||
|
<None Remove="Ui\Windows\SettingsWindow.glade" />
|
||||||
|
<None Remove="Ui\Windows\TitleUpdateWindow.glade" />
|
||||||
|
<None Remove="Modules\Updater\UpdateDialog.glade" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Ui\AboutWindow.glade" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\JoyConLeft.svg" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\JoyConPair.svg" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\JoyConRight.svg" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\ProCon.svg" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\NCAIcon.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\NROIcon.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\NSOIcon.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\NSPIcon.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\XCIIcon.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\DiscordLogo.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\GitHubLogo.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\PatreonLogo.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\Icon.png" />
|
|
||||||
<EmbeddedResource Include="Ui\assets\TwitterLogo.png" />
|
|
||||||
<EmbeddedResource Include="Ui\ControllerWindow.glade" />
|
|
||||||
<EmbeddedResource Include="Ui\MainWindow.glade" />
|
<EmbeddedResource Include="Ui\MainWindow.glade" />
|
||||||
<EmbeddedResource Include="Ui\ProfileDialog.glade" />
|
<EmbeddedResource Include="Ui\Resources\Controller_JoyConLeft.svg" />
|
||||||
<EmbeddedResource Include="Ui\SettingsWindow.glade" />
|
<EmbeddedResource Include="Ui\Resources\Controller_JoyConPair.svg" />
|
||||||
<EmbeddedResource Include="Ui\DlcWindow.glade" />
|
<EmbeddedResource Include="Ui\Resources\Controller_JoyConRight.svg" />
|
||||||
<EmbeddedResource Include="Ui\TitleUpdateWindow.glade" />
|
<EmbeddedResource Include="Ui\Resources\Controller_ProCon.svg" />
|
||||||
<EmbeddedResource Include="Updater\UpdateDialog.glade" />
|
<EmbeddedResource Include="Ui\Resources\Icon_NCA.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Icon_NRO.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Icon_NSO.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Icon_NSP.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Icon_XCI.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Logo_Discord.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Logo_GitHub.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Logo_Patreon.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Logo_Ryujinx.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Resources\Logo_Twitter.png" />
|
||||||
|
<EmbeddedResource Include="Ui\Widgets\ProfileDialog.glade" />
|
||||||
|
<EmbeddedResource Include="Ui\Windows\ControllerWindow.glade" />
|
||||||
|
<EmbeddedResource Include="Ui\Windows\DlcWindow.glade" />
|
||||||
|
<EmbeddedResource Include="Ui\Windows\SettingsWindow.glade" />
|
||||||
|
<EmbeddedResource Include="Ui\Windows\TitleUpdateWindow.glade" />
|
||||||
|
<EmbeddedResource Include="Modules\Updater\UpdateDialog.glade" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
using Gtk;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
|
||||||
{
|
|
||||||
public class AboutWindow : Window
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
#pragma warning disable IDE0044
|
|
||||||
[GUI] Label _versionText;
|
|
||||||
[GUI] Image _ryujinxLogo;
|
|
||||||
[GUI] Image _patreonLogo;
|
|
||||||
[GUI] Image _gitHubLogo;
|
|
||||||
[GUI] Image _discordLogo;
|
|
||||||
[GUI] Image _twitterLogo;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
#pragma warning restore IDE0044
|
|
||||||
|
|
||||||
public AboutWindow() : this(new Builder("Ryujinx.Ui.AboutWindow.glade")) { }
|
|
||||||
|
|
||||||
private AboutWindow(Builder builder) : base(builder.GetObject("_aboutWin").Handle)
|
|
||||||
{
|
|
||||||
builder.Autoconnect(this);
|
|
||||||
|
|
||||||
this.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
|
||||||
_ryujinxLogo.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png" , 100, 100);
|
|
||||||
_patreonLogo.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.PatreonLogo.png", 30 , 30 );
|
|
||||||
_gitHubLogo.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.GitHubLogo.png" , 30 , 30 );
|
|
||||||
_discordLogo.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.DiscordLogo.png", 30 , 30 );
|
|
||||||
_twitterLogo.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.TwitterLogo.png", 30 , 30 );
|
|
||||||
|
|
||||||
_versionText.Text = Program.Version;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Events
|
|
||||||
private void RyujinxButton_Pressed(object sender, ButtonPressEventArgs args)
|
|
||||||
{
|
|
||||||
UrlHelper.OpenUrl("https://ryujinx.org");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PatreonButton_Pressed(object sender, ButtonPressEventArgs args)
|
|
||||||
{
|
|
||||||
UrlHelper.OpenUrl("https://www.patreon.com/ryujinx");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GitHubButton_Pressed(object sender, ButtonPressEventArgs args)
|
|
||||||
{
|
|
||||||
UrlHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DiscordButton_Pressed(object sender, ButtonPressEventArgs args)
|
|
||||||
{
|
|
||||||
UrlHelper.OpenUrl("https://discordapp.com/invite/N2FmfVc");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TwitterButton_Pressed(object sender, ButtonPressEventArgs args)
|
|
||||||
{
|
|
||||||
UrlHelper.OpenUrl("https://twitter.com/RyujinxEmu");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ContributorsButton_Pressed(object sender, ButtonPressEventArgs args)
|
|
||||||
{
|
|
||||||
UrlHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CloseToggle_Activated(object sender, EventArgs args)
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,574 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Generated with glade 3.22.1 -->
|
|
||||||
<interface>
|
|
||||||
<requires lib="gtk+" version="3.20"/>
|
|
||||||
<object class="GtkDialog" id="_aboutWin">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="resizable">False</property>
|
|
||||||
<property name="modal">True</property>
|
|
||||||
<property name="window_position">center</property>
|
|
||||||
<property name="default_width">800</property>
|
|
||||||
<property name="default_height">350</property>
|
|
||||||
<property name="type_hint">dialog</property>
|
|
||||||
<child type="titlebar">
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child internal-child="vbox">
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child internal-child="action_area">
|
|
||||||
<object class="GtkButtonBox">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="bigBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="leftBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_left">10</property>
|
|
||||||
<property name="margin_right">15</property>
|
|
||||||
<property name="margin_top">10</property>
|
|
||||||
<property name="margin_bottom">15</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="valign">start</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="_ryujinxLogo">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_left">10</property>
|
|
||||||
<property name="margin_right">10</property>
|
|
||||||
<property name="margin_top">10</property>
|
|
||||||
<property name="margin_bottom">10</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Ryujinx</property>
|
|
||||||
<property name="justify">center</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="2.7000000000000002"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">(REE-YOU-JI-NX)</property>
|
|
||||||
<property name="justify">center</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEventBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<signal name="button-press-event" handler="RyujinxButton_Pressed" swapped="no"/>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_text" translatable="yes">Click to open the Ryujinx website in your default browser</property>
|
|
||||||
<property name="label" translatable="yes">www.ryujinx.org</property>
|
|
||||||
<property name="justify">center</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="underline" value="True"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="padding">5</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="_versionText">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Version x.x.x (Commit Number)</property>
|
|
||||||
<property name="justify">center</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="padding">2</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="license">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">MIT License</property>
|
|
||||||
<property name="justify">center</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="padding">5</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="disclaimer">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Ryujinx is not affiliated with Nintendo,
|
|
||||||
or any of its partners, in any way</property>
|
|
||||||
<property name="justify">center</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="scale" value="0.80000000000000004"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="padding">5</property>
|
|
||||||
<property name="position">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_top">25</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEventBox" id="PatreonButton">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_text" translatable="yes">Click to open the Ryujinx Patreon page in your default browser</property>
|
|
||||||
<signal name="button-press-event" handler="PatreonButton_Pressed" swapped="no"/>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="_patreonLogo">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Patreon</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEventBox" id="GitHubButton">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_text" translatable="yes">Click to open the Ryujinx GitHub page in your default browser</property>
|
|
||||||
<signal name="button-press-event" handler="GitHubButton_Pressed" swapped="no"/>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="_gitHubLogo">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">GitHub</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEventBox" id="DiscordButton">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_text" translatable="yes">Click to open an invite to the Ryujinx Discord server in your default browser</property>
|
|
||||||
<signal name="button-press-event" handler="DiscordButton_Pressed" swapped="no"/>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="_discordLogo">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Discord</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEventBox" id="TwitterButton">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="tooltip_text" translatable="yes">Click to open the Ryujinx Twitter page in your default browser</property>
|
|
||||||
<signal name="button-press-event" handler="TwitterButton_Pressed" swapped="no"/>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage" id="_twitterLogo">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Twitter</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="pack_type">end</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSeparator">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_top">10</property>
|
|
||||||
<property name="margin_bottom">10</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="rightBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_left">15</property>
|
|
||||||
<property name="margin_right">10</property>
|
|
||||||
<property name="margin_top">40</property>
|
|
||||||
<property name="margin_bottom">15</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="label" translatable="yes">About</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="bold"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="margin_left">10</property>
|
|
||||||
<property name="label" translatable="yes">Ryujinx is an emulator for the Nintendo Switch.
|
|
||||||
Please support us on Patreon.
|
|
||||||
Get all the latest news on our Twitter or Discord.
|
|
||||||
Developers interested in contributing can find out more on our Discord.</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="padding">5</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="label" translatable="yes">Created By:</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="bold"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="padding">5</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_left">10</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkViewport">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="baseline_position">top</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="label" translatable="yes">gdkchan
|
|
||||||
LDj3SNuD
|
|
||||||
Ac_K
|
|
||||||
Thog</property>
|
|
||||||
<property name="yalign">0</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="label" translatable="yes">»jD«
|
|
||||||
emmaus
|
|
||||||
Thealexbarney
|
|
||||||
Andy A (BaronKiko)</property>
|
|
||||||
<property name="yalign">0</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEventBox" id="ContributorsButton">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="valign">start</property>
|
|
||||||
<signal name="button-press-event" handler="ContributorsButton_Pressed" swapped="no"/>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="margin_right">5</property>
|
|
||||||
<property name="label" translatable="yes">All Contributors...</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="underline" value="True"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.App
|
||||||
{
|
{
|
||||||
public class ApplicationAddedEventArgs : EventArgs
|
public class ApplicationAddedEventArgs : EventArgs
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.App
|
||||||
{
|
{
|
||||||
public class ApplicationCountUpdatedEventArgs : EventArgs
|
public class ApplicationCountUpdatedEventArgs : EventArgs
|
||||||
{
|
{
|
|
@ -1,10 +1,9 @@
|
||||||
using LibHac;
|
using LibHac.Common;
|
||||||
using LibHac.Common;
|
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.App
|
||||||
{
|
{
|
||||||
public struct ApplicationData
|
public class ApplicationData
|
||||||
{
|
{
|
||||||
public bool Favorite { get; set; }
|
public bool Favorite { get; set; }
|
||||||
public byte[] Icon { get; set; }
|
public byte[] Icon { get; set; }
|
||||||
|
@ -19,4 +18,4 @@ namespace Ryujinx.Ui
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
|
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,6 +11,7 @@ using Ryujinx.Configuration.System;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -20,24 +21,45 @@ using System.Text.Json;
|
||||||
|
|
||||||
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.App
|
||||||
{
|
{
|
||||||
public class ApplicationLibrary
|
public class ApplicationLibrary
|
||||||
{
|
{
|
||||||
public static event EventHandler<ApplicationAddedEventArgs> ApplicationAdded;
|
public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded;
|
||||||
public static event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
||||||
|
|
||||||
private static readonly byte[] _nspIcon = GetResourceBytes("Ryujinx.Ui.assets.NSPIcon.png");
|
private readonly byte[] _nspIcon;
|
||||||
private static readonly byte[] _xciIcon = GetResourceBytes("Ryujinx.Ui.assets.XCIIcon.png");
|
private readonly byte[] _xciIcon;
|
||||||
private static readonly byte[] _ncaIcon = GetResourceBytes("Ryujinx.Ui.assets.NCAIcon.png");
|
private readonly byte[] _ncaIcon;
|
||||||
private static readonly byte[] _nroIcon = GetResourceBytes("Ryujinx.Ui.assets.NROIcon.png");
|
private readonly byte[] _nroIcon;
|
||||||
private static readonly byte[] _nsoIcon = GetResourceBytes("Ryujinx.Ui.assets.NSOIcon.png");
|
private readonly byte[] _nsoIcon;
|
||||||
|
|
||||||
private static VirtualFileSystem _virtualFileSystem;
|
private VirtualFileSystem _virtualFileSystem;
|
||||||
private static Language _desiredTitleLanguage;
|
private Language _desiredTitleLanguage;
|
||||||
private static bool _loadingError;
|
private bool _loadingError;
|
||||||
|
|
||||||
public static IEnumerable<string> GetFilesInDirectory(string directory)
|
public ApplicationLibrary(VirtualFileSystem virtualFileSystem)
|
||||||
|
{
|
||||||
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
|
||||||
|
_nspIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_NSP.png");
|
||||||
|
_xciIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_XCI.png");
|
||||||
|
_ncaIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_NCA.png");
|
||||||
|
_nroIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_NRO.png");
|
||||||
|
_nsoIcon = GetResourceBytes("Ryujinx.Ui.Resources.Icon_NSO.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] GetResourceBytes(string resourceName)
|
||||||
|
{
|
||||||
|
Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName);
|
||||||
|
byte[] resourceByteArray = new byte[resourceStream.Length];
|
||||||
|
|
||||||
|
resourceStream.Read(resourceByteArray);
|
||||||
|
|
||||||
|
return resourceByteArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> GetFilesInDirectory(string directory)
|
||||||
{
|
{
|
||||||
Stack<string> stack = new Stack<string>();
|
Stack<string> stack = new Stack<string>();
|
||||||
|
|
||||||
|
@ -46,7 +68,7 @@ namespace Ryujinx.Ui
|
||||||
while (stack.Count > 0)
|
while (stack.Count > 0)
|
||||||
{
|
{
|
||||||
string dir = stack.Pop();
|
string dir = stack.Pop();
|
||||||
string[] content = { };
|
string[] content = Array.Empty<string>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -84,19 +106,18 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReadControlData(IFileSystem controlFs, Span<byte> outProperty)
|
public void ReadControlData(IFileSystem controlFs, Span<byte> outProperty)
|
||||||
{
|
{
|
||||||
controlFs.OpenFile(out IFile controlFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
controlFs.OpenFile(out IFile controlFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
controlFile.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure();
|
controlFile.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LoadApplications(List<string> appDirs, VirtualFileSystem virtualFileSystem, Language desiredTitleLanguage)
|
public void LoadApplications(List<string> appDirs, Language desiredTitleLanguage)
|
||||||
{
|
{
|
||||||
int numApplicationsFound = 0;
|
int numApplicationsFound = 0;
|
||||||
int numApplicationsLoaded = 0;
|
int numApplicationsLoaded = 0;
|
||||||
|
|
||||||
_loadingError = false;
|
_loadingError = false;
|
||||||
_virtualFileSystem = virtualFileSystem;
|
|
||||||
_desiredTitleLanguage = desiredTitleLanguage;
|
_desiredTitleLanguage = desiredTitleLanguage;
|
||||||
|
|
||||||
// Builds the applications list with paths to found applications
|
// Builds the applications list with paths to found applications
|
||||||
|
@ -442,36 +463,26 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void OnApplicationAdded(ApplicationAddedEventArgs e)
|
protected void OnApplicationAdded(ApplicationAddedEventArgs e)
|
||||||
{
|
{
|
||||||
ApplicationAdded?.Invoke(null, e);
|
ApplicationAdded?.Invoke(null, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void OnApplicationCountUpdated(ApplicationCountUpdatedEventArgs e)
|
protected void OnApplicationCountUpdated(ApplicationCountUpdatedEventArgs e)
|
||||||
{
|
{
|
||||||
ApplicationCountUpdated?.Invoke(null, e);
|
ApplicationCountUpdated?.Invoke(null, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] GetResourceBytes(string resourceName)
|
private void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId)
|
||||||
{
|
|
||||||
Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName);
|
|
||||||
byte[] resourceByteArray = new byte[resourceStream.Length];
|
|
||||||
|
|
||||||
resourceStream.Read(resourceByteArray);
|
|
||||||
|
|
||||||
return resourceByteArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId)
|
|
||||||
{
|
{
|
||||||
(_, _, Nca controlNca) = ApplicationLoader.GetGameData(_virtualFileSystem, pfs, 0);
|
(_, _, Nca controlNca) = ApplicationLoader.GetGameData(_virtualFileSystem, pfs, 0);
|
||||||
|
|
||||||
// Return the ControlFS
|
// Return the ControlFS
|
||||||
controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
|
controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
|
||||||
titleId = controlNca?.Header.TitleId.ToString("x16");
|
titleId = controlNca?.Header.TitleId.ToString("x16");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
|
internal ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
|
||||||
{
|
{
|
||||||
string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui");
|
string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui");
|
||||||
string metadataFile = Path.Combine(metadataFolder, "metadata.json");
|
string metadataFile = Path.Combine(metadataFolder, "metadata.json");
|
||||||
|
@ -482,12 +493,7 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(metadataFolder);
|
Directory.CreateDirectory(metadataFolder);
|
||||||
|
|
||||||
appMetadata = new ApplicationMetadata
|
appMetadata = new ApplicationMetadata();
|
||||||
{
|
|
||||||
Favorite = false,
|
|
||||||
TimePlayed = 0,
|
|
||||||
LastPlayed = "Never"
|
|
||||||
};
|
|
||||||
|
|
||||||
using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough))
|
using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough))
|
||||||
{
|
{
|
||||||
|
@ -503,12 +509,7 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
|
Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
|
||||||
|
|
||||||
appMetadata = new ApplicationMetadata
|
appMetadata = new ApplicationMetadata();
|
||||||
{
|
|
||||||
Favorite = false,
|
|
||||||
TimePlayed = 0,
|
|
||||||
LastPlayed = "Never"
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modifyFunction != null)
|
if (modifyFunction != null)
|
||||||
|
@ -524,7 +525,7 @@ namespace Ryujinx.Ui
|
||||||
return appMetadata;
|
return appMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ConvertSecondsToReadableString(double seconds)
|
private string ConvertSecondsToReadableString(double seconds)
|
||||||
{
|
{
|
||||||
const int secondsPerMinute = 60;
|
const int secondsPerMinute = 60;
|
||||||
const int secondsPerHour = secondsPerMinute * 60;
|
const int secondsPerHour = secondsPerMinute * 60;
|
||||||
|
@ -552,9 +553,9 @@ namespace Ryujinx.Ui
|
||||||
return readableString;
|
return readableString;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GetNameIdDeveloper(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher)
|
private void GetNameIdDeveloper(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher)
|
||||||
{
|
{
|
||||||
Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
|
_ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
|
||||||
|
|
||||||
if (controlData.Titles.Length > (int)desiredTitleLanguage)
|
if (controlData.Titles.Length > (int)desiredTitleLanguage)
|
||||||
{
|
{
|
||||||
|
@ -611,7 +612,7 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsUpdateApplied(string titleId, out string version)
|
private bool IsUpdateApplied(string titleId, out string version)
|
||||||
{
|
{
|
||||||
string updatePath = "(unknown)";
|
string updatePath = "(unknown)";
|
||||||
|
|
9
Ryujinx/Ui/App/ApplicationMetadata.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.Ui.App
|
||||||
|
{
|
||||||
|
public class ApplicationMetadata
|
||||||
|
{
|
||||||
|
public bool Favorite { get; set; }
|
||||||
|
public double TimePlayed { get; set; }
|
||||||
|
public string LastPlayed { get; set; } = "Never";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,11 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Applet
|
||||||
{
|
{
|
||||||
internal class ErrorAppletDialog : MessageDialog
|
internal class ErrorAppletDialog : MessageDialog
|
||||||
{
|
{
|
||||||
internal static bool _isExitDialogOpen = false;
|
|
||||||
|
|
||||||
public ErrorAppletDialog(Window parentWindow, DialogFlags dialogFlags, MessageType messageType, string[] buttons) : base(parentWindow, dialogFlags, messageType, ButtonsType.None, null)
|
public ErrorAppletDialog(Window parentWindow, DialogFlags dialogFlags, MessageType messageType, string[] buttons) : base(parentWindow, dialogFlags, messageType, ButtonsType.None, null)
|
||||||
{
|
{
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
|
||||||
|
|
||||||
int responseId = 0;
|
int responseId = 0;
|
||||||
|
|
||||||
if (buttons != null)
|
if (buttons != null)
|
|
@ -1,12 +1,12 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.HOS.Applets;
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
||||||
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Applet
|
||||||
{
|
{
|
||||||
internal class GtkHostUiHandler : IHostUiHandler
|
internal class GtkHostUiHandler : IHostUiHandler
|
||||||
{
|
{
|
||||||
|
@ -19,16 +19,13 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
|
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
|
||||||
{
|
{
|
||||||
string playerCount = args.PlayerCountMin == args.PlayerCountMax
|
string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}";
|
||||||
? $"exactly {args.PlayerCountMin}"
|
|
||||||
: $"{args.PlayerCountMin}-{args.PlayerCountMax}";
|
|
||||||
|
|
||||||
string message =
|
string message = $"Application requests <b>{playerCount}</b> player(s) with:\n\n"
|
||||||
$"Application requests <b>{playerCount}</b> player(s) with:\n\n"
|
+ $"<tt><b>TYPES:</b> {args.SupportedStyles}</tt>\n\n"
|
||||||
+ $"<tt><b>TYPES:</b> {args.SupportedStyles}</tt>\n\n"
|
+ $"<tt><b>PLAYERS:</b> {string.Join(", ", args.SupportedPlayers)}</tt>\n\n"
|
||||||
+ $"<tt><b>PLAYERS:</b> {string.Join(", ", args.SupportedPlayers)}</tt>\n\n"
|
+ (args.IsDocked ? "Docked mode set. <tt>Handheld</tt> is also invalid.\n\n" : "")
|
||||||
+ (args.IsDocked ? "Docked mode set. <tt>Handheld</tt> is also invalid.\n\n" : "")
|
+ "<i>Please reconfigure Input now and then press OK.</i>";
|
||||||
+ "<i>Please reconfigure Input now and then press OK.</i>";
|
|
||||||
|
|
||||||
return DisplayMessageDialog("Controller Applet", message);
|
return DisplayMessageDialog("Controller Applet", message);
|
||||||
}
|
}
|
||||||
|
@ -36,17 +33,19 @@ namespace Ryujinx.Ui
|
||||||
public bool DisplayMessageDialog(string title, string message)
|
public bool DisplayMessageDialog(string title, string message)
|
||||||
{
|
{
|
||||||
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
|
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
|
||||||
|
|
||||||
bool okPressed = false;
|
bool okPressed = false;
|
||||||
|
|
||||||
Application.Invoke(delegate
|
Application.Invoke(delegate
|
||||||
{
|
{
|
||||||
MessageDialog msgDialog = null;
|
MessageDialog msgDialog = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
msgDialog = new MessageDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null)
|
msgDialog = new MessageDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null)
|
||||||
{
|
{
|
||||||
Title = title,
|
Title = title,
|
||||||
Text = message,
|
Text = message,
|
||||||
UseMarkup = true
|
UseMarkup = true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,16 +53,21 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
msgDialog.Response += (object o, ResponseArgs args) =>
|
msgDialog.Response += (object o, ResponseArgs args) =>
|
||||||
{
|
{
|
||||||
if (args.ResponseId == ResponseType.Ok) okPressed = true;
|
if (args.ResponseId == ResponseType.Ok)
|
||||||
|
{
|
||||||
|
okPressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
dialogCloseEvent.Set();
|
dialogCloseEvent.Set();
|
||||||
msgDialog?.Dispose();
|
msgDialog?.Dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
msgDialog.Show();
|
msgDialog.Show();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"Error displaying Message Dialog: {e}");
|
GtkDialog.CreateErrorDialog($"Error displaying Message Dialog: {ex}");
|
||||||
|
|
||||||
dialogCloseEvent.Set();
|
dialogCloseEvent.Set();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -76,24 +80,25 @@ namespace Ryujinx.Ui
|
||||||
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
|
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
|
||||||
{
|
{
|
||||||
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
|
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
|
||||||
bool okPressed = false;
|
|
||||||
bool error = false;
|
bool okPressed = false;
|
||||||
|
bool error = false;
|
||||||
string inputText = args.InitialText ?? "";
|
string inputText = args.InitialText ?? "";
|
||||||
|
|
||||||
Application.Invoke(delegate
|
Application.Invoke(delegate
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var swkbdDialog = new InputDialog(_parent)
|
var swkbdDialog = new SwkbdAppletDialog(_parent)
|
||||||
{
|
{
|
||||||
Title = "Software Keyboard",
|
Title = "Software Keyboard",
|
||||||
Text = args.HeaderText,
|
Text = args.HeaderText,
|
||||||
SecondaryText = args.SubtitleText
|
SecondaryText = args.SubtitleText
|
||||||
};
|
};
|
||||||
|
|
||||||
swkbdDialog.InputEntry.Text = inputText;
|
swkbdDialog.InputEntry.Text = inputText;
|
||||||
swkbdDialog.InputEntry.PlaceholderText = args.GuideText;
|
swkbdDialog.InputEntry.PlaceholderText = args.GuideText;
|
||||||
swkbdDialog.OkButton.Label = args.SubmitText;
|
swkbdDialog.OkButton.Label = args.SubmitText;
|
||||||
|
|
||||||
swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
||||||
|
|
||||||
|
@ -105,10 +110,11 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
swkbdDialog.Dispose();
|
swkbdDialog.Dispose();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
error = true;
|
error = true;
|
||||||
Logger.Error?.Print(LogClass.Application, $"Error displaying Software Keyboard: {e}");
|
|
||||||
|
GtkDialog.CreateErrorDialog($"Error displaying Software Keyboard: {ex}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -126,12 +132,13 @@ namespace Ryujinx.Ui
|
||||||
public void ExecuteProgram(HLE.Switch device, ProgramSpecifyKind kind, ulong value)
|
public void ExecuteProgram(HLE.Switch device, ProgramSpecifyKind kind, ulong value)
|
||||||
{
|
{
|
||||||
device.UserChannelPersistence.ExecuteProgram(kind, value);
|
device.UserChannelPersistence.ExecuteProgram(kind, value);
|
||||||
MainWindow.GlWidget?.Exit();
|
((MainWindow)_parent).GlRendererWidget?.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
|
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
|
||||||
{
|
{
|
||||||
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
|
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
|
||||||
|
|
||||||
bool showDetails = false;
|
bool showDetails = false;
|
||||||
|
|
||||||
Application.Invoke(delegate
|
Application.Invoke(delegate
|
||||||
|
@ -167,9 +174,9 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
msgDialog.Show();
|
msgDialog.Show();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"Error displaying ErrorApplet Dialog: {e}");
|
GtkDialog.CreateErrorDialog($"Error displaying ErrorApplet Dialog: {ex}");
|
||||||
|
|
||||||
dialogCloseEvent.Set();
|
dialogCloseEvent.Set();
|
||||||
}
|
}
|
||||||
|
@ -180,4 +187,4 @@ namespace Ryujinx.Ui
|
||||||
return showDetails;
|
return showDetails;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
89
Ryujinx/Ui/Applet/SwkbdAppletDialog.cs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
using Gtk;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Applet
|
||||||
|
{
|
||||||
|
public class SwkbdAppletDialog : MessageDialog
|
||||||
|
{
|
||||||
|
private int _inputMin;
|
||||||
|
private int _inputMax;
|
||||||
|
|
||||||
|
private Predicate<int> _checkLength;
|
||||||
|
|
||||||
|
private readonly Label _validationInfo;
|
||||||
|
|
||||||
|
public Entry InputEntry { get; }
|
||||||
|
public Button OkButton { get; }
|
||||||
|
public Button CancelButton { get; }
|
||||||
|
|
||||||
|
public SwkbdAppletDialog(Window parent) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, null)
|
||||||
|
{
|
||||||
|
SetDefaultSize(300, 0);
|
||||||
|
|
||||||
|
_validationInfo = new Label()
|
||||||
|
{
|
||||||
|
Visible = false
|
||||||
|
};
|
||||||
|
|
||||||
|
InputEntry = new Entry()
|
||||||
|
{
|
||||||
|
Visible = true
|
||||||
|
};
|
||||||
|
|
||||||
|
InputEntry.Activated += OnInputActivated;
|
||||||
|
InputEntry.Changed += OnInputChanged;
|
||||||
|
|
||||||
|
OkButton = (Button)AddButton("OK", ResponseType.Ok);
|
||||||
|
CancelButton = (Button)AddButton("Cancel", ResponseType.Cancel);
|
||||||
|
|
||||||
|
((Box)MessageArea).PackEnd(_validationInfo, true, true, 0);
|
||||||
|
((Box)MessageArea).PackEnd(InputEntry, true, true, 4);
|
||||||
|
|
||||||
|
SetInputLengthValidation(0, int.MaxValue); // Disable by default.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInputLengthValidation(int min, int max)
|
||||||
|
{
|
||||||
|
_inputMin = Math.Min(min, max);
|
||||||
|
_inputMax = Math.Max(min, max);
|
||||||
|
|
||||||
|
_validationInfo.Visible = false;
|
||||||
|
|
||||||
|
if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable.
|
||||||
|
{
|
||||||
|
_validationInfo.Visible = false;
|
||||||
|
|
||||||
|
_checkLength = (length) => true;
|
||||||
|
}
|
||||||
|
else if (_inputMin > 0 && _inputMax == int.MaxValue)
|
||||||
|
{
|
||||||
|
_validationInfo.Visible = true;
|
||||||
|
_validationInfo.Markup = $"<i>Must be at least {_inputMin} characters long</i>";
|
||||||
|
|
||||||
|
_checkLength = (length) => _inputMin <= length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_validationInfo.Visible = true;
|
||||||
|
_validationInfo.Markup = $"<i>Must be {_inputMin}-{_inputMax} characters long</i>";
|
||||||
|
|
||||||
|
_checkLength = (length) => _inputMin <= length && length <= _inputMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnInputChanged(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputActivated(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (OkButton.IsSensitive)
|
||||||
|
{
|
||||||
|
Respond(ResponseType.Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
OkButton.Sensitive = _checkLength(InputEntry.Text.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Ryujinx.Ui
|
|
||||||
{
|
|
||||||
internal class ApplicationMetadata
|
|
||||||
{
|
|
||||||
public bool Favorite { get; set; }
|
|
||||||
public double TimePlayed { get; set; }
|
|
||||||
public string LastPlayed { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
using Gtk;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Diagnostic
|
|
||||||
{
|
|
||||||
internal class GuideDialog : MessageDialog
|
|
||||||
{
|
|
||||||
internal static bool _isExitDialogOpen = false;
|
|
||||||
|
|
||||||
public GuideDialog(string title, string mainText, string secondaryText) : base(null, DialogFlags.Modal, MessageType.Other, ButtonsType.None, null)
|
|
||||||
{
|
|
||||||
Title = title;
|
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
|
||||||
Text = mainText;
|
|
||||||
SecondaryText = secondaryText;
|
|
||||||
WindowPosition = WindowPosition.Center;
|
|
||||||
Response += GtkDialog_Response;
|
|
||||||
|
|
||||||
Button guideButton = new Button();
|
|
||||||
guideButton.Label = "Open the Setup Guide";
|
|
||||||
|
|
||||||
ContentArea.Add(guideButton);
|
|
||||||
|
|
||||||
SetSizeRequest(100, 10);
|
|
||||||
ShowAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GtkDialog_Response(object sender, ResponseArgs args)
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
using Gtk;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Diagnostic
|
|
||||||
{
|
|
||||||
internal class UserErrorDialog : MessageDialog
|
|
||||||
{
|
|
||||||
private static string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide";
|
|
||||||
private const int OkResponseId = 0;
|
|
||||||
private const int SetupGuideResponseId = 1;
|
|
||||||
|
|
||||||
private UserError _userError;
|
|
||||||
|
|
||||||
private UserErrorDialog(UserError error) : base(null, DialogFlags.Modal, MessageType.Error, ButtonsType.None, null)
|
|
||||||
{
|
|
||||||
_userError = error;
|
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
|
||||||
WindowPosition = WindowPosition.Center;
|
|
||||||
Response += UserErrorDialog_Response;
|
|
||||||
|
|
||||||
SetSizeRequest(120, 50);
|
|
||||||
|
|
||||||
AddButton("OK", OkResponseId);
|
|
||||||
|
|
||||||
bool isInSetupGuide = IsCoveredBySetupGuide(error);
|
|
||||||
|
|
||||||
if (isInSetupGuide)
|
|
||||||
{
|
|
||||||
AddButton("Open the Setup Guide", SetupGuideResponseId);
|
|
||||||
}
|
|
||||||
|
|
||||||
string errorCode = GetErrorCode(error);
|
|
||||||
|
|
||||||
SecondaryUseMarkup = true;
|
|
||||||
|
|
||||||
Title = $"Ryujinx error ({errorCode})";
|
|
||||||
Text = $"{errorCode}: {GetErrorTitle(error)}";
|
|
||||||
SecondaryText = GetErrorDescription(error);
|
|
||||||
|
|
||||||
if (isInSetupGuide)
|
|
||||||
{
|
|
||||||
SecondaryText += "\n<b>For more information on how to fix this error, follow our Setup Guide.</b>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetErrorCode(UserError error)
|
|
||||||
{
|
|
||||||
return $"RYU-{(uint)error:X4}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetErrorTitle(UserError error)
|
|
||||||
{
|
|
||||||
switch (error)
|
|
||||||
{
|
|
||||||
case UserError.NoKeys:
|
|
||||||
return "Keys not found";
|
|
||||||
case UserError.NoFirmware:
|
|
||||||
return "Firmware not found";
|
|
||||||
case UserError.FirmwareParsingFailed:
|
|
||||||
return "Firmware parsing error";
|
|
||||||
case UserError.ApplicationNotFound:
|
|
||||||
return "Application not found";
|
|
||||||
case UserError.Unknown:
|
|
||||||
return "Unknown error";
|
|
||||||
default:
|
|
||||||
return "Undefined error";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetErrorDescription(UserError error)
|
|
||||||
{
|
|
||||||
switch (error)
|
|
||||||
{
|
|
||||||
case UserError.NoKeys:
|
|
||||||
return "Ryujinx was unable to find your 'prod.keys' file";
|
|
||||||
case UserError.NoFirmware:
|
|
||||||
return "Ryujinx was unable to find any firmwares installed";
|
|
||||||
case UserError.FirmwareParsingFailed:
|
|
||||||
return "Ryujinx was unable to parse the provided firmware. This is usually caused by outdated keys.";
|
|
||||||
case UserError.ApplicationNotFound:
|
|
||||||
return "Ryujinx couldn't find a valid application at the given path.";
|
|
||||||
case UserError.Unknown:
|
|
||||||
return "An unknown error occured!";
|
|
||||||
default:
|
|
||||||
return "An undefined error occured! This shouldn't happen, please contact a dev!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsCoveredBySetupGuide(UserError error)
|
|
||||||
{
|
|
||||||
switch (error)
|
|
||||||
{
|
|
||||||
case UserError.NoKeys:
|
|
||||||
case UserError.NoFirmware:
|
|
||||||
case UserError.FirmwareParsingFailed:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetSetupGuideUrl(UserError error)
|
|
||||||
{
|
|
||||||
if (!IsCoveredBySetupGuide(error))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (error)
|
|
||||||
{
|
|
||||||
case UserError.NoKeys:
|
|
||||||
return SetupGuideUrl + "#initial-setup---placement-of-prodkeys";
|
|
||||||
case UserError.NoFirmware:
|
|
||||||
return SetupGuideUrl + "#initial-setup-continued---installation-of-firmware";
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetupGuideUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserErrorDialog_Response(object sender, ResponseArgs args)
|
|
||||||
{
|
|
||||||
int responseId = (int)args.ResponseId;
|
|
||||||
|
|
||||||
if (responseId == SetupGuideResponseId)
|
|
||||||
{
|
|
||||||
UrlHelper.OpenUrl(GetSetupGuideUrl(_userError));
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CreateUserErrorDialog(UserError error)
|
|
||||||
{
|
|
||||||
new UserErrorDialog(error).Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,8 @@ using Ryujinx.Configuration;
|
||||||
using Ryujinx.Graphics.OpenGL;
|
using Ryujinx.Graphics.OpenGL;
|
||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using Ryujinx.Motion;
|
using Ryujinx.Modules.Motion;
|
||||||
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -73,9 +74,9 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
_device = device;
|
_device = device;
|
||||||
|
|
||||||
this.Initialized += GLRenderer_Initialized;
|
Initialized += GLRenderer_Initialized;
|
||||||
this.Destroyed += GLRenderer_Destroyed;
|
Destroyed += GLRenderer_Destroyed;
|
||||||
this.ShuttingDown += GLRenderer_ShuttingDown;
|
ShuttingDown += GLRenderer_ShuttingDown;
|
||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
||||||
|
@ -89,7 +90,7 @@ namespace Ryujinx.Ui
|
||||||
| EventMask.KeyPressMask
|
| EventMask.KeyPressMask
|
||||||
| EventMask.KeyReleaseMask));
|
| EventMask.KeyReleaseMask));
|
||||||
|
|
||||||
this.Shown += Renderer_Shown;
|
Shown += Renderer_Shown;
|
||||||
|
|
||||||
_dsuClient = new Client();
|
_dsuClient = new Client();
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,20 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Helper
|
||||||
{
|
{
|
||||||
static class UrlHelper
|
static class OpenHelper
|
||||||
{
|
{
|
||||||
|
public static void OpenFolder(string path)
|
||||||
|
{
|
||||||
|
Process.Start(new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = path,
|
||||||
|
UseShellExecute = true,
|
||||||
|
Verb = "open"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void OpenUrl(string url)
|
public static void OpenUrl(string url)
|
||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
@ -26,4 +36,4 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Diagnostic
|
namespace Ryujinx.Ui.Helper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensure installation validity
|
/// Ensure installation validity
|
116
Ryujinx/Ui/Helper/SortHelper.cs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
using Gtk;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Helper
|
||||||
|
{
|
||||||
|
static class SortHelper
|
||||||
|
{
|
||||||
|
public static int TimePlayedSort(ITreeModel model, TreeIter a, TreeIter b)
|
||||||
|
{
|
||||||
|
string aValue = model.GetValue(a, 5).ToString();
|
||||||
|
string bValue = model.GetValue(b, 5).ToString();
|
||||||
|
|
||||||
|
if (aValue.Length > 4 && aValue[^4..] == "mins")
|
||||||
|
{
|
||||||
|
aValue = (float.Parse(aValue[0..^5]) * 60).ToString();
|
||||||
|
}
|
||||||
|
else if (aValue.Length > 3 && aValue[^3..] == "hrs")
|
||||||
|
{
|
||||||
|
aValue = (float.Parse(aValue[0..^4]) * 3600).ToString();
|
||||||
|
}
|
||||||
|
else if (aValue.Length > 4 && aValue[^4..] == "days")
|
||||||
|
{
|
||||||
|
aValue = (float.Parse(aValue[0..^5]) * 86400).ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aValue = aValue[0..^1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bValue.Length > 4 && bValue[^4..] == "mins")
|
||||||
|
{
|
||||||
|
bValue = (float.Parse(bValue[0..^5]) * 60).ToString();
|
||||||
|
}
|
||||||
|
else if (bValue.Length > 3 && bValue[^3..] == "hrs")
|
||||||
|
{
|
||||||
|
bValue = (float.Parse(bValue[0..^4]) * 3600).ToString();
|
||||||
|
}
|
||||||
|
else if (bValue.Length > 4 && bValue[^4..] == "days")
|
||||||
|
{
|
||||||
|
bValue = (float.Parse(bValue[0..^5]) * 86400).ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bValue = bValue[0..^1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (float.Parse(aValue) > float.Parse(bValue))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (float.Parse(bValue) > float.Parse(aValue))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int LastPlayedSort(ITreeModel model, TreeIter a, TreeIter b)
|
||||||
|
{
|
||||||
|
string aValue = model.GetValue(a, 6).ToString();
|
||||||
|
string bValue = model.GetValue(b, 6).ToString();
|
||||||
|
|
||||||
|
if (aValue == "Never")
|
||||||
|
{
|
||||||
|
aValue = DateTime.UnixEpoch.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bValue == "Never")
|
||||||
|
{
|
||||||
|
bValue = DateTime.UnixEpoch.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateTime.Compare(DateTime.Parse(bValue), DateTime.Parse(aValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int FileSizeSort(ITreeModel model, TreeIter a, TreeIter b)
|
||||||
|
{
|
||||||
|
string aValue = model.GetValue(a, 8).ToString();
|
||||||
|
string bValue = model.GetValue(b, 8).ToString();
|
||||||
|
|
||||||
|
if (aValue[^2..] == "GB")
|
||||||
|
{
|
||||||
|
aValue = (float.Parse(aValue[0..^2]) * 1024).ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aValue = aValue[0..^2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bValue[^2..] == "GB")
|
||||||
|
{
|
||||||
|
bValue = (float.Parse(bValue[0..^2]) * 1024).ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bValue = bValue[0..^2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (float.Parse(aValue) > float.Parse(bValue))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (float.Parse(bValue) > float.Parse(aValue))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
Ryujinx/Ui/Helper/ThemeHelper.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using Gtk;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Configuration;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Helper
|
||||||
|
{
|
||||||
|
static class ThemeHelper
|
||||||
|
{
|
||||||
|
public static void ApplyTheme()
|
||||||
|
{
|
||||||
|
if (!ConfigurationState.Instance.Ui.EnableCustomTheme)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(ConfigurationState.Instance.Ui.CustomThemePath) && (Path.GetExtension(ConfigurationState.Instance.Ui.CustomThemePath) == ".css"))
|
||||||
|
{
|
||||||
|
CssProvider cssProvider = new CssProvider();
|
||||||
|
|
||||||
|
cssProvider.LoadFromPath(ConfigurationState.Instance.Ui.CustomThemePath);
|
||||||
|
|
||||||
|
StyleContext.AddProviderForScreen(Gdk.Screen.Default, cssProvider, 800);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\".");
|
||||||
|
|
||||||
|
ConfigurationState.Instance.Ui.CustomThemePath.Value = "";
|
||||||
|
ConfigurationState.Instance.Ui.EnableCustomTheme.Value = false;
|
||||||
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
using Gtk;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
|
||||||
{
|
|
||||||
public class InputDialog : MessageDialog
|
|
||||||
{
|
|
||||||
private int _inputMin, _inputMax;
|
|
||||||
private Predicate<int> _checkLength;
|
|
||||||
private Label _validationInfo;
|
|
||||||
|
|
||||||
public Entry InputEntry { get; }
|
|
||||||
public Button OkButton { get; }
|
|
||||||
public Button CancelButton { get; }
|
|
||||||
|
|
||||||
public InputDialog(Window parent)
|
|
||||||
: base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, null)
|
|
||||||
{
|
|
||||||
SetDefaultSize(300, 0);
|
|
||||||
|
|
||||||
_validationInfo = new Label() { Visible = false };
|
|
||||||
|
|
||||||
InputEntry = new Entry() { Visible = true };
|
|
||||||
InputEntry.Activated += (object sender, EventArgs e) => { if (OkButton.IsSensitive) Respond(ResponseType.Ok); };
|
|
||||||
InputEntry.Changed += OnInputChanged;
|
|
||||||
|
|
||||||
OkButton = (Button)AddButton("OK", ResponseType.Ok);
|
|
||||||
CancelButton = (Button)AddButton("Cancel", ResponseType.Cancel);
|
|
||||||
|
|
||||||
((Box)MessageArea).PackEnd(_validationInfo, true, true, 0);
|
|
||||||
((Box)MessageArea).PackEnd(InputEntry, true, true, 4);
|
|
||||||
|
|
||||||
SetInputLengthValidation(0, int.MaxValue); // disable by default
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetInputLengthValidation(int min, int max)
|
|
||||||
{
|
|
||||||
_inputMin = Math.Min(min, max);
|
|
||||||
_inputMax = Math.Max(min, max);
|
|
||||||
|
|
||||||
_validationInfo.Visible = false;
|
|
||||||
|
|
||||||
if (_inputMin <= 0 && _inputMax == int.MaxValue) // disable
|
|
||||||
{
|
|
||||||
_validationInfo.Visible = false;
|
|
||||||
_checkLength = (length) => true;
|
|
||||||
}
|
|
||||||
else if (_inputMin > 0 && _inputMax == int.MaxValue)
|
|
||||||
{
|
|
||||||
_validationInfo.Visible = true;
|
|
||||||
_validationInfo.Markup = $"<i>Must be at least {_inputMin} characters long</i>";
|
|
||||||
_checkLength = (length) => _inputMin <= length;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_validationInfo.Visible = true;
|
|
||||||
_validationInfo.Markup = $"<i>Must be {_inputMin}-{_inputMax} characters long</i>";
|
|
||||||
_checkLength = (length) => _inputMin <= length && length <= _inputMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnInputChanged(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInputChanged(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
OkButton.Sensitive = _checkLength(InputEntry.Text.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,189 +0,0 @@
|
||||||
using Gtk;
|
|
||||||
using LibHac;
|
|
||||||
using Ryujinx.Common.Configuration;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
|
||||||
{
|
|
||||||
internal class Migration
|
|
||||||
{
|
|
||||||
private VirtualFileSystem _virtualFileSystem;
|
|
||||||
|
|
||||||
public Migration(VirtualFileSystem virtualFileSystem)
|
|
||||||
{
|
|
||||||
_virtualFileSystem = virtualFileSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool PromptIfMigrationNeededForStartup(Window parentWindow, out bool isMigrationNeeded)
|
|
||||||
{
|
|
||||||
if (!IsMigrationNeeded())
|
|
||||||
{
|
|
||||||
isMigrationNeeded = false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
isMigrationNeeded = true;
|
|
||||||
|
|
||||||
int dialogResponse;
|
|
||||||
|
|
||||||
using (MessageDialog dialog = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Question,
|
|
||||||
ButtonsType.YesNo, "What's this?"))
|
|
||||||
{
|
|
||||||
dialog.Title = "Data Migration Needed";
|
|
||||||
dialog.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
|
||||||
dialog.Text =
|
|
||||||
"The folder structure of Ryujinx's RyuFs folder has been updated and renamed to \"Ryujinx\". " +
|
|
||||||
"Your RyuFs folder must be copied and migrated to the new \"Ryujinx\" structure. Would you like to do the migration now?\n\n" +
|
|
||||||
"Select \"Yes\" to automatically perform the migration. Your old RyuFs folder will remain as it is.\n\n" +
|
|
||||||
"Selecting \"No\" will exit Ryujinx without changing anything.";
|
|
||||||
|
|
||||||
dialogResponse = dialog.Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
return dialogResponse == (int)ResponseType.Yes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool DoMigrationForStartup(MainWindow parentWindow, VirtualFileSystem virtualFileSystem)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Migration migration = new Migration(virtualFileSystem);
|
|
||||||
int saveCount = migration.Migrate();
|
|
||||||
|
|
||||||
using MessageDialog dialogSuccess = new MessageDialog(parentWindow, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, null)
|
|
||||||
{
|
|
||||||
Title = "Migration Success",
|
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
|
|
||||||
Text = $"Data migration was successful. {saveCount} saves were migrated.",
|
|
||||||
};
|
|
||||||
|
|
||||||
dialogSuccess.Run();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (HorizonResultException ex)
|
|
||||||
{
|
|
||||||
GtkDialog.CreateErrorDialog(ex.Message);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of saves migrated
|
|
||||||
public int Migrate()
|
|
||||||
{
|
|
||||||
// Make sure FsClient is initialized
|
|
||||||
_virtualFileSystem.Reload();
|
|
||||||
|
|
||||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
|
||||||
|
|
||||||
string oldBasePath = Path.Combine(appDataPath, "RyuFs");
|
|
||||||
string newBasePath = Path.Combine(appDataPath, "Ryujinx");
|
|
||||||
|
|
||||||
string oldSaveDir = Path.Combine(oldBasePath, "nand/user/save");
|
|
||||||
|
|
||||||
CopyRyuFs(oldBasePath, newBasePath);
|
|
||||||
|
|
||||||
SaveImporter importer = new SaveImporter(oldSaveDir, _virtualFileSystem.FsClient);
|
|
||||||
|
|
||||||
return importer.Import();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CopyRyuFs(string oldPath, string newPath)
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(newPath);
|
|
||||||
|
|
||||||
CopyExcept(oldPath, newPath, "nand", "bis", "sdmc", "sdcard");
|
|
||||||
|
|
||||||
string oldNandPath = Path.Combine(oldPath, "nand");
|
|
||||||
string newNandPath = Path.Combine(newPath, "bis");
|
|
||||||
|
|
||||||
CopyExcept(oldNandPath, newNandPath, "system", "user");
|
|
||||||
|
|
||||||
string oldSdPath = Path.Combine(oldPath, "sdmc");
|
|
||||||
string newSdPath = Path.Combine(newPath, "sdcard");
|
|
||||||
|
|
||||||
CopyDirectory(oldSdPath, newSdPath);
|
|
||||||
|
|
||||||
string oldSystemPath = Path.Combine(oldNandPath, "system");
|
|
||||||
string newSystemPath = Path.Combine(newNandPath, "system");
|
|
||||||
|
|
||||||
CopyExcept(oldSystemPath, newSystemPath, "save");
|
|
||||||
|
|
||||||
string oldUserPath = Path.Combine(oldNandPath, "user");
|
|
||||||
string newUserPath = Path.Combine(newNandPath, "user");
|
|
||||||
|
|
||||||
CopyExcept(oldUserPath, newUserPath, "save");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CopyExcept(string srcPath, string dstPath, params string[] exclude)
|
|
||||||
{
|
|
||||||
exclude = exclude.Select(x => x.ToLowerInvariant()).ToArray();
|
|
||||||
|
|
||||||
DirectoryInfo srcDir = new DirectoryInfo(srcPath);
|
|
||||||
|
|
||||||
if (!srcDir.Exists)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(dstPath);
|
|
||||||
|
|
||||||
foreach (DirectoryInfo subDir in srcDir.EnumerateDirectories())
|
|
||||||
{
|
|
||||||
if (exclude.Contains(subDir.Name.ToLowerInvariant()))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CopyDirectory(subDir.FullName, Path.Combine(dstPath, subDir.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (FileInfo file in srcDir.EnumerateFiles())
|
|
||||||
{
|
|
||||||
file.CopyTo(Path.Combine(dstPath, file.Name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CopyDirectory(string srcPath, string dstPath)
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(dstPath);
|
|
||||||
|
|
||||||
DirectoryInfo srcDir = new DirectoryInfo(srcPath);
|
|
||||||
|
|
||||||
if (!srcDir.Exists)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(dstPath);
|
|
||||||
|
|
||||||
foreach (DirectoryInfo subDir in srcDir.EnumerateDirectories())
|
|
||||||
{
|
|
||||||
CopyDirectory(subDir.FullName, Path.Combine(dstPath, subDir.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (FileInfo file in srcDir.EnumerateFiles())
|
|
||||||
{
|
|
||||||
file.CopyTo(Path.Combine(dstPath, file.Name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsMigrationNeeded()
|
|
||||||
{
|
|
||||||
if (AppDataManager.IsCustomBasePath) return false;
|
|
||||||
|
|
||||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
|
||||||
|
|
||||||
string oldBasePath = Path.Combine(appDataPath, "RyuFs");
|
|
||||||
string newBasePath = Path.Combine(appDataPath, "Ryujinx");
|
|
||||||
|
|
||||||
return Directory.Exists(oldBasePath) && !Directory.Exists(newBasePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
@ -1,219 +0,0 @@
|
||||||
using LibHac;
|
|
||||||
using LibHac.Common;
|
|
||||||
using LibHac.Fs;
|
|
||||||
using LibHac.Fs.Shim;
|
|
||||||
using LibHac.FsSystem;
|
|
||||||
using LibHac.Ncm;
|
|
||||||
using Ryujinx.HLE.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
using ApplicationId = LibHac.Ncm.ApplicationId;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
|
||||||
{
|
|
||||||
internal class SaveImporter
|
|
||||||
{
|
|
||||||
private FileSystemClient FsClient { get; }
|
|
||||||
private string ImportPath { get; }
|
|
||||||
|
|
||||||
public SaveImporter(string importPath, FileSystemClient destFsClient)
|
|
||||||
{
|
|
||||||
ImportPath = importPath;
|
|
||||||
FsClient = destFsClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of saves imported
|
|
||||||
public int Import()
|
|
||||||
{
|
|
||||||
return ImportSaves(FsClient, ImportPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ImportSaves(FileSystemClient fsClient, string rootSaveDir)
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(rootSaveDir))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveFinder finder = new SaveFinder();
|
|
||||||
finder.FindSaves(rootSaveDir);
|
|
||||||
|
|
||||||
foreach (SaveToImport save in finder.Saves)
|
|
||||||
{
|
|
||||||
Result importResult = ImportSave(fsClient, save);
|
|
||||||
|
|
||||||
if (importResult.IsFailure())
|
|
||||||
{
|
|
||||||
throw new HorizonResultException(importResult, $"Error importing save {save.Path}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return finder.Saves.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Result ImportSave(FileSystemClient fs, SaveToImport save)
|
|
||||||
{
|
|
||||||
SaveDataAttribute key = save.Attribute;
|
|
||||||
|
|
||||||
Result result = fs.CreateSaveData(new ApplicationId(key.ProgramId.Value), key.UserId, key.ProgramId.Value, 0, 0, 0);
|
|
||||||
if (result.IsFailure()) return result;
|
|
||||||
|
|
||||||
bool isOldMounted = false;
|
|
||||||
bool isNewMounted = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result = fs.Register("OldSave".ToU8Span(), new LocalFileSystem(save.Path));
|
|
||||||
if (result.IsFailure()) return result;
|
|
||||||
|
|
||||||
isOldMounted = true;
|
|
||||||
|
|
||||||
result = fs.MountSaveData("NewSave".ToU8Span(), new ApplicationId(key.ProgramId.Value), key.UserId);
|
|
||||||
if (result.IsFailure()) return result;
|
|
||||||
|
|
||||||
isNewMounted = true;
|
|
||||||
|
|
||||||
result = fs.CopyDirectory("OldSave:/", "NewSave:/");
|
|
||||||
if (result.IsFailure()) return result;
|
|
||||||
|
|
||||||
result = fs.Commit("NewSave".ToU8Span());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (isOldMounted)
|
|
||||||
{
|
|
||||||
fs.Unmount("OldSave".ToU8Span());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNewMounted)
|
|
||||||
{
|
|
||||||
fs.Unmount("NewSave".ToU8Span());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SaveFinder
|
|
||||||
{
|
|
||||||
public List<SaveToImport> Saves { get; } = new List<SaveToImport>();
|
|
||||||
|
|
||||||
public void FindSaves(string rootPath)
|
|
||||||
{
|
|
||||||
foreach (string subDir in Directory.EnumerateDirectories(rootPath))
|
|
||||||
{
|
|
||||||
if (TryGetUInt64(subDir, out ulong saveDataId))
|
|
||||||
{
|
|
||||||
SearchSaveId(subDir, saveDataId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SearchSaveId(string path, ulong saveDataId)
|
|
||||||
{
|
|
||||||
foreach (string subDir in Directory.EnumerateDirectories(path))
|
|
||||||
{
|
|
||||||
if (TryGetUserId(subDir, out UserId userId))
|
|
||||||
{
|
|
||||||
SearchUser(subDir, saveDataId, userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SearchUser(string path, ulong saveDataId, UserId userId)
|
|
||||||
{
|
|
||||||
foreach (string subDir in Directory.EnumerateDirectories(path))
|
|
||||||
{
|
|
||||||
if (TryGetUInt64(subDir, out ulong titleId) && TryGetDataPath(subDir, out string dataPath))
|
|
||||||
{
|
|
||||||
SaveDataAttribute attribute = new SaveDataAttribute
|
|
||||||
{
|
|
||||||
Type = SaveDataType.Account,
|
|
||||||
UserId = userId,
|
|
||||||
ProgramId = new ProgramId(titleId)
|
|
||||||
};
|
|
||||||
|
|
||||||
SaveToImport save = new SaveToImport(dataPath, attribute);
|
|
||||||
|
|
||||||
Saves.Add(save);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetDataPath(string path, out string dataPath)
|
|
||||||
{
|
|
||||||
string committedPath = Path.Combine(path, "0");
|
|
||||||
string workingPath = Path.Combine(path, "1");
|
|
||||||
|
|
||||||
if (Directory.Exists(committedPath) && Directory.EnumerateFileSystemEntries(committedPath).Any())
|
|
||||||
{
|
|
||||||
dataPath = committedPath;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Directory.Exists(workingPath) && Directory.EnumerateFileSystemEntries(workingPath).Any())
|
|
||||||
{
|
|
||||||
dataPath = workingPath;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
dataPath = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetUInt64(string path, out ulong converted)
|
|
||||||
{
|
|
||||||
string name = Path.GetFileName(path);
|
|
||||||
|
|
||||||
if (name.Length == 16)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
converted = Convert.ToUInt64(name, 16);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
converted = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetUserId(string path, out UserId userId)
|
|
||||||
{
|
|
||||||
string name = Path.GetFileName(path);
|
|
||||||
|
|
||||||
if (name.Length == 32)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
UInt128 id = new UInt128(name);
|
|
||||||
|
|
||||||
userId = Unsafe.As<UInt128, UserId>(ref id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
userId = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SaveToImport
|
|
||||||
{
|
|
||||||
public string Path { get; }
|
|
||||||
public SaveDataAttribute Attribute { get; }
|
|
||||||
|
|
||||||
public SaveToImport(string path, SaveDataAttribute attribute)
|
|
||||||
{
|
|
||||||
Path = path;
|
|
||||||
Attribute = attribute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,4 +21,4 @@ namespace Ryujinx.Ui
|
||||||
GpuName = gpuName;
|
GpuName = gpuName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
198
Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs
generated
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
using Gtk;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Widgets
|
||||||
|
{
|
||||||
|
public partial class GameTableContextMenu : Menu
|
||||||
|
{
|
||||||
|
private MenuItem _openSaveUserDirMenuItem;
|
||||||
|
private MenuItem _openSaveDeviceDirMenuItem;
|
||||||
|
private MenuItem _openSaveBcatDirMenuItem;
|
||||||
|
private MenuItem _manageTitleUpdatesMenuItem;
|
||||||
|
private MenuItem _manageDlcMenuItem;
|
||||||
|
private MenuItem _openTitleModDirMenuItem;
|
||||||
|
private Menu _extractSubMenu;
|
||||||
|
private MenuItem _extractMenuItem;
|
||||||
|
private MenuItem _extractRomFsMenuItem;
|
||||||
|
private MenuItem _extractExeFsMenuItem;
|
||||||
|
private MenuItem _extractLogoMenuItem;
|
||||||
|
private Menu _manageSubMenu;
|
||||||
|
private MenuItem _manageCacheMenuItem;
|
||||||
|
private MenuItem _purgePtcCacheMenuItem;
|
||||||
|
private MenuItem _purgeShaderCacheMenuItem;
|
||||||
|
private MenuItem _openPtcDirMenuItem;
|
||||||
|
private MenuItem _openShaderCacheDirMenuItem;
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// _openSaveUserDirMenuItem
|
||||||
|
//
|
||||||
|
_openSaveUserDirMenuItem = new MenuItem("Open User Save Directory")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the directory which contains Application's User Saves."
|
||||||
|
};
|
||||||
|
_openSaveUserDirMenuItem.Activated += OpenSaveUserDir_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _openSaveDeviceDirMenuItem
|
||||||
|
//
|
||||||
|
_openSaveDeviceDirMenuItem = new MenuItem("Open Device Save Directory")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the directory which contains Application's Device Saves."
|
||||||
|
};
|
||||||
|
_openSaveDeviceDirMenuItem.Activated += OpenSaveDeviceDir_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _openSaveBcatDirMenuItem
|
||||||
|
//
|
||||||
|
_openSaveBcatDirMenuItem = new MenuItem("Open BCAT Save Directory")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the directory which contains Application's BCAT Saves."
|
||||||
|
};
|
||||||
|
_openSaveBcatDirMenuItem.Activated += OpenSaveBcatDir_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _manageTitleUpdatesMenuItem
|
||||||
|
//
|
||||||
|
_manageTitleUpdatesMenuItem = new MenuItem("Manage Title Updates")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the Title Update management window"
|
||||||
|
};
|
||||||
|
_manageTitleUpdatesMenuItem.Activated += ManageTitleUpdates_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _manageDlcMenuItem
|
||||||
|
//
|
||||||
|
_manageDlcMenuItem = new MenuItem("Manage DLC")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the DLC management window"
|
||||||
|
};
|
||||||
|
_manageDlcMenuItem.Activated += ManageDlc_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _openTitleModDirMenuItem
|
||||||
|
//
|
||||||
|
_openTitleModDirMenuItem = new MenuItem("Open Mods Directory")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the directory which contains Application's Mods."
|
||||||
|
};
|
||||||
|
_openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _extractSubMenu
|
||||||
|
//
|
||||||
|
_extractSubMenu = new Menu();
|
||||||
|
|
||||||
|
//
|
||||||
|
// _extractMenuItem
|
||||||
|
//
|
||||||
|
_extractMenuItem = new MenuItem("Extract Data")
|
||||||
|
{
|
||||||
|
Submenu = _extractSubMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _extractRomFsMenuItem
|
||||||
|
//
|
||||||
|
_extractRomFsMenuItem = new MenuItem("RomFS")
|
||||||
|
{
|
||||||
|
TooltipText = "Extract the RomFS section from Application's current config (including updates)."
|
||||||
|
};
|
||||||
|
_extractRomFsMenuItem.Activated += ExtractRomFs_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _extractExeFsMenuItem
|
||||||
|
//
|
||||||
|
_extractExeFsMenuItem = new MenuItem("ExeFS")
|
||||||
|
{
|
||||||
|
TooltipText = "Extract the ExeFS section from Application's current config (including updates)."
|
||||||
|
};
|
||||||
|
_extractExeFsMenuItem.Activated += ExtractExeFs_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _extractLogoMenuItem
|
||||||
|
//
|
||||||
|
_extractLogoMenuItem = new MenuItem("Logo")
|
||||||
|
{
|
||||||
|
TooltipText = "Extract the Logo section from Application's current config (including updates)."
|
||||||
|
};
|
||||||
|
_extractLogoMenuItem.Activated += ExtractLogo_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _manageSubMenu
|
||||||
|
//
|
||||||
|
_manageSubMenu = new Menu();
|
||||||
|
|
||||||
|
//
|
||||||
|
// _manageCacheMenuItem
|
||||||
|
//
|
||||||
|
_manageCacheMenuItem = new MenuItem("Cache Management")
|
||||||
|
{
|
||||||
|
Submenu = _manageSubMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _purgePtcCacheMenuItem
|
||||||
|
//
|
||||||
|
_purgePtcCacheMenuItem = new MenuItem("Purge PPTC Cache")
|
||||||
|
{
|
||||||
|
TooltipText = "Delete the Application's PPTC cache."
|
||||||
|
};
|
||||||
|
_purgePtcCacheMenuItem.Activated += PurgePtcCache_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _purgeShaderCacheMenuItem
|
||||||
|
//
|
||||||
|
_purgeShaderCacheMenuItem = new MenuItem("Purge Shader Cache")
|
||||||
|
{
|
||||||
|
TooltipText = "Delete the Application's shader cache."
|
||||||
|
};
|
||||||
|
_purgeShaderCacheMenuItem.Activated += PurgeShaderCache_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _openPtcDirMenuItem
|
||||||
|
//
|
||||||
|
_openPtcDirMenuItem = new MenuItem("Open PPTC Directory")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the directory which contains the Application's PPTC cache."
|
||||||
|
};
|
||||||
|
_openPtcDirMenuItem.Activated += OpenPtcDir_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _openShaderCacheDirMenuItem
|
||||||
|
//
|
||||||
|
_openShaderCacheDirMenuItem = new MenuItem("Open Shader Cache Directory")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the directory which contains the Application's shader cache."
|
||||||
|
};
|
||||||
|
_openShaderCacheDirMenuItem.Activated += OpenShaderCacheDir_Clicked;
|
||||||
|
|
||||||
|
ShowComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowComponent()
|
||||||
|
{
|
||||||
|
_extractSubMenu.Append(_extractExeFsMenuItem);
|
||||||
|
_extractSubMenu.Append(_extractRomFsMenuItem);
|
||||||
|
_extractSubMenu.Append(_extractLogoMenuItem);
|
||||||
|
|
||||||
|
_manageSubMenu.Append(_purgePtcCacheMenuItem);
|
||||||
|
_manageSubMenu.Append(_purgeShaderCacheMenuItem);
|
||||||
|
_manageSubMenu.Append(_openPtcDirMenuItem);
|
||||||
|
_manageSubMenu.Append(_openShaderCacheDirMenuItem);
|
||||||
|
|
||||||
|
Add(_openSaveUserDirMenuItem);
|
||||||
|
Add(_openSaveDeviceDirMenuItem);
|
||||||
|
Add(_openSaveBcatDirMenuItem);
|
||||||
|
Add(new SeparatorMenuItem());
|
||||||
|
Add(_manageTitleUpdatesMenuItem);
|
||||||
|
Add(_manageDlcMenuItem);
|
||||||
|
Add(_openTitleModDirMenuItem);
|
||||||
|
Add(new SeparatorMenuItem());
|
||||||
|
Add(_manageCacheMenuItem);
|
||||||
|
Add(_extractMenuItem);
|
||||||
|
|
||||||
|
ShowAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,159 +11,66 @@ using LibHac.Ncm;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
|
using Ryujinx.Ui.Helper;
|
||||||
|
using Ryujinx.Ui.Windows;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using static LibHac.Fs.ApplicationSaveDataManagement;
|
using static LibHac.Fs.ApplicationSaveDataManagement;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Widgets
|
||||||
{
|
{
|
||||||
public class GameTableContextMenu : Menu
|
public partial class GameTableContextMenu : Menu
|
||||||
{
|
{
|
||||||
private readonly ListStore _gameTableStore;
|
private readonly MainWindow _parent;
|
||||||
private readonly TreeIter _rowIter;
|
private readonly VirtualFileSystem _virtualFileSystem;
|
||||||
private readonly VirtualFileSystem _virtualFileSystem;
|
|
||||||
|
|
||||||
private readonly BlitStruct<ApplicationControlProperty> _controlData;
|
private readonly BlitStruct<ApplicationControlProperty> _controlData;
|
||||||
|
|
||||||
|
private readonly string _titleFilePath;
|
||||||
|
private readonly string _titleName;
|
||||||
|
private readonly string _titleIdText;
|
||||||
|
private readonly ulong _titleId;
|
||||||
|
|
||||||
private MessageDialog _dialog;
|
private MessageDialog _dialog;
|
||||||
private bool _cancel;
|
private bool _cancel;
|
||||||
|
|
||||||
public GameTableContextMenu(ListStore gameTableStore, BlitStruct<ApplicationControlProperty> controlData, TreeIter rowIter, VirtualFileSystem virtualFileSystem)
|
public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData)
|
||||||
{
|
{
|
||||||
_gameTableStore = gameTableStore;
|
_parent = parent;
|
||||||
_rowIter = rowIter;
|
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
_titleFilePath = titleFilePath;
|
||||||
|
_titleName = titleName;
|
||||||
|
_titleIdText = titleId;
|
||||||
_controlData = controlData;
|
_controlData = controlData;
|
||||||
|
|
||||||
MenuItem openSaveUserDir = new MenuItem("Open User Save Directory")
|
if (!ulong.TryParse(_titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _titleId))
|
||||||
{
|
{
|
||||||
Sensitive = !Utilities.IsEmpty(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0,
|
GtkDialog.CreateErrorDialog("The selected game did not have a valid Title Id");
|
||||||
TooltipText = "Open the directory which contains Application's User Saves."
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem openSaveDeviceDir = new MenuItem("Open Device Save Directory")
|
return;
|
||||||
{
|
}
|
||||||
Sensitive = !Utilities.IsEmpty(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0,
|
|
||||||
TooltipText = "Open the directory which contains Application's Device Saves."
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem openSaveBcatDir = new MenuItem("Open BCAT Save Directory")
|
_openSaveUserDirMenuItem.Sensitive = !Utilities.IsEmpty(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0;
|
||||||
{
|
_openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsEmpty(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0;
|
||||||
Sensitive = !Utilities.IsEmpty(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0,
|
_openSaveBcatDirMenuItem.Sensitive = !Utilities.IsEmpty(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0;
|
||||||
TooltipText = "Open the directory which contains Application's BCAT Saves."
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem manageTitleUpdates = new MenuItem("Manage Title Updates")
|
string fileExt = System.IO.Path.GetExtension(_titleFilePath).ToLower();
|
||||||
{
|
bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci";
|
||||||
TooltipText = "Open the Title Update management window"
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem manageDlc = new MenuItem("Manage DLC")
|
_extractRomFsMenuItem.Sensitive = hasNca;
|
||||||
{
|
_extractExeFsMenuItem.Sensitive = hasNca;
|
||||||
TooltipText = "Open the DLC management window"
|
_extractLogoMenuItem.Sensitive = hasNca;
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem openTitleModDir = new MenuItem("Open Mods Directory")
|
PopupAtPointer(null);
|
||||||
{
|
|
||||||
TooltipText = "Open the directory which contains Application's Mods."
|
|
||||||
};
|
|
||||||
|
|
||||||
string ext = System.IO.Path.GetExtension(_gameTableStore.GetValue(_rowIter, 9).ToString()).ToLower();
|
|
||||||
bool hasNca = ext == ".nca" || ext == ".nsp" || ext == ".pfs0" || ext == ".xci";
|
|
||||||
|
|
||||||
MenuItem extractMenu = new MenuItem("Extract Data");
|
|
||||||
|
|
||||||
MenuItem extractRomFs = new MenuItem("RomFS")
|
|
||||||
{
|
|
||||||
Sensitive = hasNca,
|
|
||||||
TooltipText = "Extract the RomFS section from Application's current config (including updates)."
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem extractExeFs = new MenuItem("ExeFS")
|
|
||||||
{
|
|
||||||
Sensitive = hasNca,
|
|
||||||
TooltipText = "Extract the ExeFS section from Application's current config (including updates)."
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem extractLogo = new MenuItem("Logo")
|
|
||||||
{
|
|
||||||
Sensitive = hasNca,
|
|
||||||
TooltipText = "Extract the Logo section from Application's current config (including updates)."
|
|
||||||
};
|
|
||||||
|
|
||||||
Menu extractSubMenu = new Menu();
|
|
||||||
|
|
||||||
extractSubMenu.Append(extractExeFs);
|
|
||||||
extractSubMenu.Append(extractRomFs);
|
|
||||||
extractSubMenu.Append(extractLogo);
|
|
||||||
|
|
||||||
extractMenu.Submenu = extractSubMenu;
|
|
||||||
|
|
||||||
MenuItem managePtcMenu = new MenuItem("Cache Management");
|
|
||||||
|
|
||||||
MenuItem purgePtcCache = new MenuItem("Purge PPTC Cache")
|
|
||||||
{
|
|
||||||
TooltipText = "Delete the Application's PPTC cache."
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem purgeShaderCache = new MenuItem("Purge Shader Cache")
|
|
||||||
{
|
|
||||||
TooltipText = "Delete the Application's shader cache."
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem openPtcDir = new MenuItem("Open PPTC Directory")
|
|
||||||
{
|
|
||||||
TooltipText = "Open the directory which contains the Application's PPTC cache."
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuItem openShaderCacheDir = new MenuItem("Open Shader Cache Directory")
|
|
||||||
{
|
|
||||||
TooltipText = "Open the directory which contains the Application's shader cache."
|
|
||||||
};
|
|
||||||
|
|
||||||
Menu manageSubMenu = new Menu();
|
|
||||||
|
|
||||||
manageSubMenu.Append(purgePtcCache);
|
|
||||||
manageSubMenu.Append(purgeShaderCache);
|
|
||||||
manageSubMenu.Append(openPtcDir);
|
|
||||||
manageSubMenu.Append(openShaderCacheDir);
|
|
||||||
|
|
||||||
managePtcMenu.Submenu = manageSubMenu;
|
|
||||||
|
|
||||||
openSaveUserDir.Activated += OpenSaveUserDir_Clicked;
|
|
||||||
openSaveDeviceDir.Activated += OpenSaveDeviceDir_Clicked;
|
|
||||||
openSaveBcatDir.Activated += OpenSaveBcatDir_Clicked;
|
|
||||||
manageTitleUpdates.Activated += ManageTitleUpdates_Clicked;
|
|
||||||
manageDlc.Activated += ManageDlc_Clicked;
|
|
||||||
openTitleModDir.Activated += OpenTitleModDir_Clicked;
|
|
||||||
extractRomFs.Activated += ExtractRomFs_Clicked;
|
|
||||||
extractExeFs.Activated += ExtractExeFs_Clicked;
|
|
||||||
extractLogo.Activated += ExtractLogo_Clicked;
|
|
||||||
purgePtcCache.Activated += PurgePtcCache_Clicked;
|
|
||||||
purgeShaderCache.Activated += PurgeShaderCache_Clicked;
|
|
||||||
openPtcDir.Activated += OpenPtcDir_Clicked;
|
|
||||||
openShaderCacheDir.Activated += OpenShaderCacheDir_Clicked;
|
|
||||||
|
|
||||||
this.Add(openSaveUserDir);
|
|
||||||
this.Add(openSaveDeviceDir);
|
|
||||||
this.Add(openSaveBcatDir);
|
|
||||||
this.Add(new SeparatorMenuItem());
|
|
||||||
this.Add(manageTitleUpdates);
|
|
||||||
this.Add(manageDlc);
|
|
||||||
this.Add(openTitleModDir);
|
|
||||||
this.Add(new SeparatorMenuItem());
|
|
||||||
this.Add(managePtcMenu);
|
|
||||||
this.Add(extractMenu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryFindSaveData(string titleName, ulong titleId, BlitStruct<ApplicationControlProperty> controlHolder, SaveDataFilter filter, out ulong saveDataId)
|
private bool TryFindSaveData(string titleName, ulong titleId, BlitStruct<ApplicationControlProperty> controlHolder, SaveDataFilter filter, out ulong saveDataId)
|
||||||
|
@ -178,7 +85,6 @@ namespace Ryujinx.Ui
|
||||||
using MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.YesNo, null)
|
using MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.YesNo, null)
|
||||||
{
|
{
|
||||||
Title = "Ryujinx",
|
Title = "Ryujinx",
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
|
|
||||||
Text = $"There is no savedata for {titleName} [{titleId:x16}]",
|
Text = $"There is no savedata for {titleName} [{titleId:x16}]",
|
||||||
SecondaryText = "Would you like to create savedata for this game?",
|
SecondaryText = "Would you like to create savedata for this game?",
|
||||||
WindowPosition = WindowPosition.Center
|
WindowPosition = WindowPosition.Center
|
||||||
|
@ -191,7 +97,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
ref ApplicationControlProperty control = ref controlHolder.Value;
|
ref ApplicationControlProperty control = ref controlHolder.Value;
|
||||||
|
|
||||||
if (LibHac.Utilities.IsEmpty(controlHolder.ByteSpan))
|
if (Utilities.IsEmpty(controlHolder.ByteSpan))
|
||||||
{
|
{
|
||||||
// If the current application doesn't have a loaded control property, create a dummy one
|
// If the current application doesn't have a loaded control property, create a dummy one
|
||||||
// and set the savedata sizes so a user savedata will be created.
|
// and set the savedata sizes so a user savedata will be created.
|
||||||
|
@ -201,11 +107,10 @@ namespace Ryujinx.Ui
|
||||||
control.UserAccountSaveDataSize = 0x4000;
|
control.UserAccountSaveDataSize = 0x4000;
|
||||||
control.UserAccountSaveDataJournalSize = 0x4000;
|
control.UserAccountSaveDataJournalSize = 0x4000;
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Application,
|
Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
|
||||||
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Uid user = new Uid(1, 0);
|
Uid user = new Uid(1, 0); // TODO: Remove Hardcoded value.
|
||||||
|
|
||||||
result = EnsureApplicationSaveData(_virtualFileSystem.FsClient, out _, new LibHac.Ncm.ApplicationId(titleId), ref control, ref user);
|
result = EnsureApplicationSaveData(_virtualFileSystem.FsClient, out _, new LibHac.Ncm.ApplicationId(titleId), ref control, ref user);
|
||||||
|
|
||||||
|
@ -232,8 +137,15 @@ namespace Ryujinx.Ui
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetSaveDataDirectory(ulong saveDataId)
|
private void OpenSaveDir(SaveDataFilter saveDataFilter)
|
||||||
{
|
{
|
||||||
|
saveDataFilter.SetProgramId(new ProgramId(_titleId));
|
||||||
|
|
||||||
|
if (!TryFindSaveData(_titleName, _titleId, _controlData, saveDataFilter, out ulong saveDataId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string saveRootPath = System.IO.Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
|
string saveRootPath = System.IO.Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}");
|
||||||
|
|
||||||
if (!Directory.Exists(saveRootPath))
|
if (!Directory.Exists(saveRootPath))
|
||||||
|
@ -248,17 +160,19 @@ namespace Ryujinx.Ui
|
||||||
// If the committed directory exists, that path will be loaded the next time the savedata is mounted
|
// If the committed directory exists, that path will be loaded the next time the savedata is mounted
|
||||||
if (Directory.Exists(committedPath))
|
if (Directory.Exists(committedPath))
|
||||||
{
|
{
|
||||||
return committedPath;
|
OpenHelper.OpenFolder(committedPath);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// 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);
|
// 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;
|
OpenHelper.OpenFolder(workingPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractSection(NcaSectionType ncaSectionType, int programIndex = 0)
|
private void ExtractSection(NcaSectionType ncaSectionType, int programIndex = 0)
|
||||||
|
@ -266,24 +180,21 @@ namespace Ryujinx.Ui
|
||||||
FileChooserDialog fileChooser = new FileChooserDialog("Choose the folder to extract into", null, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Extract", ResponseType.Accept);
|
FileChooserDialog fileChooser = new FileChooserDialog("Choose the folder to extract into", null, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Extract", ResponseType.Accept);
|
||||||
fileChooser.SetPosition(WindowPosition.Center);
|
fileChooser.SetPosition(WindowPosition.Center);
|
||||||
|
|
||||||
int response = fileChooser.Run();
|
ResponseType response = (ResponseType)fileChooser.Run();
|
||||||
string destination = fileChooser.Filename;
|
string destination = fileChooser.Filename;
|
||||||
|
|
||||||
fileChooser.Dispose();
|
fileChooser.Dispose();
|
||||||
|
|
||||||
if (response == (int)ResponseType.Accept)
|
if (response == ResponseType.Accept)
|
||||||
{
|
{
|
||||||
Thread extractorThread = new Thread(() =>
|
Thread extractorThread = new Thread(() =>
|
||||||
{
|
{
|
||||||
string sourceFile = _gameTableStore.GetValue(_rowIter, 9).ToString();
|
|
||||||
|
|
||||||
Gtk.Application.Invoke(delegate
|
Gtk.Application.Invoke(delegate
|
||||||
{
|
{
|
||||||
_dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Cancel, null)
|
_dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Cancel, null)
|
||||||
{
|
{
|
||||||
Title = "Ryujinx - NCA Section Extractor",
|
Title = "Ryujinx - NCA Section Extractor",
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
|
SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...",
|
||||||
SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(sourceFile)}...",
|
|
||||||
WindowPosition = WindowPosition.Center
|
WindowPosition = WindowPosition.Center
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -295,18 +206,18 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
using (FileStream file = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
|
using (FileStream file = new FileStream(_titleFilePath, FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
Nca mainNca = null;
|
Nca mainNca = null;
|
||||||
Nca patchNca = null;
|
Nca patchNca = null;
|
||||||
|
|
||||||
if ((System.IO.Path.GetExtension(sourceFile).ToLower() == ".nsp") ||
|
if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") ||
|
||||||
(System.IO.Path.GetExtension(sourceFile).ToLower() == ".pfs0") ||
|
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") ||
|
||||||
(System.IO.Path.GetExtension(sourceFile).ToLower() == ".xci"))
|
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci"))
|
||||||
{
|
{
|
||||||
PartitionFileSystem pfs;
|
PartitionFileSystem pfs;
|
||||||
|
|
||||||
if (System.IO.Path.GetExtension(sourceFile) == ".xci")
|
if (System.IO.Path.GetExtension(_titleFilePath) == ".xci")
|
||||||
{
|
{
|
||||||
Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage());
|
Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage());
|
||||||
|
|
||||||
|
@ -338,7 +249,7 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (System.IO.Path.GetExtension(sourceFile).ToLower() == ".nca")
|
else if (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nca")
|
||||||
{
|
{
|
||||||
mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
|
mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
|
||||||
}
|
}
|
||||||
|
@ -355,7 +266,6 @@ namespace Ryujinx.Ui
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
(Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
|
(Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
|
||||||
|
|
||||||
if (updatePatchNca != null)
|
if (updatePatchNca != null)
|
||||||
|
@ -370,8 +280,8 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
FileSystemClient fsClient = _virtualFileSystem.FsClient;
|
FileSystemClient fsClient = _virtualFileSystem.FsClient;
|
||||||
|
|
||||||
string source = DateTime.Now.ToFileTime().ToString().Substring(10);
|
string source = DateTime.Now.ToFileTime().ToString()[10..];
|
||||||
string output = DateTime.Now.ToFileTime().ToString().Substring(10);
|
string output = DateTime.Now.ToFileTime().ToString()[10..];
|
||||||
|
|
||||||
fsClient.Register(source.ToU8Span(), ncaFileSystem);
|
fsClient.Register(source.ToU8Span(), ncaFileSystem);
|
||||||
fsClient.Register(output.ToU8Span(), new LocalFileSystem(destination));
|
fsClient.Register(output.ToU8Span(), new LocalFileSystem(destination));
|
||||||
|
@ -400,7 +310,6 @@ namespace Ryujinx.Ui
|
||||||
MessageDialog dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null)
|
MessageDialog dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null)
|
||||||
{
|
{
|
||||||
Title = "Ryujinx - NCA Section Extractor",
|
Title = "Ryujinx - NCA Section Extractor",
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
|
|
||||||
SecondaryText = "Extraction has completed successfully.",
|
SecondaryText = "Extraction has completed successfully.",
|
||||||
WindowPosition = WindowPosition.Center
|
WindowPosition = WindowPosition.Center
|
||||||
};
|
};
|
||||||
|
@ -510,111 +419,49 @@ namespace Ryujinx.Ui
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
// Events
|
// Events
|
||||||
|
//
|
||||||
private void OpenSaveUserDir_Clicked(object sender, EventArgs args)
|
private void OpenSaveUserDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
|
SaveDataFilter saveDataFilter = new SaveDataFilter();
|
||||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
saveDataFilter.SetUserId(new UserId(1, 0)); // TODO: Remove Hardcoded value.
|
||||||
|
|
||||||
if (!ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
OpenSaveDir(saveDataFilter);
|
||||||
{
|
|
||||||
GtkDialog.CreateErrorDialog("UI error: The selected game did not have a valid title ID");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveDataFilter filter = new SaveDataFilter();
|
|
||||||
filter.SetUserId(new UserId(1, 0));
|
|
||||||
|
|
||||||
OpenSaveDir(titleName, titleIdNumber, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenSaveDir(string titleName, ulong titleId, SaveDataFilter filter)
|
|
||||||
{
|
|
||||||
filter.SetProgramId(new ProgramId(titleId));
|
|
||||||
|
|
||||||
if (!TryFindSaveData(titleName, titleId, _controlData, filter, out ulong saveDataId))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string saveDir = GetSaveDataDirectory(saveDataId);
|
|
||||||
|
|
||||||
Process.Start(new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = saveDir,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "open"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args)
|
private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
|
SaveDataFilter saveDataFilter = new SaveDataFilter();
|
||||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
saveDataFilter.SetSaveDataType(SaveDataType.Device);
|
||||||
|
|
||||||
if (!ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
OpenSaveDir(saveDataFilter);
|
||||||
{
|
|
||||||
GtkDialog.CreateErrorDialog("UI error: The selected game did not have a valid title ID");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveDataFilter filter = new SaveDataFilter();
|
|
||||||
filter.SetSaveDataType(SaveDataType.Device);
|
|
||||||
|
|
||||||
OpenSaveDir(titleName, titleIdNumber, filter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSaveBcatDir_Clicked(object sender, EventArgs args)
|
private void OpenSaveBcatDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
|
SaveDataFilter saveDataFilter = new SaveDataFilter();
|
||||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
saveDataFilter.SetSaveDataType(SaveDataType.Bcat);
|
||||||
|
|
||||||
if (!ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
OpenSaveDir(saveDataFilter);
|
||||||
{
|
|
||||||
GtkDialog.CreateErrorDialog("UI error: The selected game did not have a valid title ID");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveDataFilter filter = new SaveDataFilter();
|
|
||||||
filter.SetSaveDataType(SaveDataType.Bcat);
|
|
||||||
|
|
||||||
OpenSaveDir(titleName, titleIdNumber, filter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ManageTitleUpdates_Clicked(object sender, EventArgs args)
|
private void ManageTitleUpdates_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
|
new TitleUpdateWindow(_parent, _virtualFileSystem, _titleIdText, _titleName).Show();
|
||||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
|
||||||
|
|
||||||
TitleUpdateWindow titleUpdateWindow = new TitleUpdateWindow(titleId, titleName, _virtualFileSystem);
|
|
||||||
titleUpdateWindow.Show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ManageDlc_Clicked(object sender, EventArgs args)
|
private void ManageDlc_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
|
new DlcWindow(_virtualFileSystem, _titleIdText, _titleName).Show();
|
||||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
|
||||||
|
|
||||||
DlcWindow dlcWindow = new DlcWindow(titleId, titleName, _virtualFileSystem);
|
|
||||||
dlcWindow.Show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenTitleModDir_Clicked(object sender, EventArgs args)
|
private void OpenTitleModDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
string modsBasePath = _virtualFileSystem.ModLoader.GetModsBasePath();
|
||||||
|
string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, _titleIdText);
|
||||||
|
|
||||||
var modsBasePath = _virtualFileSystem.ModLoader.GetModsBasePath();
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
var titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId);
|
|
||||||
|
|
||||||
Process.Start(new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = titleModsPath,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "open"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractRomFs_Clicked(object sender, EventArgs args)
|
private void ExtractRomFs_Clicked(object sender, EventArgs args)
|
||||||
|
@ -634,8 +481,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void OpenPtcDir_Clicked(object sender, EventArgs args)
|
private void OpenPtcDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu");
|
||||||
string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleId, "cache", "cpu");
|
|
||||||
|
|
||||||
string mainPath = System.IO.Path.Combine(ptcDir, "0");
|
string mainPath = System.IO.Path.Combine(ptcDir, "0");
|
||||||
string backupPath = System.IO.Path.Combine(ptcDir, "1");
|
string backupPath = System.IO.Path.Combine(ptcDir, "1");
|
||||||
|
@ -646,52 +492,40 @@ namespace Ryujinx.Ui
|
||||||
Directory.CreateDirectory(mainPath);
|
Directory.CreateDirectory(mainPath);
|
||||||
Directory.CreateDirectory(backupPath);
|
Directory.CreateDirectory(backupPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
Process.Start(new ProcessStartInfo
|
OpenHelper.OpenFolder(ptcDir);
|
||||||
{
|
|
||||||
FileName = ptcDir,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "open"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenShaderCacheDir_Clicked(object sender, EventArgs args)
|
private void OpenShaderCacheDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader");
|
||||||
string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleId, "cache", "shader");
|
|
||||||
|
|
||||||
if (!Directory.Exists(shaderCacheDir))
|
if (!Directory.Exists(shaderCacheDir))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(shaderCacheDir);
|
Directory.CreateDirectory(shaderCacheDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
Process.Start(new ProcessStartInfo
|
OpenHelper.OpenFolder(shaderCacheDir);
|
||||||
{
|
|
||||||
FileName = shaderCacheDir,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "open"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PurgePtcCache_Clicked(object sender, EventArgs args)
|
private void PurgePtcCache_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string[] tableEntry = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n");
|
DirectoryInfo mainDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0"));
|
||||||
string titleId = tableEntry[1].ToLower();
|
DirectoryInfo backupDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1"));
|
||||||
|
|
||||||
DirectoryInfo mainDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, titleId, "cache", "cpu", "0"));
|
MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the PPTC cache for :\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
|
||||||
DirectoryInfo backupDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, titleId, "cache", "cpu", "1"));
|
|
||||||
|
|
||||||
MessageDialog warningDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
|
|
||||||
{
|
|
||||||
Title = "Ryujinx - Warning",
|
|
||||||
Text = $"You are about to delete the PPTC cache for '{tableEntry[0]}'. Are you sure you want to proceed?",
|
|
||||||
WindowPosition = WindowPosition.Center
|
|
||||||
};
|
|
||||||
|
|
||||||
List<FileInfo> cacheFiles = new List<FileInfo>();
|
List<FileInfo> cacheFiles = new List<FileInfo>();
|
||||||
|
|
||||||
if (mainDir.Exists) { cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache")); }
|
if (mainDir.Exists)
|
||||||
if (backupDir.Exists) { cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); }
|
{
|
||||||
|
cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backupDir.Exists)
|
||||||
|
{
|
||||||
|
cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache"));
|
||||||
|
}
|
||||||
|
|
||||||
if (cacheFiles.Count > 0 && warningDialog.Run() == (int)ResponseType.Yes)
|
if (cacheFiles.Count > 0 && warningDialog.Run() == (int)ResponseType.Yes)
|
||||||
{
|
{
|
||||||
|
@ -703,7 +537,7 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"Error purging PPTC cache {file.Name}: {e}");
|
GtkDialog.CreateErrorDialog($"Error purging PPTC cache {file.Name}: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,21 +547,16 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void PurgeShaderCache_Clicked(object sender, EventArgs args)
|
private void PurgeShaderCache_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string[] tableEntry = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n");
|
DirectoryInfo shaderCacheDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader"));
|
||||||
string titleId = tableEntry[1].ToLower();
|
|
||||||
|
|
||||||
DirectoryInfo shaderCacheDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, titleId, "cache", "shader"));
|
MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
|
||||||
|
|
||||||
MessageDialog warningDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
|
|
||||||
{
|
|
||||||
Title = "Ryujinx - Warning",
|
|
||||||
Text = $"You are about to delete the shader cache for '{tableEntry[0]}'. Are you sure you want to proceed?",
|
|
||||||
WindowPosition = WindowPosition.Center
|
|
||||||
};
|
|
||||||
|
|
||||||
List<DirectoryInfo> cacheDirectory = new List<DirectoryInfo>();
|
List<DirectoryInfo> cacheDirectory = new List<DirectoryInfo>();
|
||||||
|
|
||||||
if (shaderCacheDir.Exists) { cacheDirectory.AddRange(shaderCacheDir.EnumerateDirectories("*")); }
|
if (shaderCacheDir.Exists)
|
||||||
|
{
|
||||||
|
cacheDirectory.AddRange(shaderCacheDir.EnumerateDirectories("*"));
|
||||||
|
}
|
||||||
|
|
||||||
if (cacheDirectory.Count > 0 && warningDialog.Run() == (int)ResponseType.Yes)
|
if (cacheDirectory.Count > 0 && warningDialog.Run() == (int)ResponseType.Yes)
|
||||||
{
|
{
|
||||||
|
@ -739,7 +568,7 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"Error purging shader cache {directory.Name}: {e}");
|
GtkDialog.CreateErrorDialog($"Error purging shader cache {directory.Name}: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -747,4 +576,4 @@ namespace Ryujinx.Ui
|
||||||
warningDialog.Dispose();
|
warningDialog.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using System.Reflection;
|
using Ryujinx.Common.Logging;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Widgets
|
||||||
{
|
{
|
||||||
internal class GtkDialog : MessageDialog
|
internal class GtkDialog : MessageDialog
|
||||||
{
|
{
|
||||||
|
@ -10,14 +10,15 @@ namespace Ryujinx.Ui
|
||||||
private GtkDialog(string title, string mainText, string secondaryText, MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok)
|
private GtkDialog(string title, string mainText, string secondaryText, MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok)
|
||||||
: base(null, DialogFlags.Modal, messageType, buttonsType, null)
|
: base(null, DialogFlags.Modal, messageType, buttonsType, null)
|
||||||
{
|
{
|
||||||
Title = title;
|
Title = title;
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
Text = mainText;
|
||||||
Text = mainText;
|
SecondaryText = secondaryText;
|
||||||
SecondaryText = secondaryText;
|
WindowPosition = WindowPosition.Center;
|
||||||
WindowPosition = WindowPosition.Center;
|
SecondaryUseMarkup = true;
|
||||||
Response += GtkDialog_Response;
|
|
||||||
|
|
||||||
SetSizeRequest(100, 20);
|
Response += GtkDialog_Response;
|
||||||
|
|
||||||
|
SetSizeRequest(200, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GtkDialog_Response(object sender, ResponseArgs args)
|
private void GtkDialog_Response(object sender, ResponseArgs args)
|
||||||
|
@ -25,9 +26,19 @@ namespace Ryujinx.Ui
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void CreateInfoDialog(string title, string mainText, string secondaryText)
|
internal static void CreateInfoDialog(string mainText, string secondaryText)
|
||||||
{
|
{
|
||||||
new GtkDialog(title, mainText, secondaryText, MessageType.Info).Run();
|
new GtkDialog("Ryujinx - Info", mainText, secondaryText, MessageType.Info).Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void CreateUpdaterInfoDialog(string mainText, string secondaryText)
|
||||||
|
{
|
||||||
|
new GtkDialog("Ryujinx - Updater", mainText, secondaryText, MessageType.Info).Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static MessageDialog CreateWaitingDialog(string mainText, string secondaryText)
|
||||||
|
{
|
||||||
|
return new GtkDialog("Ryujinx - Waiting", mainText, secondaryText, MessageType.Info, ButtonsType.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void CreateWarningDialog(string mainText, string secondaryText)
|
internal static void CreateWarningDialog(string mainText, string secondaryText)
|
||||||
|
@ -37,6 +48,8 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
internal static void CreateErrorDialog(string errorMessage)
|
internal static void CreateErrorDialog(string errorMessage)
|
||||||
{
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, errorMessage);
|
||||||
|
|
||||||
new GtkDialog("Ryujinx - Error", "Ryujinx has encountered an error", errorMessage, MessageType.Error).Run();
|
new GtkDialog("Ryujinx - Error", "Ryujinx has encountered an error", errorMessage, MessageType.Error).Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,10 +61,14 @@ namespace Ryujinx.Ui
|
||||||
internal static bool CreateChoiceDialog(string title, string mainText, string secondaryText)
|
internal static bool CreateChoiceDialog(string title, string mainText, string secondaryText)
|
||||||
{
|
{
|
||||||
if (_isChoiceDialogOpen)
|
if (_isChoiceDialogOpen)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_isChoiceDialogOpen = true;
|
_isChoiceDialogOpen = true;
|
||||||
|
|
||||||
ResponseType response = (ResponseType)new GtkDialog(title, mainText, secondaryText, MessageType.Question, ButtonsType.YesNo).Run();
|
ResponseType response = (ResponseType)new GtkDialog(title, mainText, secondaryText, MessageType.Question, ButtonsType.YesNo).Run();
|
||||||
|
|
||||||
_isChoiceDialogOpen = false;
|
_isChoiceDialogOpen = false;
|
||||||
|
|
||||||
return response == ResponseType.Yes;
|
return response == ResponseType.Yes;
|
|
@ -1,10 +1,9 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Widgets
|
||||||
{
|
{
|
||||||
public class ProfileDialog : Dialog
|
public class ProfileDialog : Dialog
|
||||||
{
|
{
|
||||||
|
@ -15,18 +14,16 @@ namespace Ryujinx.Ui
|
||||||
[GUI] Label _errorMessage;
|
[GUI] Label _errorMessage;
|
||||||
#pragma warning restore CS0649, IDE0044
|
#pragma warning restore CS0649, IDE0044
|
||||||
|
|
||||||
public ProfileDialog() : this(new Builder("Ryujinx.Ui.ProfileDialog.glade")) { }
|
public ProfileDialog() : this(new Builder("Ryujinx.Ui.Widgets.ProfileDialog.glade")) { }
|
||||||
|
|
||||||
private ProfileDialog(Builder builder) : base(builder.GetObject("_profileDialog").Handle)
|
private ProfileDialog(Builder builder) : base(builder.GetObject("_profileDialog").Handle)
|
||||||
{
|
{
|
||||||
builder.Autoconnect(this);
|
builder.Autoconnect(this);
|
||||||
|
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OkToggle_Activated(object sender, EventArgs args)
|
private void OkToggle_Activated(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
((ToggleButton)sender).SetStateFlags(0, true);
|
((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true);
|
||||||
|
|
||||||
bool validFileName = true;
|
bool validFileName = true;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Ui.Diagnostic
|
namespace Ryujinx.Ui.Widgets
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represent a common error that could be reported to the user by the emulator.
|
/// Represent a common error that could be reported to the user by the emulator.
|
||||||
|
@ -36,4 +36,4 @@
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Unknown = 0xDEAD
|
Unknown = 0xDEAD
|
||||||
}
|
}
|
||||||
}
|
}
|
122
Ryujinx/Ui/Widgets/UserErrorDialog.cs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
using Gtk;
|
||||||
|
using Ryujinx.Ui.Helper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Widgets
|
||||||
|
{
|
||||||
|
internal class UserErrorDialog : MessageDialog
|
||||||
|
{
|
||||||
|
private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide";
|
||||||
|
private const int OkResponseId = 0;
|
||||||
|
private const int SetupGuideResponseId = 1;
|
||||||
|
|
||||||
|
private readonly UserError _userError;
|
||||||
|
|
||||||
|
private UserErrorDialog(UserError error) : base(null, DialogFlags.Modal, MessageType.Error, ButtonsType.None, null)
|
||||||
|
{
|
||||||
|
_userError = error;
|
||||||
|
|
||||||
|
WindowPosition = WindowPosition.Center;
|
||||||
|
SecondaryUseMarkup = true;
|
||||||
|
|
||||||
|
Response += UserErrorDialog_Response;
|
||||||
|
|
||||||
|
SetSizeRequest(120, 50);
|
||||||
|
|
||||||
|
AddButton("OK", OkResponseId);
|
||||||
|
|
||||||
|
bool isInSetupGuide = IsCoveredBySetupGuide(error);
|
||||||
|
|
||||||
|
if (isInSetupGuide)
|
||||||
|
{
|
||||||
|
AddButton("Open the Setup Guide", SetupGuideResponseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
string errorCode = GetErrorCode(error);
|
||||||
|
|
||||||
|
SecondaryUseMarkup = true;
|
||||||
|
|
||||||
|
Title = $"Ryujinx error ({errorCode})";
|
||||||
|
Text = $"{errorCode}: {GetErrorTitle(error)}";
|
||||||
|
SecondaryText = GetErrorDescription(error);
|
||||||
|
|
||||||
|
if (isInSetupGuide)
|
||||||
|
{
|
||||||
|
SecondaryText += "\n<b>For more information on how to fix this error, follow our Setup Guide.</b>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetErrorCode(UserError error)
|
||||||
|
{
|
||||||
|
return $"RYU-{(uint)error:X4}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetErrorTitle(UserError error)
|
||||||
|
{
|
||||||
|
return error switch
|
||||||
|
{
|
||||||
|
UserError.NoKeys => "Keys not found",
|
||||||
|
UserError.NoFirmware => "Firmware not found",
|
||||||
|
UserError.FirmwareParsingFailed => "Firmware parsing error",
|
||||||
|
UserError.ApplicationNotFound => "Application not found",
|
||||||
|
UserError.Unknown => "Unknown error",
|
||||||
|
_ => "Undefined error",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetErrorDescription(UserError error)
|
||||||
|
{
|
||||||
|
return error switch
|
||||||
|
{
|
||||||
|
UserError.NoKeys => "Ryujinx was unable to find your 'prod.keys' file",
|
||||||
|
UserError.NoFirmware => "Ryujinx was unable to find any firmwares installed",
|
||||||
|
UserError.FirmwareParsingFailed => "Ryujinx was unable to parse the provided firmware. This is usually caused by outdated keys.",
|
||||||
|
UserError.ApplicationNotFound => "Ryujinx couldn't find a valid application at the given path.",
|
||||||
|
UserError.Unknown => "An unknown error occured!",
|
||||||
|
_ => "An undefined error occured! This shouldn't happen, please contact a dev!",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCoveredBySetupGuide(UserError error)
|
||||||
|
{
|
||||||
|
return error switch
|
||||||
|
{
|
||||||
|
UserError.NoKeys or
|
||||||
|
UserError.NoFirmware or
|
||||||
|
UserError.FirmwareParsingFailed => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSetupGuideUrl(UserError error)
|
||||||
|
{
|
||||||
|
if (!IsCoveredBySetupGuide(error))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error switch
|
||||||
|
{
|
||||||
|
UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys",
|
||||||
|
UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware",
|
||||||
|
_ => SetupGuideUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserErrorDialog_Response(object sender, ResponseArgs args)
|
||||||
|
{
|
||||||
|
int responseId = (int)args.ResponseId;
|
||||||
|
|
||||||
|
if (responseId == SetupGuideResponseId)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl(GetSetupGuideUrl(_userError));
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateUserErrorDialog(UserError error)
|
||||||
|
{
|
||||||
|
new UserErrorDialog(error).Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
467
Ryujinx/Ui/Windows/AboutWindow.Designer.cs
generated
Normal file
|
@ -0,0 +1,467 @@
|
||||||
|
using Gtk;
|
||||||
|
using Pango;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Windows
|
||||||
|
{
|
||||||
|
public partial class AboutWindow : Window
|
||||||
|
{
|
||||||
|
private Box _mainBox;
|
||||||
|
private Box _leftBox;
|
||||||
|
private Box _logoBox;
|
||||||
|
private Image _ryujinxLogo;
|
||||||
|
private Box _logoTextBox;
|
||||||
|
private Label _ryujinxLabel;
|
||||||
|
private Label _ryujinxPhoneticLabel;
|
||||||
|
private EventBox _ryujinxLink;
|
||||||
|
private Label _ryujinxLinkLabel;
|
||||||
|
private Label _versionLabel;
|
||||||
|
private Label _disclaimerLabel;
|
||||||
|
private Box _socialBox;
|
||||||
|
private EventBox _patreonEventBox;
|
||||||
|
private Box _patreonBox;
|
||||||
|
private Image _patreonLogo;
|
||||||
|
private Label _patreonLabel;
|
||||||
|
private EventBox _githubEventBox;
|
||||||
|
private Box _githubBox;
|
||||||
|
private Image _githubLogo;
|
||||||
|
private Label _githubLabel;
|
||||||
|
private Box _discordBox;
|
||||||
|
private EventBox _discordEventBox;
|
||||||
|
private Image _discordLogo;
|
||||||
|
private Label _discordLabel;
|
||||||
|
private EventBox _twitterEventBox;
|
||||||
|
private Box _twitterBox;
|
||||||
|
private Image _twitterLogo;
|
||||||
|
private Label _twitterLabel;
|
||||||
|
private Separator _separator;
|
||||||
|
private Box _rightBox;
|
||||||
|
private Label _aboutLabel;
|
||||||
|
private Label _aboutDescriptionLabel;
|
||||||
|
private Label _createdByLabel;
|
||||||
|
private TextView _createdByText;
|
||||||
|
private EventBox _contributorsEventBox;
|
||||||
|
private Label _contributorsLinkLabel;
|
||||||
|
private Label _patreonNamesLabel;
|
||||||
|
private ScrolledWindow _patreonNamesScrolled;
|
||||||
|
private TextView _patreonNamesText;
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
|
||||||
|
#pragma warning disable CS0612
|
||||||
|
|
||||||
|
//
|
||||||
|
// AboutWindow
|
||||||
|
//
|
||||||
|
CanFocus = false;
|
||||||
|
Resizable = false;
|
||||||
|
Modal = true;
|
||||||
|
WindowPosition = WindowPosition.Center;
|
||||||
|
DefaultWidth = 800;
|
||||||
|
DefaultHeight = 450;
|
||||||
|
TypeHint = Gdk.WindowTypeHint.Dialog;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _mainBox
|
||||||
|
//
|
||||||
|
_mainBox = new Box(Orientation.Horizontal, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// _leftBox
|
||||||
|
//
|
||||||
|
_leftBox = new Box(Orientation.Vertical, 0)
|
||||||
|
{
|
||||||
|
Margin = 15,
|
||||||
|
MarginLeft = 30,
|
||||||
|
MarginRight = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _logoBox
|
||||||
|
//
|
||||||
|
_logoBox = new Box(Orientation.Horizontal, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// _ryujinxLogo
|
||||||
|
//
|
||||||
|
_ryujinxLogo = new Image(new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Ryujinx.png", 100, 100))
|
||||||
|
{
|
||||||
|
Margin = 10,
|
||||||
|
MarginLeft = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _logoTextBox
|
||||||
|
//
|
||||||
|
_logoTextBox = new Box(Orientation.Vertical, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// _ryujinxLabel
|
||||||
|
//
|
||||||
|
_ryujinxLabel = new Label("Ryujinx")
|
||||||
|
{
|
||||||
|
MarginTop = 15,
|
||||||
|
Justify = Justification.Center,
|
||||||
|
Attributes = new AttrList()
|
||||||
|
};
|
||||||
|
_ryujinxLabel.Attributes.Insert(new Pango.AttrScale(2.7f));
|
||||||
|
|
||||||
|
//
|
||||||
|
// _ryujinxPhoneticLabel
|
||||||
|
//
|
||||||
|
_ryujinxPhoneticLabel = new Label("(REE-YOU-JI-NX)")
|
||||||
|
{
|
||||||
|
Justify = Justification.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _ryujinxLink
|
||||||
|
//
|
||||||
|
_ryujinxLink = new EventBox()
|
||||||
|
{
|
||||||
|
Margin = 5
|
||||||
|
};
|
||||||
|
_ryujinxLink.ButtonPressEvent += RyujinxButton_Pressed;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _ryujinxLinkLabel
|
||||||
|
//
|
||||||
|
_ryujinxLinkLabel = new Label("www.ryujinx.org")
|
||||||
|
{
|
||||||
|
TooltipText = "Click to open the Ryujinx website in your default browser.",
|
||||||
|
Justify = Justification.Center,
|
||||||
|
Attributes = new AttrList()
|
||||||
|
};
|
||||||
|
_ryujinxLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
|
||||||
|
|
||||||
|
//
|
||||||
|
// _versionLabel
|
||||||
|
//
|
||||||
|
_versionLabel = new Label(Program.Version)
|
||||||
|
{
|
||||||
|
Expand = true,
|
||||||
|
Justify = Justification.Center,
|
||||||
|
Margin = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _disclaimerLabel
|
||||||
|
//
|
||||||
|
_disclaimerLabel = new Label("Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.")
|
||||||
|
{
|
||||||
|
Expand = true,
|
||||||
|
Justify = Justification.Center,
|
||||||
|
Margin = 5,
|
||||||
|
Attributes = new AttrList()
|
||||||
|
};
|
||||||
|
_disclaimerLabel.Attributes.Insert(new Pango.AttrScale(0.8f));
|
||||||
|
|
||||||
|
//
|
||||||
|
// _socialBox
|
||||||
|
//
|
||||||
|
_socialBox = new Box(Orientation.Horizontal, 0)
|
||||||
|
{
|
||||||
|
Margin = 25,
|
||||||
|
MarginBottom = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _patreonEventBox
|
||||||
|
//
|
||||||
|
_patreonEventBox = new EventBox()
|
||||||
|
{
|
||||||
|
TooltipText = "Click to open the Ryujinx Patreon page in your default browser."
|
||||||
|
};
|
||||||
|
_patreonEventBox.ButtonPressEvent += PatreonButton_Pressed;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _patreonBox
|
||||||
|
//
|
||||||
|
_patreonBox = new Box(Orientation.Vertical, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// _patreonLogo
|
||||||
|
//
|
||||||
|
_patreonLogo = new Image(new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Patreon.png", 30, 30))
|
||||||
|
{
|
||||||
|
Margin = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _patreonLabel
|
||||||
|
//
|
||||||
|
_patreonLabel = new Label("Patreon")
|
||||||
|
{
|
||||||
|
Justify = Justification.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _githubEventBox
|
||||||
|
//
|
||||||
|
_githubEventBox = new EventBox()
|
||||||
|
{
|
||||||
|
TooltipText = "Click to open the Ryujinx GitHub page in your default browser."
|
||||||
|
};
|
||||||
|
_githubEventBox.ButtonPressEvent += GitHubButton_Pressed;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _githubBox
|
||||||
|
//
|
||||||
|
_githubBox = new Box(Orientation.Vertical, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// _githubLogo
|
||||||
|
//
|
||||||
|
_githubLogo = new Image(new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_GitHub.png", 30, 30))
|
||||||
|
{
|
||||||
|
Margin = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _githubLabel
|
||||||
|
//
|
||||||
|
_githubLabel = new Label("GitHub")
|
||||||
|
{
|
||||||
|
Justify = Justification.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _discordBox
|
||||||
|
//
|
||||||
|
_discordBox = new Box(Orientation.Vertical, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// _discordEventBox
|
||||||
|
//
|
||||||
|
_discordEventBox = new EventBox()
|
||||||
|
{
|
||||||
|
TooltipText = "Click to open an invite to the Ryujinx Discord server in your default browser."
|
||||||
|
};
|
||||||
|
_discordEventBox.ButtonPressEvent += DiscordButton_Pressed;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _discordLogo
|
||||||
|
//
|
||||||
|
_discordLogo = new Image(new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Discord.png", 30, 30))
|
||||||
|
{
|
||||||
|
Margin = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _discordLabel
|
||||||
|
//
|
||||||
|
_discordLabel = new Label("Discord")
|
||||||
|
{
|
||||||
|
Justify = Justification.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _twitterEventBox
|
||||||
|
//
|
||||||
|
_twitterEventBox = new EventBox()
|
||||||
|
{
|
||||||
|
TooltipText = "Click to open the Ryujinx Twitter page in your default browser."
|
||||||
|
};
|
||||||
|
_twitterEventBox.ButtonPressEvent += TwitterButton_Pressed;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _twitterBox
|
||||||
|
//
|
||||||
|
_twitterBox = new Box(Orientation.Vertical, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// _twitterLogo
|
||||||
|
//
|
||||||
|
_twitterLogo = new Image(new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Twitter.png", 30, 30))
|
||||||
|
{
|
||||||
|
Margin = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _twitterLabel
|
||||||
|
//
|
||||||
|
_twitterLabel = new Label("Twitter")
|
||||||
|
{
|
||||||
|
Justify = Justification.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _separator
|
||||||
|
//
|
||||||
|
_separator = new Separator(Orientation.Vertical)
|
||||||
|
{
|
||||||
|
Margin = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _rightBox
|
||||||
|
//
|
||||||
|
_rightBox = new Box(Orientation.Vertical, 0)
|
||||||
|
{
|
||||||
|
Margin = 15,
|
||||||
|
MarginTop = 40
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _aboutLabel
|
||||||
|
//
|
||||||
|
_aboutLabel = new Label("About :")
|
||||||
|
{
|
||||||
|
Halign = Align.Start,
|
||||||
|
Attributes = new AttrList()
|
||||||
|
};
|
||||||
|
_aboutLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
|
||||||
|
_aboutLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
|
||||||
|
|
||||||
|
//
|
||||||
|
// _aboutDescriptionLabel
|
||||||
|
//
|
||||||
|
_aboutDescriptionLabel = new Label("Ryujinx is an emulator for the Nintendo Switch™.\n" +
|
||||||
|
"Please support us on Patreon.\n" +
|
||||||
|
"Get all the latest news on our Twitter or Discord.\n" +
|
||||||
|
"Developers interested in contributing can find out more on our GitHub or Discord.")
|
||||||
|
{
|
||||||
|
Margin = 15,
|
||||||
|
Halign = Align.Start
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// _createdByLabel
|
||||||
|
//
|
||||||
|
_createdByLabel = new Label("Maintained by :")
|
||||||
|
{
|
||||||
|
Halign = Align.Start,
|
||||||
|
Attributes = new AttrList()
|
||||||
|
};
|
||||||
|
_createdByLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
|
||||||
|
_createdByLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
|
||||||
|
|
||||||
|
//
|
||||||
|
// _createdByText
|
||||||
|
//
|
||||||
|
_createdByText = new TextView()
|
||||||
|
{
|
||||||
|
WrapMode = Gtk.WrapMode.Word,
|
||||||
|
Editable = false,
|
||||||
|
CursorVisible = false,
|
||||||
|
Margin = 15,
|
||||||
|
MarginRight = 30
|
||||||
|
};
|
||||||
|
_createdByText.Buffer.Text = "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD« and more...";
|
||||||
|
|
||||||
|
//
|
||||||
|
// _contributorsEventBox
|
||||||
|
//
|
||||||
|
_contributorsEventBox = new EventBox();
|
||||||
|
_contributorsEventBox.ButtonPressEvent += ContributorsButton_Pressed;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _contributorsLinkLabel
|
||||||
|
//
|
||||||
|
_contributorsLinkLabel = new Label("See All Contributors...")
|
||||||
|
{
|
||||||
|
TooltipText = "Click to open the Contributors page in your default browser.",
|
||||||
|
MarginRight = 30,
|
||||||
|
Halign = Align.End,
|
||||||
|
Attributes = new AttrList()
|
||||||
|
};
|
||||||
|
_contributorsLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
|
||||||
|
|
||||||
|
//
|
||||||
|
// _patreonNamesLabel
|
||||||
|
//
|
||||||
|
_patreonNamesLabel = new Label("Supported on Patreon by :")
|
||||||
|
{
|
||||||
|
Halign = Align.Start,
|
||||||
|
Attributes = new AttrList()
|
||||||
|
};
|
||||||
|
_patreonNamesLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
|
||||||
|
_patreonNamesLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
|
||||||
|
|
||||||
|
//
|
||||||
|
// _patreonNamesScrolled
|
||||||
|
//
|
||||||
|
_patreonNamesScrolled = new ScrolledWindow()
|
||||||
|
{
|
||||||
|
Margin = 15,
|
||||||
|
MarginRight = 30,
|
||||||
|
Expand = true,
|
||||||
|
ShadowType = ShadowType.In
|
||||||
|
};
|
||||||
|
_patreonNamesScrolled.SetPolicy(PolicyType.Never, PolicyType.Automatic);
|
||||||
|
|
||||||
|
//
|
||||||
|
// _patreonNamesText
|
||||||
|
//
|
||||||
|
_patreonNamesText = new TextView()
|
||||||
|
{
|
||||||
|
WrapMode = Gtk.WrapMode.Word
|
||||||
|
};
|
||||||
|
_patreonNamesText.Buffer.Text = "Loading...";
|
||||||
|
_patreonNamesText.SetProperty("editable", new GLib.Value(false));
|
||||||
|
|
||||||
|
#pragma warning restore CS0612
|
||||||
|
|
||||||
|
ShowComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowComponent()
|
||||||
|
{
|
||||||
|
_logoBox.Add(_ryujinxLogo);
|
||||||
|
|
||||||
|
_ryujinxLink.Add(_ryujinxLinkLabel);
|
||||||
|
|
||||||
|
_logoTextBox.Add(_ryujinxLabel);
|
||||||
|
_logoTextBox.Add(_ryujinxPhoneticLabel);
|
||||||
|
_logoTextBox.Add(_ryujinxLink);
|
||||||
|
|
||||||
|
_logoBox.Add(_logoTextBox);
|
||||||
|
|
||||||
|
_patreonBox.Add(_patreonLogo);
|
||||||
|
_patreonBox.Add(_patreonLabel);
|
||||||
|
_patreonEventBox.Add(_patreonBox);
|
||||||
|
|
||||||
|
_githubBox.Add(_githubLogo);
|
||||||
|
_githubBox.Add(_githubLabel);
|
||||||
|
_githubEventBox.Add(_githubBox);
|
||||||
|
|
||||||
|
_discordBox.Add(_discordLogo);
|
||||||
|
_discordBox.Add(_discordLabel);
|
||||||
|
_discordEventBox.Add(_discordBox);
|
||||||
|
|
||||||
|
_twitterBox.Add(_twitterLogo);
|
||||||
|
_twitterBox.Add(_twitterLabel);
|
||||||
|
_twitterEventBox.Add(_twitterBox);
|
||||||
|
|
||||||
|
_socialBox.Add(_patreonEventBox);
|
||||||
|
_socialBox.Add(_githubEventBox);
|
||||||
|
_socialBox.Add(_discordEventBox);
|
||||||
|
_socialBox.Add(_twitterEventBox);
|
||||||
|
|
||||||
|
_leftBox.Add(_logoBox);
|
||||||
|
_leftBox.Add(_versionLabel);
|
||||||
|
_leftBox.Add(_disclaimerLabel);
|
||||||
|
_leftBox.Add(_socialBox);
|
||||||
|
|
||||||
|
_contributorsEventBox.Add(_contributorsLinkLabel);
|
||||||
|
_patreonNamesScrolled.Add(_patreonNamesText);
|
||||||
|
|
||||||
|
_rightBox.Add(_aboutLabel);
|
||||||
|
_rightBox.Add(_aboutDescriptionLabel);
|
||||||
|
_rightBox.Add(_createdByLabel);
|
||||||
|
_rightBox.Add(_createdByText);
|
||||||
|
_rightBox.Add(_contributorsEventBox);
|
||||||
|
_rightBox.Add(_patreonNamesLabel);
|
||||||
|
_rightBox.Add(_patreonNamesScrolled);
|
||||||
|
|
||||||
|
_mainBox.Add(_leftBox);
|
||||||
|
_mainBox.Add(_separator);
|
||||||
|
_mainBox.Add(_rightBox);
|
||||||
|
|
||||||
|
Add(_mainBox);
|
||||||
|
|
||||||
|
ShowAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
Ryujinx/Ui/Windows/AboutWindow.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
using Gtk;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.Ui.Helper;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Windows
|
||||||
|
{
|
||||||
|
public partial class AboutWindow : Window
|
||||||
|
{
|
||||||
|
public AboutWindow() : base($"Ryujinx {Program.Version} - About")
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
_ = DownloadPatronsJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DownloadPatronsJson()
|
||||||
|
{
|
||||||
|
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||||
|
{
|
||||||
|
_patreonNamesText.Buffer.Text = "Connection Error.";
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpClient httpClient = new HttpClient();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
||||||
|
|
||||||
|
_patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_patreonNamesText.Buffer.Text = "API Error.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Events
|
||||||
|
//
|
||||||
|
private void RyujinxButton_Pressed(object sender, ButtonPressEventArgs args)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl("https://ryujinx.org");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PatreonButton_Pressed(object sender, ButtonPressEventArgs args)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl("https://www.patreon.com/ryujinx");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GitHubButton_Pressed(object sender, ButtonPressEventArgs args)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DiscordButton_Pressed(object sender, ButtonPressEventArgs args)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl("https://discordapp.com/invite/N2FmfVc");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TwitterButton_Pressed(object sender, ButtonPressEventArgs args)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl("https://twitter.com/RyujinxEmu");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ContributorsButton_Pressed(object sender, ButtonPressEventArgs args)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Configuration;
|
using Ryujinx.Configuration;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -15,14 +15,14 @@ using System.Threading;
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
using Key = Ryujinx.Configuration.Hid.Key;
|
using Key = Ryujinx.Configuration.Hid.Key;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
public class ControllerWindow : Window
|
public class ControllerWindow : Window
|
||||||
{
|
{
|
||||||
private PlayerIndex _playerIndex;
|
private readonly PlayerIndex _playerIndex;
|
||||||
private InputConfig _inputConfig;
|
private readonly InputConfig _inputConfig;
|
||||||
private bool _isWaitingForInput;
|
|
||||||
private VirtualFileSystem _virtualFileSystem;
|
private bool _isWaitingForInput;
|
||||||
|
|
||||||
#pragma warning disable CS0649, IDE0044
|
#pragma warning disable CS0649, IDE0044
|
||||||
[GUI] Adjustment _controllerDeadzoneLeft;
|
[GUI] Adjustment _controllerDeadzoneLeft;
|
||||||
|
@ -90,17 +90,14 @@ namespace Ryujinx.Ui
|
||||||
[GUI] Image _controllerImage;
|
[GUI] Image _controllerImage;
|
||||||
#pragma warning restore CS0649, IDE0044
|
#pragma warning restore CS0649, IDE0044
|
||||||
|
|
||||||
public ControllerWindow(PlayerIndex controllerId, VirtualFileSystem virtualFileSystem) : this(new Builder("Ryujinx.Ui.ControllerWindow.glade"), controllerId, virtualFileSystem) { }
|
public ControllerWindow(PlayerIndex controllerId) : this(new Builder("Ryujinx.Ui.Windows.ControllerWindow.glade"), controllerId) { }
|
||||||
|
|
||||||
private ControllerWindow(Builder builder, PlayerIndex controllerId, VirtualFileSystem virtualFileSystem) : base(builder.GetObject("_controllerWin").Handle)
|
private ControllerWindow(Builder builder, PlayerIndex controllerId) : base(builder.GetObject("_controllerWin").Handle)
|
||||||
{
|
{
|
||||||
builder.Autoconnect(this);
|
builder.Autoconnect(this);
|
||||||
|
|
||||||
this.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
_playerIndex = controllerId;
|
||||||
|
_inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerIndex);
|
||||||
_playerIndex = controllerId;
|
|
||||||
_virtualFileSystem = virtualFileSystem;
|
|
||||||
_inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerIndex);
|
|
||||||
|
|
||||||
Title = $"Ryujinx - Controller Settings - {_playerIndex}";
|
Title = $"Ryujinx - Controller Settings - {_playerIndex}";
|
||||||
|
|
||||||
|
@ -119,7 +116,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
_controllerType.Active = 0; // Set initial value to first in list.
|
_controllerType.Active = 0; // Set initial value to first in list.
|
||||||
|
|
||||||
//Bind Events
|
// Bind Events.
|
||||||
_lStickX.Clicked += Button_Pressed;
|
_lStickX.Clicked += Button_Pressed;
|
||||||
_lStickY.Clicked += Button_Pressed;
|
_lStickY.Clicked += Button_Pressed;
|
||||||
_lStickUp.Clicked += Button_Pressed;
|
_lStickUp.Clicked += Button_Pressed;
|
||||||
|
@ -153,12 +150,15 @@ namespace Ryujinx.Ui
|
||||||
_rSl.Clicked += Button_Pressed;
|
_rSl.Clicked += Button_Pressed;
|
||||||
_rSr.Clicked += Button_Pressed;
|
_rSr.Clicked += Button_Pressed;
|
||||||
|
|
||||||
// Setup current values
|
// Setup current values.
|
||||||
UpdateInputDeviceList();
|
UpdateInputDeviceList();
|
||||||
SetAvailableOptions();
|
SetAvailableOptions();
|
||||||
|
|
||||||
ClearValues();
|
ClearValues();
|
||||||
if (_inputDevice.ActiveId != null) SetCurrentValues();
|
if (_inputDevice.ActiveId != null)
|
||||||
|
{
|
||||||
|
SetCurrentValues();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateInputDeviceList()
|
private void UpdateInputDeviceList()
|
||||||
|
@ -193,7 +193,7 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("keyboard"))
|
if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("keyboard"))
|
||||||
{
|
{
|
||||||
this.ShowAll();
|
ShowAll();
|
||||||
_leftStickController.Hide();
|
_leftStickController.Hide();
|
||||||
_rightStickController.Hide();
|
_rightStickController.Hide();
|
||||||
_deadZoneLeftBox.Hide();
|
_deadZoneLeftBox.Hide();
|
||||||
|
@ -202,7 +202,7 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
else if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("controller"))
|
else if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("controller"))
|
||||||
{
|
{
|
||||||
this.ShowAll();
|
ShowAll();
|
||||||
_leftStickKeyboard.Hide();
|
_leftStickKeyboard.Hide();
|
||||||
_rightStickKeyboard.Hide();
|
_rightStickKeyboard.Hide();
|
||||||
}
|
}
|
||||||
|
@ -249,21 +249,13 @@ namespace Ryujinx.Ui
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_controllerType.ActiveId)
|
_controllerImage.Pixbuf = _controllerType.ActiveId switch
|
||||||
{
|
{
|
||||||
case "ProController":
|
"ProController" => new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Controller_ProCon.svg", 400, 400),
|
||||||
_controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.ProCon.svg", 400, 400);
|
"JoyconLeft" => new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Controller_JoyConLeft.svg", 400, 400),
|
||||||
break;
|
"JoyconRight" => new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Controller_JoyConRight.svg", 400, 400),
|
||||||
case "JoyconLeft":
|
_ => new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Controller_JoyConPair.svg", 400, 400),
|
||||||
_controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConLeft.svg", 400, 400);
|
};
|
||||||
break;
|
|
||||||
case "JoyconRight":
|
|
||||||
_controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConRight.svg", 400, 400);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
_controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConPair.svg", 400, 400);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearValues()
|
private void ClearValues()
|
||||||
|
@ -620,8 +612,7 @@ namespace Ryujinx.Ui
|
||||||
if (joystickState.IsButtonDown(i))
|
if (joystickState.IsButtonDown(i))
|
||||||
{
|
{
|
||||||
Enum.TryParse($"Button{i}", out pressedButton);
|
Enum.TryParse($"Button{i}", out pressedButton);
|
||||||
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,7 +665,9 @@ namespace Ryujinx.Ui
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Events
|
//
|
||||||
|
// Events
|
||||||
|
//
|
||||||
private void InputDevice_Changed(object sender, EventArgs args)
|
private void InputDevice_Changed(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
SetAvailableOptions();
|
SetAvailableOptions();
|
||||||
|
@ -692,7 +685,7 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
UpdateInputDeviceList();
|
UpdateInputDeviceList();
|
||||||
|
|
||||||
_refreshInputDevicesButton.SetStateFlags(0, true);
|
_refreshInputDevicesButton.SetStateFlags(StateFlags.Normal, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Button_Pressed(object sender, EventArgs args)
|
private void Button_Pressed(object sender, EventArgs args)
|
||||||
|
@ -719,7 +712,7 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
Application.Invoke(delegate
|
Application.Invoke(delegate
|
||||||
{
|
{
|
||||||
button.SetStateFlags(0, true);
|
button.SetStateFlags(StateFlags.Normal, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
_isWaitingForInput = false;
|
_isWaitingForInput = false;
|
||||||
|
@ -731,7 +724,7 @@ namespace Ryujinx.Ui
|
||||||
Application.Invoke(delegate
|
Application.Invoke(delegate
|
||||||
{
|
{
|
||||||
button.Label = pressedKey.ToString();
|
button.Label = pressedKey.ToString();
|
||||||
button.SetStateFlags(0, true);
|
button.SetStateFlags(StateFlags.Normal, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (_inputDevice.ActiveId.StartsWith("controller"))
|
else if (_inputDevice.ActiveId.StartsWith("controller"))
|
||||||
|
@ -745,7 +738,7 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
Application.Invoke(delegate
|
Application.Invoke(delegate
|
||||||
{
|
{
|
||||||
button.SetStateFlags(0, true);
|
button.SetStateFlags(StateFlags.Normal, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
_isWaitingForInput = false;
|
_isWaitingForInput = false;
|
||||||
|
@ -757,7 +750,7 @@ namespace Ryujinx.Ui
|
||||||
Application.Invoke(delegate
|
Application.Invoke(delegate
|
||||||
{
|
{
|
||||||
button.Label = pressedButton.ToString();
|
button.Label = pressedButton.ToString();
|
||||||
button.SetStateFlags(0, true);
|
button.SetStateFlags(StateFlags.Normal, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -788,7 +781,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void ProfileLoad_Activated(object sender, EventArgs args)
|
private void ProfileLoad_Activated(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
((ToggleButton)sender).SetStateFlags(0, true);
|
((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true);
|
||||||
|
|
||||||
if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == null) return;
|
if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == null) return;
|
||||||
|
|
||||||
|
@ -940,7 +933,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void ProfileAdd_Activated(object sender, EventArgs args)
|
private void ProfileAdd_Activated(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
((ToggleButton)sender).SetStateFlags(0, true);
|
((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true);
|
||||||
|
|
||||||
if (_inputDevice.ActiveId == "disabled") return;
|
if (_inputDevice.ActiveId == "disabled") return;
|
||||||
|
|
||||||
|
@ -973,7 +966,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void ProfileRemove_Activated(object sender, EventArgs args)
|
private void ProfileRemove_Activated(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
((ToggleButton) sender).SetStateFlags(0, true);
|
((ToggleButton) sender).SetStateFlags(StateFlags.Normal, true);
|
||||||
|
|
||||||
if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) return;
|
if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) return;
|
||||||
|
|
||||||
|
@ -1021,7 +1014,7 @@ namespace Ryujinx.Ui
|
||||||
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
|
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
|
||||||
ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
|
ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
|
||||||
|
|
||||||
MainWindow.SaveConfig();
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using LibHac;
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.FsSystem.NcaUtils;
|
using LibHac.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -16,7 +15,7 @@ using System.Text;
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
public class DlcWindow : Window
|
public class DlcWindow : Window
|
||||||
{
|
{
|
||||||
|
@ -31,9 +30,9 @@ namespace Ryujinx.Ui
|
||||||
[GUI] TreeSelection _dlcTreeSelection;
|
[GUI] TreeSelection _dlcTreeSelection;
|
||||||
#pragma warning restore CS0649, IDE0044
|
#pragma warning restore CS0649, IDE0044
|
||||||
|
|
||||||
public DlcWindow(string titleId, string titleName, VirtualFileSystem virtualFileSystem) : this(new Builder("Ryujinx.Ui.DlcWindow.glade"), titleId, titleName, virtualFileSystem) { }
|
public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.DlcWindow.glade"), virtualFileSystem, titleId, titleName) { }
|
||||||
|
|
||||||
private DlcWindow(Builder builder, string titleId, string titleName, VirtualFileSystem virtualFileSystem) : base(builder.GetObject("_dlcWindow").Handle)
|
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetObject("_dlcWindow").Handle)
|
||||||
{
|
{
|
||||||
builder.Autoconnect(this);
|
builder.Autoconnect(this);
|
||||||
|
|
||||||
|
@ -51,10 +50,7 @@ namespace Ryujinx.Ui
|
||||||
_dlcContainerList = new List<DlcContainer>();
|
_dlcContainerList = new List<DlcContainer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
_dlcTreeView.Model = new TreeStore(
|
_dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string));
|
||||||
typeof(bool),
|
|
||||||
typeof(string),
|
|
||||||
typeof(string));
|
|
||||||
|
|
||||||
CellRendererToggle enableToggle = new CellRendererToggle();
|
CellRendererToggle enableToggle = new CellRendererToggle();
|
||||||
enableToggle.Toggled += (sender, args) =>
|
enableToggle.Toggled += (sender, args) =>
|
||||||
|
@ -104,17 +100,9 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
return new Nca(_virtualFileSystem.KeySet, ncaStorage);
|
return new Nca(_virtualFileSystem.KeySet, ncaStorage);
|
||||||
}
|
}
|
||||||
catch (InvalidDataException exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"{exception.Message}. Errored File: {containerPath}");
|
GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {containerPath}");
|
||||||
|
|
||||||
GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add DLC Failed!", "The NCA header content type check has failed. This is usually because the header key is incorrect or missing.");
|
|
||||||
}
|
|
||||||
catch (MissingKeyException exception)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {containerPath}");
|
|
||||||
|
|
||||||
GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add DLC Failed!", $"Your key set is missing a key with the name: {exception.Name}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
|
@ -6,20 +6,20 @@ using Ryujinx.Configuration;
|
||||||
using Ryujinx.Configuration.System;
|
using Ryujinx.Configuration.System;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||||
|
using Ryujinx.Ui.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
public class SettingsWindow : Window
|
public class SettingsWindow : Window
|
||||||
{
|
{
|
||||||
private readonly VirtualFileSystem _virtualFileSystem;
|
private readonly MainWindow _parent;
|
||||||
private readonly ListStore _gameDirsBoxStore;
|
private readonly ListStore _gameDirsBoxStore;
|
||||||
private readonly ListStore _audioBackendStore;
|
private readonly ListStore _audioBackendStore;
|
||||||
private readonly TimeZoneContentManager _timeZoneContentManager;
|
private readonly TimeZoneContentManager _timeZoneContentManager;
|
||||||
|
@ -86,36 +86,34 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
#pragma warning restore CS0649, IDE0044
|
#pragma warning restore CS0649, IDE0044
|
||||||
|
|
||||||
public SettingsWindow(VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : this(new Builder("Ryujinx.Ui.SettingsWindow.glade"), virtualFileSystem, contentManager) { }
|
public SettingsWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : this(parent, new Builder("Ryujinx.Ui.Windows.SettingsWindow.glade"), virtualFileSystem, contentManager) { }
|
||||||
|
|
||||||
private SettingsWindow(Builder builder, VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : base(builder.GetObject("_settingsWin").Handle)
|
private SettingsWindow(MainWindow parent, Builder builder, VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : base(builder.GetObject("_settingsWin").Handle)
|
||||||
{
|
{
|
||||||
|
_parent = parent;
|
||||||
|
|
||||||
builder.Autoconnect(this);
|
builder.Autoconnect(this);
|
||||||
|
|
||||||
this.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
|
||||||
|
|
||||||
_virtualFileSystem = virtualFileSystem;
|
|
||||||
|
|
||||||
_timeZoneContentManager = new TimeZoneContentManager();
|
_timeZoneContentManager = new TimeZoneContentManager();
|
||||||
_timeZoneContentManager.InitializeInstance(virtualFileSystem, contentManager, LibHac.FsSystem.IntegrityCheckLevel.None);
|
_timeZoneContentManager.InitializeInstance(virtualFileSystem, contentManager, LibHac.FsSystem.IntegrityCheckLevel.None);
|
||||||
|
|
||||||
_validTzRegions = new HashSet<string>(_timeZoneContentManager.LocationNameCache.Length, StringComparer.Ordinal); // Zone regions are identifiers. Must match exactly.
|
_validTzRegions = new HashSet<string>(_timeZoneContentManager.LocationNameCache.Length, StringComparer.Ordinal); // Zone regions are identifiers. Must match exactly.
|
||||||
|
|
||||||
//Bind Events
|
// Bind Events.
|
||||||
_configureController1.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player1);
|
_configureController1.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player1);
|
||||||
_configureController2.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player2);
|
_configureController2.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player2);
|
||||||
_configureController3.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player3);
|
_configureController3.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player3);
|
||||||
_configureController4.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player4);
|
_configureController4.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player4);
|
||||||
_configureController5.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player5);
|
_configureController5.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player5);
|
||||||
_configureController6.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player6);
|
_configureController6.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player6);
|
||||||
_configureController7.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player7);
|
_configureController7.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player7);
|
||||||
_configureController8.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player8);
|
_configureController8.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player8);
|
||||||
_configureControllerH.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Handheld);
|
_configureControllerH.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Handheld);
|
||||||
_systemTimeZoneEntry.FocusOutEvent += TimeZoneEntry_FocusOut;
|
_systemTimeZoneEntry.FocusOutEvent += TimeZoneEntry_FocusOut;
|
||||||
|
|
||||||
_resScaleCombo.Changed += (sender, args) => _resScaleText.Visible = _resScaleCombo.ActiveId == "-1";
|
_resScaleCombo.Changed += (sender, args) => _resScaleText.Visible = _resScaleCombo.ActiveId == "-1";
|
||||||
|
|
||||||
//Setup Currents
|
// Setup Currents.
|
||||||
if (ConfigurationState.Instance.Logger.EnableFileLog)
|
if (ConfigurationState.Instance.Logger.EnableFileLog)
|
||||||
{
|
{
|
||||||
_fileLogToggle.Click();
|
_fileLogToggle.Click();
|
||||||
|
@ -419,12 +417,14 @@ namespace Ryujinx.Ui
|
||||||
ConfigurationState.Instance.System.AudioBackend.Value = (AudioBackend)_audioBackendStore.GetValue(activeIter, 1);
|
ConfigurationState.Instance.System.AudioBackend.Value = (AudioBackend)_audioBackendStore.GetValue(activeIter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow.SaveConfig();
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
MainWindow.UpdateGraphicsConfig();
|
_parent.UpdateGraphicsConfig();
|
||||||
MainWindow.ApplyTheme();
|
ThemeHelper.ApplyTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Events
|
//
|
||||||
|
// Events
|
||||||
|
//
|
||||||
private void TimeZoneEntry_FocusOut(object sender, FocusOutEventArgs e)
|
private void TimeZoneEntry_FocusOut(object sender, FocusOutEventArgs e)
|
||||||
{
|
{
|
||||||
if (!_validTzRegions.Contains(_systemTimeZoneEntry.Text))
|
if (!_validTzRegions.Contains(_systemTimeZoneEntry.Text))
|
||||||
|
@ -439,7 +439,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
return ((string)compl.Model.GetValue(iter, 1)).Contains(key, StringComparison.OrdinalIgnoreCase) || // region
|
return ((string)compl.Model.GetValue(iter, 1)).Contains(key, StringComparison.OrdinalIgnoreCase) || // region
|
||||||
((string)compl.Model.GetValue(iter, 2)).StartsWith(key, StringComparison.OrdinalIgnoreCase) || // abbr
|
((string)compl.Model.GetValue(iter, 2)).StartsWith(key, StringComparison.OrdinalIgnoreCase) || // abbr
|
||||||
((string)compl.Model.GetValue(iter, 0)).Substring(3).StartsWith(key); // offset
|
((string)compl.Model.GetValue(iter, 0))[3..].StartsWith(key); // offset
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SystemTimeSpin_ValueChanged(object sender, EventArgs e)
|
private void SystemTimeSpin_ValueChanged(object sender, EventArgs e)
|
||||||
|
@ -511,7 +511,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
_addGameDirBox.Buffer.Text = "";
|
_addGameDirBox.Buffer.Text = "";
|
||||||
|
|
||||||
((ToggleButton)sender).SetStateFlags(0, true);
|
((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveDir_Pressed(object sender, EventArgs args)
|
private void RemoveDir_Pressed(object sender, EventArgs args)
|
||||||
|
@ -523,7 +523,7 @@ namespace Ryujinx.Ui
|
||||||
_gameDirsBoxStore.Remove(ref treeIter);
|
_gameDirsBoxStore.Remove(ref treeIter);
|
||||||
}
|
}
|
||||||
|
|
||||||
((ToggleButton)sender).SetStateFlags(0, true);
|
((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CustThemeToggle_Activated(object sender, EventArgs args)
|
private void CustThemeToggle_Activated(object sender, EventArgs args)
|
||||||
|
@ -535,27 +535,25 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void BrowseThemeDir_Pressed(object sender, EventArgs args)
|
private void BrowseThemeDir_Pressed(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
FileChooserDialog fileChooser = new FileChooserDialog("Choose the theme to load", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Select", ResponseType.Accept);
|
using (FileChooserDialog fileChooser = new FileChooserDialog("Choose the theme to load", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Select", ResponseType.Accept))
|
||||||
|
|
||||||
fileChooser.Filter = new FileFilter();
|
|
||||||
fileChooser.Filter.AddPattern("*.css");
|
|
||||||
|
|
||||||
if (fileChooser.Run() == (int)ResponseType.Accept)
|
|
||||||
{
|
{
|
||||||
_custThemePath.Buffer.Text = fileChooser.Filename;
|
fileChooser.Filter = new FileFilter();
|
||||||
|
fileChooser.Filter.AddPattern("*.css");
|
||||||
|
|
||||||
|
if (fileChooser.Run() == (int)ResponseType.Accept)
|
||||||
|
{
|
||||||
|
_custThemePath.Buffer.Text = fileChooser.Filename;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileChooser.Dispose();
|
_browseThemePath.SetStateFlags(StateFlags.Normal, true);
|
||||||
|
|
||||||
_browseThemePath.SetStateFlags(0, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureController_Pressed(object sender, EventArgs args, PlayerIndex playerIndex)
|
private void ConfigureController_Pressed(object sender, PlayerIndex playerIndex)
|
||||||
{
|
{
|
||||||
((ToggleButton)sender).SetStateFlags(0, true);
|
((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true);
|
||||||
|
|
||||||
ControllerWindow controllerWin = new ControllerWindow(playerIndex, _virtualFileSystem);
|
new ControllerWindow(playerIndex).Show();
|
||||||
controllerWin.Show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveToggle_Activated(object sender, EventArgs args)
|
private void SaveToggle_Activated(object sender, EventArgs args)
|
||||||
|
@ -574,4 +572,4 @@ namespace Ryujinx.Ui
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using LibHac;
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
@ -7,9 +6,9 @@ using LibHac.FsSystem;
|
||||||
using LibHac.FsSystem.NcaUtils;
|
using LibHac.FsSystem.NcaUtils;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -19,16 +18,18 @@ using System.Text;
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
|
||||||
|
|
||||||
namespace Ryujinx.Ui
|
namespace Ryujinx.Ui.Windows
|
||||||
{
|
{
|
||||||
public class TitleUpdateWindow : Window
|
public class TitleUpdateWindow : Window
|
||||||
{
|
{
|
||||||
|
private readonly MainWindow _parent;
|
||||||
private readonly VirtualFileSystem _virtualFileSystem;
|
private readonly VirtualFileSystem _virtualFileSystem;
|
||||||
private readonly string _titleId;
|
private readonly string _titleId;
|
||||||
private readonly string _updateJsonPath;
|
private readonly string _updateJsonPath;
|
||||||
|
|
||||||
private TitleUpdateMetadata _titleUpdateWindowData;
|
private TitleUpdateMetadata _titleUpdateWindowData;
|
||||||
private Dictionary<RadioButton, string> _radioButtonToPathDictionary;
|
|
||||||
|
private readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary;
|
||||||
|
|
||||||
#pragma warning disable CS0649, IDE0044
|
#pragma warning disable CS0649, IDE0044
|
||||||
[GUI] Label _baseTitleInfoLabel;
|
[GUI] Label _baseTitleInfoLabel;
|
||||||
|
@ -36,10 +37,12 @@ namespace Ryujinx.Ui
|
||||||
[GUI] RadioButton _noUpdateRadioButton;
|
[GUI] RadioButton _noUpdateRadioButton;
|
||||||
#pragma warning restore CS0649, IDE0044
|
#pragma warning restore CS0649, IDE0044
|
||||||
|
|
||||||
public TitleUpdateWindow(string titleId, string titleName, VirtualFileSystem virtualFileSystem) : this(new Builder("Ryujinx.Ui.TitleUpdateWindow.glade"), titleId, titleName, virtualFileSystem) { }
|
public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, titleId, titleName) { }
|
||||||
|
|
||||||
private TitleUpdateWindow(Builder builder, string titleId, string titleName, VirtualFileSystem virtualFileSystem) : base(builder.GetObject("_titleUpdateWindow").Handle)
|
private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetObject("_titleUpdateWindow").Handle)
|
||||||
{
|
{
|
||||||
|
_parent = parent;
|
||||||
|
|
||||||
builder.Autoconnect(this);
|
builder.Autoconnect(this);
|
||||||
|
|
||||||
_titleId = titleId;
|
_titleId = titleId;
|
||||||
|
@ -61,20 +64,26 @@ namespace Ryujinx.Ui
|
||||||
}
|
}
|
||||||
|
|
||||||
_baseTitleInfoLabel.Text = $"Updates Available for {titleName} [{titleId.ToUpper()}]";
|
_baseTitleInfoLabel.Text = $"Updates Available for {titleName} [{titleId.ToUpper()}]";
|
||||||
_noUpdateRadioButton.Active = true;
|
|
||||||
|
|
||||||
foreach (string path in _titleUpdateWindowData.Paths)
|
foreach (string path in _titleUpdateWindowData.Paths)
|
||||||
{
|
{
|
||||||
AddUpdate(path, false);
|
AddUpdate(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ((RadioButton update, var _) in _radioButtonToPathDictionary.Where(keyValuePair => keyValuePair.Value == _titleUpdateWindowData.Selected))
|
if (_titleUpdateWindowData.Selected == "")
|
||||||
{
|
{
|
||||||
update.Active = true;
|
_noUpdateRadioButton.Active = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ((RadioButton update, var _) in _radioButtonToPathDictionary.Where(keyValuePair => keyValuePair.Value == _titleUpdateWindowData.Selected))
|
||||||
|
{
|
||||||
|
update.Active = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddUpdate(string path, bool showErrorDialog = true)
|
private void AddUpdate(string path)
|
||||||
{
|
{
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
|
@ -107,23 +116,9 @@ namespace Ryujinx.Ui
|
||||||
GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!");
|
GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InvalidDataException exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"{exception.Message}. Errored File: {path}");
|
GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}");
|
||||||
|
|
||||||
if (showErrorDialog)
|
|
||||||
{
|
|
||||||
GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add Update Failed!", "The NCA header content type check has failed. This is usually because the header key is incorrect or missing.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (MissingKeyException exception)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {path}");
|
|
||||||
|
|
||||||
if (showErrorDialog)
|
|
||||||
{
|
|
||||||
GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add Update Failed!", $"Your key set is missing a key with the name: {exception.Name}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,23 +139,21 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
private void AddButton_Clicked(object sender, EventArgs args)
|
private void AddButton_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
FileChooserDialog fileChooser = new FileChooserDialog("Select update files", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Add", ResponseType.Accept)
|
using (FileChooserDialog fileChooser = new FileChooserDialog("Select update files", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Add", ResponseType.Accept))
|
||||||
{
|
{
|
||||||
SelectMultiple = true,
|
fileChooser.SelectMultiple = true;
|
||||||
Filter = new FileFilter()
|
fileChooser.SetPosition(WindowPosition.Center);
|
||||||
};
|
fileChooser.Filter = new FileFilter();
|
||||||
fileChooser.SetPosition(WindowPosition.Center);
|
fileChooser.Filter.AddPattern("*.nsp");
|
||||||
fileChooser.Filter.AddPattern("*.nsp");
|
|
||||||
|
|
||||||
if (fileChooser.Run() == (int)ResponseType.Accept)
|
if (fileChooser.Run() == (int)ResponseType.Accept)
|
||||||
{
|
|
||||||
foreach (string path in fileChooser.Filenames)
|
|
||||||
{
|
{
|
||||||
AddUpdate(path);
|
foreach (string path in fileChooser.Filenames)
|
||||||
|
{
|
||||||
|
AddUpdate(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileChooser.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveButton_Clicked(object sender, EventArgs args)
|
private void RemoveButton_Clicked(object sender, EventArgs args)
|
||||||
|
@ -196,7 +189,8 @@ namespace Ryujinx.Ui
|
||||||
dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow.UpdateGameTable();
|
_parent.UpdateGameTable();
|
||||||
|
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|