[Ryujinx] Address dotnet-format issues (#5395)

* dotnet format style --severity info

Some changes were manually reverted.

* dotnet format analyzers --serverity info

Some changes have been minimally adapted.

* Restore a few unused methods and variables

* Address dotnet format CA1816 warnings

* Address or silence dotnet format CA2208 warnings

* Address or silence dotnet format CA1806 and a few CA1854 warnings

* Address dotnet format CA1822 warnings

* Make dotnet format succeed in style mode

* Address dotnet format CA2208 warnings properly

* Address most dotnet format whitespace warnings

* Apply dotnet format whitespace formatting

A few of them have been manually reverted and the corresponding warning was silenced

* Format if-blocks correctly

* Another rebase, another dotnet format run

* Run dotnet format whitespace after rebase

* Run dotnet format after rebase and remove unused usings

- analyzers
- style
- whitespace

* Add comments to disabled warnings

* Simplify properties and array initialization, Use const when possible, Remove trailing commas

* Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas"

This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e.

* dotnet format whitespace after rebase

* First dotnet format pass

* Fix build issues

* Apply suggestions from code review

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Second dotnet format pass

* Update src/Ryujinx/Modules/Updater/Updater.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Add trailing commas and improve formatting

* Fix formatting and naming issues

* Rename nvStutterWorkaround to nvidiaStutterWorkaround

* Use using declarations and extend resource lifetimes

* Fix GTK issues

* Add formatting for generated files

* Add trailing commas

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
This commit is contained in:
TSRBerry 2023-07-02 00:25:07 +02:00 committed by GitHub
parent 02b5c7ea89
commit 0684b00b3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1891 additions and 1830 deletions

View file

@ -25,7 +25,7 @@ namespace Ryujinx.Input.GTK3
private readonly GTK3KeyboardDriver _driver; private readonly GTK3KeyboardDriver _driver;
private StandardKeyboardInputConfig _configuration; private StandardKeyboardInputConfig _configuration;
private List<ButtonMappingEntry> _buttonsUserMapping; private readonly List<ButtonMappingEntry> _buttonsUserMapping;
public GTK3Keyboard(GTK3KeyboardDriver driver, string id, string name) public GTK3Keyboard(GTK3KeyboardDriver driver, string id, string name)
{ {
@ -48,6 +48,7 @@ namespace Ryujinx.Input.GTK3
public void Dispose() public void Dispose()
{ {
// No operations // No operations
GC.SuppressFinalize(this);
} }
public KeyboardStateSnapshot GetKeyboardStateSnapshot() public KeyboardStateSnapshot GetKeyboardStateSnapshot()
@ -87,7 +88,7 @@ namespace Ryujinx.Input.GTK3
stickX -= 1; stickX -= 1;
} }
OpenTK.Mathematics.Vector2 stick = new OpenTK.Mathematics.Vector2(stickX, stickY); OpenTK.Mathematics.Vector2 stick = new(stickX, stickY);
stick.NormalizeFast(); stick.NormalizeFast();

View file

@ -9,7 +9,7 @@ namespace Ryujinx.Input.GTK3
public class GTK3KeyboardDriver : IGamepadDriver public class GTK3KeyboardDriver : IGamepadDriver
{ {
private readonly Widget _widget; private readonly Widget _widget;
private HashSet<GtkKey> _pressedKeys; private readonly HashSet<GtkKey> _pressedKeys;
public GTK3KeyboardDriver(Widget widget) public GTK3KeyboardDriver(Widget widget)
{ {
@ -28,13 +28,13 @@ namespace Ryujinx.Input.GTK3
public event Action<string> OnGamepadConnected public event Action<string> OnGamepadConnected
{ {
add { } add { }
remove { } remove { }
} }
public event Action<string> OnGamepadDisconnected public event Action<string> OnGamepadDisconnected
{ {
add { } add { }
remove { } remove { }
} }
@ -49,6 +49,7 @@ namespace Ryujinx.Input.GTK3
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this);
Dispose(true); Dispose(true);
} }

View file

@ -83,6 +83,7 @@ namespace Ryujinx.Input.GTK3
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this);
_driver = null; _driver = null;
} }
} }

View file

@ -14,16 +14,16 @@ namespace Ryujinx.Input.GTK3
public bool[] PressedButtons { get; } public bool[] PressedButtons { get; }
public Vector2 CurrentPosition { get; private set; } public Vector2 CurrentPosition { get; private set; }
public Vector2 Scroll{ get; private set; } public Vector2 Scroll { get; private set; }
public GTK3MouseDriver(Widget parent) public GTK3MouseDriver(Widget parent)
{ {
_widget = parent; _widget = parent;
_widget.MotionNotifyEvent += Parent_MotionNotifyEvent; _widget.MotionNotifyEvent += Parent_MotionNotifyEvent;
_widget.ButtonPressEvent += Parent_ButtonPressEvent; _widget.ButtonPressEvent += Parent_ButtonPressEvent;
_widget.ButtonReleaseEvent += Parent_ButtonReleaseEvent; _widget.ButtonReleaseEvent += Parent_ButtonReleaseEvent;
_widget.ScrollEvent += Parent_ScrollEvent; _widget.ScrollEvent += Parent_ScrollEvent;
PressedButtons = new bool[(int)MouseButton.Count]; PressedButtons = new bool[(int)MouseButton.Count];
} }
@ -58,7 +58,7 @@ namespace Ryujinx.Input.GTK3
public bool IsButtonPressed(MouseButton button) public bool IsButtonPressed(MouseButton button)
{ {
return PressedButtons[(int) button]; return PressedButtons[(int)button];
} }
public Size GetClientSize() public Size GetClientSize()
@ -70,17 +70,17 @@ namespace Ryujinx.Input.GTK3
public event Action<string> OnGamepadConnected public event Action<string> OnGamepadConnected
{ {
add { } add { }
remove { } remove { }
} }
public event Action<string> OnGamepadDisconnected public event Action<string> OnGamepadDisconnected
{ {
add { } add { }
remove { } remove { }
} }
public ReadOnlySpan<string> GamepadsIds => new[] {"0"}; public ReadOnlySpan<string> GamepadsIds => new[] { "0" };
public IGamepad GetGamepad(string id) public IGamepad GetGamepad(string id)
{ {
@ -94,6 +94,8 @@ namespace Ryujinx.Input.GTK3
return; return;
} }
GC.SuppressFinalize(this);
_isDisposed = true; _isDisposed = true;
_widget.MotionNotifyEvent -= Parent_MotionNotifyEvent; _widget.MotionNotifyEvent -= Parent_MotionNotifyEvent;

View file

@ -12,17 +12,17 @@ namespace Ryujinx.Modules
{ {
public class UpdateDialog : Gtk.Window public class UpdateDialog : Gtk.Window
{ {
#pragma warning disable CS0649, IDE0044 #pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier
[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.Modules.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) { }
@ -31,16 +31,16 @@ namespace Ryujinx.Modules
builder.Autoconnect(this); builder.Autoconnect(this);
_mainWindow = mainWindow; _mainWindow = mainWindow;
_buildUrl = buildUrl; _buildUrl = buildUrl;
Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
MainText.Text = "Do you want to update Ryujinx to the latest version?"; MainText.Text = "Do you want to update Ryujinx to the latest version?";
SecondaryText.Text = $"{Program.Version} -> {newVersion}"; SecondaryText.Text = $"{Program.Version} -> {newVersion}";
ProgressBar.Hide(); ProgressBar.Hide();
YesButton.Clicked += YesButton_Clicked; YesButton.Clicked += YesButton_Clicked;
NoButton.Clicked += NoButton_Clicked; NoButton.Clicked += NoButton_Clicked;
} }
private void YesButton_Clicked(object sender, EventArgs args) private void YesButton_Clicked(object sender, EventArgs args)
@ -52,7 +52,7 @@ namespace Ryujinx.Modules
ProcessStartInfo processStart = new(ryuName) ProcessStartInfo processStart = new(ryuName)
{ {
UseShellExecute = true, UseShellExecute = true,
WorkingDirectory = ReleaseInformation.GetBaseApplicationDirectory() WorkingDirectory = ReleaseInformation.GetBaseApplicationDirectory(),
}; };
foreach (string argument in CommandLineState.Arguments) foreach (string argument in CommandLineState.Arguments)
@ -74,7 +74,7 @@ namespace Ryujinx.Modules
ProgressBar.Show(); ProgressBar.Show();
SecondaryText.Text = ""; SecondaryText.Text = "";
_restartQuery = true; _restartQuery = true;
Updater.UpdateRyujinx(this, _buildUrl); Updater.UpdateRyujinx(this, _buildUrl);
} }
@ -85,7 +85,7 @@ namespace Ryujinx.Modules
Updater.Running = false; Updater.Running = false;
_mainWindow.Window.Functions = WMFunction.All; _mainWindow.Window.Functions = WMFunction.All;
_mainWindow.ExitMenuItem.Sensitive = true; _mainWindow.ExitMenuItem.Sensitive = true;
_mainWindow.UpdateMenuItem.Sensitive = true; _mainWindow.UpdateMenuItem.Sensitive = true;
Dispose(); Dispose();

View file

@ -24,28 +24,28 @@ namespace Ryujinx.Modules
{ {
public static class Updater public static class Updater
{ {
private const string GitHubApiURL = "https://api.github.com"; private const string GitHubApiUrl = "https://api.github.com";
private const int ConnectionCount = 4; private const int ConnectionCount = 4;
internal static bool Running; internal static bool Running;
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
private static string _buildVer; private static string _buildVer;
private static string _platformExt; private static string _platformExt;
private static string _buildUrl; private static string _buildUrl;
private static long _buildSize; private static long _buildSize;
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
// On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates.
private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; private static readonly string[] _windowsDependencyDirs = { "bin", "etc", "lib", "share" };
private static HttpClient ConstructHttpClient() private static HttpClient ConstructHttpClient()
{ {
HttpClient result = new HttpClient(); HttpClient result = new();
// Required by GitHub to interact with APIs. // Required by GitHub to interact with APIs.
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
@ -55,7 +55,10 @@ namespace Ryujinx.Modules
public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate)
{ {
if (Running) return; if (Running)
{
return;
}
Running = true; Running = true;
mainWindow.UpdateMenuItem.Sensitive = false; mainWindow.UpdateMenuItem.Sensitive = false;
@ -65,17 +68,17 @@ namespace Ryujinx.Modules
// Detect current platform // Detect current platform
if (OperatingSystem.IsMacOS()) if (OperatingSystem.IsMacOS())
{ {
_platformExt = "osx_x64.zip"; _platformExt = "osx_x64.zip";
artifactIndex = 1; artifactIndex = 1;
} }
else if (OperatingSystem.IsWindows()) else if (OperatingSystem.IsWindows())
{ {
_platformExt = "win_x64.zip"; _platformExt = "win_x64.zip";
artifactIndex = 2; artifactIndex = 2;
} }
else if (OperatingSystem.IsLinux()) else if (OperatingSystem.IsLinux())
{ {
_platformExt = "linux_x64.tar.gz"; _platformExt = "linux_x64.tar.gz";
artifactIndex = 0; artifactIndex = 0;
} }
@ -105,11 +108,11 @@ namespace Ryujinx.Modules
try try
{ {
using HttpClient jsonClient = ConstructHttpClient(); using HttpClient jsonClient = ConstructHttpClient();
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
// Fetch latest build information // Fetch latest build information
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl);
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse);
_buildVer = fetched.Name; _buildVer = fetched.Name;
foreach (var asset in fetched.Assets) foreach (var asset in fetched.Assets)
@ -176,45 +179,43 @@ namespace Ryujinx.Modules
} }
// Fetch build size information to learn chunk sizes. // Fetch build size information to learn chunk sizes.
using (HttpClient buildSizeClient = ConstructHttpClient()) using HttpClient buildSizeClient = ConstructHttpClient();
try
{ {
try buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
{
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
_buildSize = message.Content.Headers.ContentRange.Length.Value; _buildSize = message.Content.Headers.ContentRange.Length.Value;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, ex.Message);
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
_buildSize = -1; _buildSize = -1;
}
} }
// Show a message asking the user if they want to update // Show a message asking the user if they want to update
UpdateDialog updateDialog = new UpdateDialog(mainWindow, newVersion, _buildUrl); UpdateDialog updateDialog = new(mainWindow, newVersion, _buildUrl);
updateDialog.Show(); updateDialog.Show();
} }
public static void UpdateRyujinx(UpdateDialog updateDialog, string downloadUrl) public static void UpdateRyujinx(UpdateDialog updateDialog, string downloadUrl)
{ {
// Empty update dir, although it shouldn't ever have anything inside it // Empty update dir, although it shouldn't ever have anything inside it
if (Directory.Exists(UpdateDir)) if (Directory.Exists(_updateDir))
{ {
Directory.Delete(UpdateDir, true); Directory.Delete(_updateDir, true);
} }
Directory.CreateDirectory(UpdateDir); Directory.CreateDirectory(_updateDir);
string updateFile = Path.Combine(UpdateDir, "update.bin"); string updateFile = Path.Combine(_updateDir, "update.bin");
// Download the update .zip // Download the update .zip
updateDialog.MainText.Text = "Downloading Update..."; updateDialog.MainText.Text = "Downloading Update...";
updateDialog.ProgressBar.Value = 0; updateDialog.ProgressBar.Value = 0;
updateDialog.ProgressBar.MaxValue = 100; updateDialog.ProgressBar.MaxValue = 100;
if (_buildSize >= 0) if (_buildSize >= 0)
@ -237,8 +238,8 @@ namespace Ryujinx.Modules
int totalProgressPercentage = 0; int totalProgressPercentage = 0;
int[] progressPercentage = new int[ConnectionCount]; int[] progressPercentage = new int[ConnectionCount];
List<byte[]> list = new List<byte[]>(ConnectionCount); List<byte[]> list = new(ConnectionCount);
List<WebClient> webClients = new List<WebClient>(ConnectionCount); List<WebClient> webClients = new(ConnectionCount);
for (int i = 0; i < ConnectionCount; i++) for (int i = 0; i < ConnectionCount; i++)
{ {
@ -249,7 +250,7 @@ namespace Ryujinx.Modules
{ {
#pragma warning disable SYSLIB0014 #pragma warning disable SYSLIB0014
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
using WebClient client = new WebClient(); using WebClient client = new();
#pragma warning restore SYSLIB0014 #pragma warning restore SYSLIB0014
webClients.Add(client); webClients.Add(client);
@ -337,35 +338,32 @@ namespace Ryujinx.Modules
private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile) private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile)
{ {
using HttpClient client = new HttpClient(); using HttpClient client = new();
// We do not want to timeout while downloading // We do not want to timeout while downloading
client.Timeout = TimeSpan.FromDays(1); client.Timeout = TimeSpan.FromDays(1);
using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result;
using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result;
using Stream updateFileStream = File.Open(updateFile, FileMode.Create);
long totalBytes = response.Content.Headers.ContentLength.Value;
long byteWritten = 0;
byte[] buffer = new byte[32 * 1024];
while (true)
{ {
using (Stream updateFileStream = File.Open(updateFile, FileMode.Create)) int readSize = remoteFileStream.Read(buffer);
if (readSize == 0)
{ {
long totalBytes = response.Content.Headers.ContentLength.Value; break;
long byteWritten = 0;
byte[] buffer = new byte[32 * 1024];
while (true)
{
int readSize = remoteFileStream.Read(buffer);
if (readSize == 0)
{
break;
}
byteWritten += readSize;
updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100;
updateFileStream.Write(buffer, 0, readSize);
}
} }
byteWritten += readSize;
updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100;
updateFileStream.Write(buffer, 0, readSize);
} }
InstallUpdate(updateDialog, updateFile); InstallUpdate(updateDialog, updateFile);
@ -373,9 +371,9 @@ namespace Ryujinx.Modules
private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile)
{ {
Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)) Thread worker = new(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile))
{ {
Name = "Updater.SingleThreadWorker" Name = "Updater.SingleThreadWorker",
}; };
worker.Start(); worker.Start();
} }
@ -383,14 +381,14 @@ namespace Ryujinx.Modules
private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile) private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile)
{ {
// Extract Update // Extract Update
updateDialog.MainText.Text = "Extracting Update..."; updateDialog.MainText.Text = "Extracting Update...";
updateDialog.ProgressBar.Value = 0; updateDialog.ProgressBar.Value = 0;
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())
{ {
using Stream inStream = File.OpenRead(updateFile); using Stream inStream = File.OpenRead(updateFile);
using Stream gzipStream = new GZipInputStream(inStream); using Stream gzipStream = new GZipInputStream(inStream);
using TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII); using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
updateDialog.ProgressBar.MaxValue = inStream.Length; updateDialog.ProgressBar.MaxValue = inStream.Length;
await Task.Run(() => await Task.Run(() =>
@ -401,16 +399,17 @@ namespace Ryujinx.Modules
{ {
while ((tarEntry = tarStream.GetNextEntry()) != null) while ((tarEntry = tarStream.GetNextEntry()) != null)
{ {
if (tarEntry.IsDirectory) continue; if (tarEntry.IsDirectory)
{
continue;
}
string outPath = Path.Combine(UpdateDir, tarEntry.Name); string outPath = Path.Combine(_updateDir, tarEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath)); Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (FileStream outStream = File.OpenWrite(outPath)) using FileStream outStream = File.OpenWrite(outPath);
{ tarStream.CopyEntryContents(outStream);
tarStream.CopyEntryContents(outStream);
}
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
@ -429,25 +428,26 @@ namespace Ryujinx.Modules
} }
else else
{ {
using Stream inStream = File.OpenRead(updateFile); using Stream inStream = File.OpenRead(updateFile);
using ZipFile zipFile = new ZipFile(inStream); using ZipFile zipFile = new(inStream);
updateDialog.ProgressBar.MaxValue = zipFile.Count; updateDialog.ProgressBar.MaxValue = zipFile.Count;
await Task.Run(() => await Task.Run(() =>
{ {
foreach (ZipEntry zipEntry in zipFile) foreach (ZipEntry zipEntry in zipFile)
{ {
if (zipEntry.IsDirectory) continue; if (zipEntry.IsDirectory)
{
continue;
}
string outPath = Path.Combine(UpdateDir, zipEntry.Name); string outPath = Path.Combine(_updateDir, zipEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath)); Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (Stream zipStream = zipFile.GetInputStream(zipEntry)) using Stream zipStream = zipFile.GetInputStream(zipEntry);
using (FileStream outStream = File.OpenWrite(outPath)) using FileStream outStream = File.OpenWrite(outPath);
{ zipStream.CopyTo(outStream);
zipStream.CopyTo(outStream);
}
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
@ -464,8 +464,8 @@ namespace Ryujinx.Modules
List<string> allFiles = EnumerateFilesToDelete().ToList(); List<string> allFiles = EnumerateFilesToDelete().ToList();
updateDialog.MainText.Text = "Renaming Old Files..."; updateDialog.MainText.Text = "Renaming Old Files...";
updateDialog.ProgressBar.Value = 0; updateDialog.ProgressBar.Value = 0;
updateDialog.ProgressBar.MaxValue = allFiles.Count; updateDialog.ProgressBar.MaxValue = allFiles.Count;
// Replace old files // Replace old files
@ -490,19 +490,19 @@ namespace Ryujinx.Modules
Application.Invoke(delegate Application.Invoke(delegate
{ {
updateDialog.MainText.Text = "Adding New Files..."; updateDialog.MainText.Text = "Adding New Files...";
updateDialog.ProgressBar.Value = 0; updateDialog.ProgressBar.Value = 0;
updateDialog.ProgressBar.MaxValue = Directory.GetFiles(UpdatePublishDir, "*", SearchOption.AllDirectories).Length; updateDialog.ProgressBar.MaxValue = Directory.GetFiles(_updatePublishDir, "*", SearchOption.AllDirectories).Length;
}); });
MoveAllFilesOver(UpdatePublishDir, HomeDir, updateDialog); MoveAllFilesOver(_updatePublishDir, _homeDir, updateDialog);
}); });
Directory.Delete(UpdateDir, true); Directory.Delete(_updateDir, true);
updateDialog.MainText.Text = "Update Complete!"; updateDialog.MainText.Text = "Update Complete!";
updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?"; updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?";
updateDialog.Modal = true; updateDialog.Modal = true;
updateDialog.ProgressBar.Hide(); updateDialog.ProgressBar.Hide();
updateDialog.YesButton.Show(); updateDialog.YesButton.Show();
@ -563,15 +563,15 @@ namespace Ryujinx.Modules
// NOTE: This method should always reflect the latest build layout. // NOTE: This method should always reflect the latest build layout.
private static IEnumerable<string> EnumerateFilesToDelete() private static IEnumerable<string> EnumerateFilesToDelete()
{ {
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir.
// Determine and exclude user files only when the updater is running, not when cleaning old files // Determine and exclude user files only when the updater is running, not when cleaning old files
if (Running) if (Running)
{ {
// Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list.
var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename)); var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename));
// Remove user files from the paths in files. // Remove user files from the paths in files.
files = files.Except(userFiles); files = files.Except(userFiles);
@ -579,9 +579,9 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
foreach (string dir in WindowsDependencyDirs) foreach (string dir in _windowsDependencyDirs)
{ {
string dirPath = Path.Combine(HomeDir, dir); string dirPath = Path.Combine(_homeDir, dir);
if (Directory.Exists(dirPath)) if (Directory.Exists(dirPath))
{ {
files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories)); files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories));

View file

@ -43,10 +43,7 @@ namespace Ryujinx
[LibraryImport("libc", SetLastError = true)] [LibraryImport("libc", SetLastError = true)]
private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite); private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
[LibraryImport("libc")] private const uint MbIconWarning = 0x30;
private static partial IntPtr getenv([MarshalAs(UnmanagedType.LPStr)] string name);
private const uint MB_ICONWARNING = 0x30;
static Program() static Program()
{ {
@ -78,16 +75,16 @@ namespace Ryujinx
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
{ {
MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING); MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning);
} }
// Parse arguments // Parse arguments
CommandLineState.ParseArguments(args); CommandLineState.ParseArguments(args);
// 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) => Exit(); AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit();
// Make process DPI aware for proper window sizing on high-res screens. // Make process DPI aware for proper window sizing on high-res screens.
ForceDpiAware.Windows(); ForceDpiAware.Windows();
@ -102,7 +99,11 @@ namespace Ryujinx
// This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads). // This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads).
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())
{ {
XInitThreads(); if (XInitThreads() == 0)
{
throw new NotSupportedException("Failed to initialize multi-threading support.");
}
Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); Environment.SetEnvironmentVariable("GDK_BACKEND", "x11");
setenv("GDK_BACKEND", "x11", 1); setenv("GDK_BACKEND", "x11", 1);
} }
@ -121,7 +122,7 @@ namespace Ryujinx
resourcesDataDir = baseDirectory; resourcesDataDir = baseDirectory;
} }
void SetEnvironmentVariableNoCaching(string key, string value) static void SetEnvironmentVariableNoCaching(string key, string value)
{ {
int res = setenv(key, value, 1); int res = setenv(key, value, 1);
Debug.Assert(res != -1); Debug.Assert(res != -1);
@ -163,11 +164,11 @@ namespace Ryujinx
// Sets ImageSharp Jpeg Encoder Quality. // Sets ImageSharp Jpeg Encoder Quality.
SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder() SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()
{ {
Quality = 100 Quality = 100,
}); });
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
ConfigurationPath = File.Exists(localConfigurationPath) ConfigurationPath = File.Exists(localConfigurationPath)
@ -232,7 +233,7 @@ namespace Ryujinx
"never" => HideCursorMode.Never, "never" => HideCursorMode.Never,
"onidle" => HideCursorMode.OnIdle, "onidle" => HideCursorMode.OnIdle,
"always" => HideCursorMode.Always, "always" => HideCursorMode.Always,
_ => ConfigurationState.Instance.HideCursor.Value _ => ConfigurationState.Instance.HideCursor.Value,
}; };
} }
@ -261,7 +262,7 @@ namespace Ryujinx
} }
// Show the main window UI. // Show the main window UI.
MainWindow mainWindow = new MainWindow(); MainWindow mainWindow = new();
mainWindow.Show(); mainWindow.Show();
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())
@ -278,7 +279,7 @@ namespace Ryujinx
{ {
{ 0, "Yes, until the next restart" }, { 0, "Yes, until the next restart" },
{ 1, "Yes, permanently" }, { 1, "Yes, permanently" },
{ 2, "No" } { 2, "No" },
}; };
ResponseType response = GtkDialog.CreateCustomDialog( ResponseType response = GtkDialog.CreateCustomDialog(
@ -347,7 +348,7 @@ namespace Ryujinx
var buttonTexts = new Dictionary<int, string>() var buttonTexts = new Dictionary<int, string>()
{ {
{ 0, "Yes (Vulkan)" }, { 0, "Yes (Vulkan)" },
{ 1, "No (OpenGL)" } { 1, "No (OpenGL)" },
}; };
ResponseType response = GtkDialog.CreateCustomDialog( ResponseType response = GtkDialog.CreateCustomDialog(

View file

@ -11,15 +11,15 @@ namespace Ryujinx.Ui.Applet
/// </summary> /// </summary>
internal class GtkDynamicTextInputHandler : IDynamicTextInputHandler internal class GtkDynamicTextInputHandler : IDynamicTextInputHandler
{ {
private readonly Window _parent; private readonly Window _parent;
private readonly OffscreenWindow _inputToTextWindow = new OffscreenWindow(); private readonly OffscreenWindow _inputToTextWindow = new();
private readonly RawInputToTextEntry _inputToTextEntry = new RawInputToTextEntry(); private readonly RawInputToTextEntry _inputToTextEntry = new();
private bool _canProcessInput; private bool _canProcessInput;
public event DynamicTextChangedHandler TextChangedEvent; public event DynamicTextChangedHandler TextChangedEvent;
public event KeyPressedHandler KeyPressedEvent; public event KeyPressedHandler KeyPressedEvent;
public event KeyReleasedHandler KeyReleasedEvent; public event KeyReleasedHandler KeyReleasedEvent;
public bool TextProcessingEnabled public bool TextProcessingEnabled
{ {
@ -37,7 +37,7 @@ namespace Ryujinx.Ui.Applet
public GtkDynamicTextInputHandler(Window parent) public GtkDynamicTextInputHandler(Window parent)
{ {
_parent = parent; _parent = parent;
_parent.KeyPressEvent += HandleKeyPressEvent; _parent.KeyPressEvent += HandleKeyPressEvent;
_parent.KeyReleaseEvent += HandleKeyReleaseEvent; _parent.KeyReleaseEvent += HandleKeyReleaseEvent;
_inputToTextWindow.Add(_inputToTextEntry); _inputToTextWindow.Add(_inputToTextEntry);
@ -101,7 +101,7 @@ namespace Ryujinx.Ui.Applet
public void Dispose() public void Dispose()
{ {
_parent.KeyPressEvent -= HandleKeyPressEvent; _parent.KeyPressEvent -= HandleKeyPressEvent;
_parent.KeyReleaseEvent -= HandleKeyReleaseEvent; _parent.KeyReleaseEvent -= HandleKeyReleaseEvent;
} }
} }

View file

@ -5,7 +5,6 @@ using Ryujinx.HLE.Ui;
using Ryujinx.Ui.Widgets; using Ryujinx.Ui.Widgets;
using System; using System;
using System.Threading; using System.Threading;
using Action = System.Action;
namespace Ryujinx.Ui.Applet namespace Ryujinx.Ui.Applet
{ {
@ -37,7 +36,7 @@ namespace Ryujinx.Ui.Applet
public bool DisplayMessageDialog(string title, string message) public bool DisplayMessageDialog(string title, string message)
{ {
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false); ManualResetEvent dialogCloseEvent = new(false);
bool okPressed = false; bool okPressed = false;
@ -49,9 +48,9 @@ namespace Ryujinx.Ui.Applet
{ {
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,
}; };
msgDialog.SetDefaultSize(400, 0); msgDialog.SetDefaultSize(400, 0);
@ -84,10 +83,10 @@ namespace Ryujinx.Ui.Applet
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText) public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
{ {
ManualResetEvent dialogCloseEvent = new ManualResetEvent(false); ManualResetEvent dialogCloseEvent = new(false);
bool okPressed = false; bool okPressed = false;
bool error = false; bool error = false;
string inputText = args.InitialText ?? ""; string inputText = args.InitialText ?? "";
Application.Invoke(delegate Application.Invoke(delegate
@ -96,14 +95,14 @@ namespace Ryujinx.Ui.Applet
{ {
var swkbdDialog = new SwkbdAppletDialog(_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);
swkbdDialog.SetInputValidation(args.KeyboardMode); swkbdDialog.SetInputValidation(args.KeyboardMode);
@ -143,7 +142,7 @@ namespace Ryujinx.Ui.Applet
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(false);
bool showDetails = false; bool showDetails = false;
@ -151,12 +150,12 @@ namespace Ryujinx.Ui.Applet
{ {
try try
{ {
ErrorAppletDialog msgDialog = new ErrorAppletDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Error, buttons) ErrorAppletDialog msgDialog = new(_parent, DialogFlags.DestroyWithParent, MessageType.Error, buttons)
{ {
Title = title, Title = title,
Text = message, Text = message,
UseMarkup = true, UseMarkup = true,
WindowPosition = WindowPosition.CenterAlways WindowPosition = WindowPosition.CenterAlways,
}; };
msgDialog.SetDefaultSize(400, 0); msgDialog.SetDefaultSize(400, 0);
@ -193,19 +192,6 @@ namespace Ryujinx.Ui.Applet
return showDetails; return showDetails;
} }
private void SynchronousGtkInvoke(Action action)
{
var waitHandle = new ManualResetEventSlim();
Application.Invoke(delegate
{
action();
waitHandle.Set();
});
waitHandle.Wait();
}
public IDynamicTextInputHandler CreateDynamicTextInputHandler() public IDynamicTextInputHandler CreateDynamicTextInputHandler()
{ {
return new GtkDynamicTextInputHandler(_parent); return new GtkDynamicTextInputHandler(_parent);

View file

@ -6,7 +6,7 @@ namespace Ryujinx.Ui.Applet
{ {
internal class GtkHostUiTheme : IHostUiTheme internal class GtkHostUiTheme : IHostUiTheme
{ {
private const int RenderSurfaceWidth = 32; private const int RenderSurfaceWidth = 32;
private const int RenderSurfaceHeight = 32; private const int RenderSurfaceHeight = 32;
public string FontFamily { get; private set; } public string FontFamily { get; private set; }
@ -19,7 +19,7 @@ namespace Ryujinx.Ui.Applet
public GtkHostUiTheme(Window parent) public GtkHostUiTheme(Window parent)
{ {
Entry entry = new Entry(); Entry entry = new();
entry.SetStateFlags(StateFlags.Selected, true); entry.SetStateFlags(StateFlags.Selected, true);
// Get the font and some colors directly from GTK. // Get the font and some colors directly from GTK.
@ -30,10 +30,10 @@ namespace Ryujinx.Ui.Applet
var defaultForegroundColor = entry.StyleContext.GetColor(StateFlags.Normal); var defaultForegroundColor = entry.StyleContext.GetColor(StateFlags.Normal);
var selectedForegroundColor = entry.StyleContext.GetColor(StateFlags.Selected); var selectedForegroundColor = entry.StyleContext.GetColor(StateFlags.Selected);
DefaultForegroundColor = new ThemeColor((float) defaultForegroundColor.Alpha, (float) defaultForegroundColor.Red, (float) defaultForegroundColor.Green, (float) defaultForegroundColor.Blue); DefaultForegroundColor = new ThemeColor((float)defaultForegroundColor.Alpha, (float)defaultForegroundColor.Red, (float)defaultForegroundColor.Green, (float)defaultForegroundColor.Blue);
SelectionForegroundColor = new ThemeColor((float)selectedForegroundColor.Alpha, (float)selectedForegroundColor.Red, (float)selectedForegroundColor.Green, (float)selectedForegroundColor.Blue); SelectionForegroundColor = new ThemeColor((float)selectedForegroundColor.Alpha, (float)selectedForegroundColor.Red, (float)selectedForegroundColor.Green, (float)selectedForegroundColor.Blue);
ListBoxRow row = new ListBoxRow(); ListBoxRow row = new();
row.SetStateFlags(StateFlags.Selected, true); row.SetStateFlags(StateFlags.Selected, true);
// Request the main thread to render some UI elements to an image to get an approximation for the color. // Request the main thread to render some UI elements to an image to get an approximation for the color.
@ -67,7 +67,7 @@ namespace Ryujinx.Ui.Applet
SelectionBackgroundColor = DefaultBorderColor; SelectionBackgroundColor = DefaultBorderColor;
} }
private ThemeColor ToThemeColor(byte[] data) private static ThemeColor ToThemeColor(byte[] data)
{ {
Debug.Assert(data.Length == 4 * RenderSurfaceWidth * RenderSurfaceHeight); Debug.Assert(data.Length == 4 * RenderSurfaceWidth * RenderSurfaceHeight);

View file

@ -9,7 +9,9 @@ namespace Ryujinx.Ui.Applet
{ {
private int _inputMin; private int _inputMin;
private int _inputMax; private int _inputMax;
#pragma warning disable IDE0052 // Remove unread private member
private KeyboardMode _mode; private KeyboardMode _mode;
#pragma warning restore IDE0052
private string _validationInfoText = ""; private string _validationInfoText = "";
@ -18,8 +20,8 @@ namespace Ryujinx.Ui.Applet
private readonly Label _validationInfo; private readonly Label _validationInfo;
public Entry InputEntry { get; } public Entry InputEntry { get; }
public Button OkButton { get; } public Button OkButton { get; }
public Button CancelButton { get; } public Button CancelButton { get; }
public SwkbdAppletDialog(Window parent) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, null) public SwkbdAppletDialog(Window parent) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, null)
@ -28,22 +30,22 @@ namespace Ryujinx.Ui.Applet
_validationInfo = new Label() _validationInfo = new Label()
{ {
Visible = false Visible = false,
}; };
InputEntry = new Entry() InputEntry = new Entry()
{ {
Visible = true Visible = true,
}; };
InputEntry.Activated += OnInputActivated; InputEntry.Activated += OnInputActivated;
InputEntry.Changed += OnInputChanged; InputEntry.Changed += OnInputChanged;
OkButton = (Button)AddButton("OK", ResponseType.Ok); OkButton = (Button)AddButton("OK", ResponseType.Ok);
CancelButton = (Button)AddButton("Cancel", ResponseType.Cancel); CancelButton = (Button)AddButton("Cancel", ResponseType.Cancel);
((Box)MessageArea).PackEnd(_validationInfo, true, true, 0); ((Box)MessageArea).PackEnd(_validationInfo, true, true, 0);
((Box)MessageArea).PackEnd(InputEntry, true, true, 4); ((Box)MessageArea).PackEnd(InputEntry, true, true, 4);
} }
private void ApplyValidationInfo() private void ApplyValidationInfo()

View file

@ -12,7 +12,7 @@ namespace Ryujinx.Ui.Helper
{ {
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib"; private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
private struct Selector private readonly struct Selector
{ {
public readonly IntPtr NativePtr; public readonly IntPtr NativePtr;
@ -29,7 +29,7 @@ namespace Ryujinx.Ui.Helper
NativePtr = sel_registerName(data); NativePtr = sel_registerName(data);
} }
public static implicit operator Selector(string value) => new Selector(value); public static implicit operator Selector(string value) => new(value);
} }
private static unsafe IntPtr GetClass(string value) private static unsafe IntPtr GetClass(string value)
@ -45,27 +45,27 @@ namespace Ryujinx.Ui.Helper
return objc_getClass(data); return objc_getClass(data);
} }
private struct NSPoint private struct NsPoint
{ {
public double X; public double X;
public double Y; public double Y;
public NSPoint(double x, double y) public NsPoint(double x, double y)
{ {
X = x; X = x;
Y = y; Y = y;
} }
} }
private struct NSRect private struct NsRect
{ {
public NSPoint Pos; public NsPoint Pos;
public NSPoint Size; public NsPoint Size;
public NSRect(double x, double y, double width, double height) public NsRect(double x, double y, double width, double height)
{ {
Pos = new NSPoint(x, y); Pos = new NsPoint(x, y);
Size = new NSPoint(width, height); Size = new NsPoint(width, height);
} }
} }
@ -81,7 +81,7 @@ namespace Ryujinx.Ui.Helper
// Create a child NSView to render into. // Create a child NSView to render into.
IntPtr nsViewClass = GetClass("NSView"); IntPtr nsViewClass = GetClass("NSView");
IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc"); IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc");
objc_msgSend(child, "init", new NSRect()); objc_msgSend(child, "init", new NsRect());
// Add it as a child. // Add it as a child.
objc_msgSend(nsView, "addSubview:", child); objc_msgSend(nsView, "addSubview:", child);
@ -92,11 +92,12 @@ namespace Ryujinx.Ui.Helper
objc_msgSend(metalLayer, "setContentsScale:", (double)display.GetMonitorAtWindow(window).ScaleFactor); objc_msgSend(metalLayer, "setContentsScale:", (double)display.GetMonitorAtWindow(window).ScaleFactor);
// Set the frame position/location. // Set the frame position/location.
updateBounds = (Window window) => { updateBounds = (Window window) =>
{
window.GetPosition(out int x, out int y); window.GetPosition(out int x, out int y);
int width = window.Width; int width = window.Width;
int height = window.Height; int height = window.Height;
objc_msgSend(child, "setFrame:", new NSRect(x, y, width, height)); objc_msgSend(child, "setFrame:", new NsRect(x, y, width, height));
}; };
updateBounds(window); updateBounds(window);
@ -120,7 +121,7 @@ namespace Ryujinx.Ui.Helper
private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
[LibraryImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point); private static partial void objc_msgSend(IntPtr receiver, Selector selector, NsRect point);
[LibraryImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);

View file

@ -16,7 +16,7 @@ namespace Ryujinx.Ui.Helper
if (File.Exists(ConfigurationState.Instance.Ui.CustomThemePath) && (Path.GetExtension(ConfigurationState.Instance.Ui.CustomThemePath) == ".css")) if (File.Exists(ConfigurationState.Instance.Ui.CustomThemePath) && (Path.GetExtension(ConfigurationState.Instance.Ui.CustomThemePath) == ".css"))
{ {
CssProvider cssProvider = new CssProvider(); CssProvider cssProvider = new();
cssProvider.LoadFromPath(ConfigurationState.Instance.Ui.CustomThemePath); cssProvider.LoadFromPath(ConfigurationState.Instance.Ui.CustomThemePath);
@ -26,7 +26,7 @@ namespace Ryujinx.Ui.Helper
{ {
Logger.Warning?.Print(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\"."); 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.CustomThemePath.Value = "";
ConfigurationState.Instance.Ui.EnableCustomTheme.Value = false; ConfigurationState.Instance.Ui.EnableCustomTheme.Value = false;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
} }

View file

@ -18,8 +18,6 @@ using Ryujinx.Common.SystemInterop;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
@ -51,9 +49,9 @@ namespace Ryujinx.Ui
{ {
public class MainWindow : Window public class MainWindow : Window
{ {
private readonly VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private readonly ContentManager _contentManager; private readonly ContentManager _contentManager;
private readonly AccountManager _accountManager; private readonly AccountManager _accountManager;
private readonly LibHacHorizonManager _libHacHorizonManager; private readonly LibHacHorizonManager _libHacHorizonManager;
private UserChannelPersistence _userChannelPersistence; private UserChannelPersistence _userChannelPersistence;
@ -63,9 +61,9 @@ namespace Ryujinx.Ui
private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
private readonly ApplicationLibrary _applicationLibrary; private readonly ApplicationLibrary _applicationLibrary;
private readonly GtkHostUiHandler _uiHandler; private readonly GtkHostUiHandler _uiHandler;
private readonly AutoResetEvent _deviceExitStatus; private readonly AutoResetEvent _deviceExitStatus;
private readonly ListStore _tableStore; private readonly ListStore _tableStore;
private bool _updatingGameTable; private bool _updatingGameTable;
private bool _gameLoaded; private bool _gameLoaded;
@ -74,76 +72,76 @@ namespace Ryujinx.Ui
private string _currentEmulatedGamePath = null; private string _currentEmulatedGamePath = null;
private string _lastScannedAmiiboId = ""; private string _lastScannedAmiiboId = "";
private bool _lastScannedAmiiboShowAll = false; private bool _lastScannedAmiiboShowAll = false;
public RendererWidgetBase RendererWidget; public RendererWidgetBase RendererWidget;
public InputManager InputManager; public InputManager InputManager;
public bool IsFocused; public bool IsFocused;
#pragma warning disable CS0169, CS0649, IDE0044 #pragma warning disable CS0169, CS0649, IDE0044, IDE0051 // Field is never assigned to, Add readonly modifier, Remove unused private member
[GUI] public MenuItem ExitMenuItem; [GUI] public MenuItem ExitMenuItem;
[GUI] public MenuItem UpdateMenuItem; [GUI] public MenuItem UpdateMenuItem;
[GUI] MenuBar _menuBar; [GUI] MenuBar _menuBar;
[GUI] Box _footerBox; [GUI] Box _footerBox;
[GUI] Box _statusBar; [GUI] Box _statusBar;
[GUI] MenuItem _optionMenu; [GUI] MenuItem _optionMenu;
[GUI] MenuItem _manageUserProfiles; [GUI] MenuItem _manageUserProfiles;
[GUI] MenuItem _fileMenu; [GUI] MenuItem _fileMenu;
[GUI] MenuItem _loadApplicationFile; [GUI] MenuItem _loadApplicationFile;
[GUI] MenuItem _loadApplicationFolder; [GUI] MenuItem _loadApplicationFolder;
[GUI] MenuItem _appletMenu; [GUI] MenuItem _appletMenu;
[GUI] MenuItem _actionMenu; [GUI] MenuItem _actionMenu;
[GUI] MenuItem _pauseEmulation; [GUI] MenuItem _pauseEmulation;
[GUI] MenuItem _resumeEmulation; [GUI] MenuItem _resumeEmulation;
[GUI] MenuItem _stopEmulation; [GUI] MenuItem _stopEmulation;
[GUI] MenuItem _simulateWakeUpMessage; [GUI] MenuItem _simulateWakeUpMessage;
[GUI] MenuItem _scanAmiibo; [GUI] MenuItem _scanAmiibo;
[GUI] MenuItem _takeScreenshot; [GUI] MenuItem _takeScreenshot;
[GUI] MenuItem _hideUi; [GUI] MenuItem _hideUi;
[GUI] MenuItem _fullScreen; [GUI] MenuItem _fullScreen;
[GUI] CheckMenuItem _startFullScreen; [GUI] CheckMenuItem _startFullScreen;
[GUI] CheckMenuItem _showConsole; [GUI] CheckMenuItem _showConsole;
[GUI] CheckMenuItem _favToggle; [GUI] CheckMenuItem _favToggle;
[GUI] MenuItem _firmwareInstallDirectory; [GUI] MenuItem _firmwareInstallDirectory;
[GUI] MenuItem _firmwareInstallFile; [GUI] MenuItem _firmwareInstallFile;
[GUI] MenuItem _fileTypesSubMenu; [GUI] MenuItem _fileTypesSubMenu;
[GUI] Label _fifoStatus; [GUI] Label _fifoStatus;
[GUI] CheckMenuItem _iconToggle; [GUI] CheckMenuItem _iconToggle;
[GUI] CheckMenuItem _developerToggle; [GUI] CheckMenuItem _developerToggle;
[GUI] CheckMenuItem _appToggle; [GUI] CheckMenuItem _appToggle;
[GUI] CheckMenuItem _timePlayedToggle; [GUI] CheckMenuItem _timePlayedToggle;
[GUI] CheckMenuItem _versionToggle; [GUI] CheckMenuItem _versionToggle;
[GUI] CheckMenuItem _lastPlayedToggle; [GUI] CheckMenuItem _lastPlayedToggle;
[GUI] CheckMenuItem _fileExtToggle; [GUI] CheckMenuItem _fileExtToggle;
[GUI] CheckMenuItem _pathToggle; [GUI] CheckMenuItem _pathToggle;
[GUI] CheckMenuItem _fileSizeToggle; [GUI] CheckMenuItem _fileSizeToggle;
[GUI] CheckMenuItem _nspShown; [GUI] CheckMenuItem _nspShown;
[GUI] CheckMenuItem _pfs0Shown; [GUI] CheckMenuItem _pfs0Shown;
[GUI] CheckMenuItem _xciShown; [GUI] CheckMenuItem _xciShown;
[GUI] CheckMenuItem _ncaShown; [GUI] CheckMenuItem _ncaShown;
[GUI] CheckMenuItem _nroShown; [GUI] CheckMenuItem _nroShown;
[GUI] CheckMenuItem _nsoShown; [GUI] CheckMenuItem _nsoShown;
[GUI] Label _gpuBackend; [GUI] Label _gpuBackend;
[GUI] Label _dockedMode; [GUI] Label _dockedMode;
[GUI] Label _aspectRatio; [GUI] Label _aspectRatio;
[GUI] Label _gameStatus; [GUI] Label _gameStatus;
[GUI] TreeView _gameTable; [GUI] TreeView _gameTable;
[GUI] TreeSelection _gameTableSelection; [GUI] TreeSelection _gameTableSelection;
[GUI] ScrolledWindow _gameTableWindow; [GUI] ScrolledWindow _gameTableWindow;
[GUI] Label _gpuName; [GUI] Label _gpuName;
[GUI] Label _progressLabel; [GUI] Label _progressLabel;
[GUI] Label _firmwareVersionLabel; [GUI] Label _firmwareVersionLabel;
[GUI] Gtk.ProgressBar _progressBar; [GUI] Gtk.ProgressBar _progressBar;
[GUI] Box _viewBox; [GUI] Box _viewBox;
[GUI] Label _vSyncStatus; [GUI] Label _vSyncStatus;
[GUI] Label _volumeStatus; [GUI] Label _volumeStatus;
[GUI] Box _listStatusBox; [GUI] Box _listStatusBox;
[GUI] Label _loadingStatusLabel; [GUI] Label _loadingStatusLabel;
[GUI] Gtk.ProgressBar _loadingStatusBar; [GUI] Gtk.ProgressBar _loadingStatusBar;
#pragma warning restore CS0649, IDE0044, CS0169 #pragma warning restore CS0649, IDE0044, CS0169, IDE0051
public MainWindow() : this(new Builder("Ryujinx.Ui.MainWindow.glade")) { } public MainWindow() : this(new Builder("Ryujinx.Ui.MainWindow.glade")) { }
@ -156,14 +154,14 @@ namespace Ryujinx.Ui
SetWindowSizePosition(); SetWindowSizePosition();
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
Title = $"Ryujinx {Program.Version}"; Title = $"Ryujinx {Program.Version}";
// Hide emulation context status bar. // Hide emulation context status bar.
_statusBar.Hide(); _statusBar.Hide();
// Instantiate HLE objects. // Instantiate HLE objects.
_virtualFileSystem = VirtualFileSystem.CreateInstance(); _virtualFileSystem = VirtualFileSystem.CreateInstance();
_libHacHorizonManager = new LibHacHorizonManager(); _libHacHorizonManager = new LibHacHorizonManager();
_libHacHorizonManager.InitializeFsServer(_virtualFileSystem); _libHacHorizonManager.InitializeFsServer(_virtualFileSystem);
@ -178,36 +176,36 @@ namespace Ryujinx.Ui
// Consider removing this at some point in the future when we don't need to worry about old saves. // Consider removing this at some point in the future when we don't need to worry about old saves.
VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient); VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient);
_contentManager = new ContentManager(_virtualFileSystem); _contentManager = new ContentManager(_virtualFileSystem);
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile); _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile);
_userChannelPersistence = new UserChannelPersistence(); _userChannelPersistence = new UserChannelPersistence();
// Instantiate GUI objects. // Instantiate GUI objects.
_applicationLibrary = new ApplicationLibrary(_virtualFileSystem); _applicationLibrary = new ApplicationLibrary(_virtualFileSystem);
_uiHandler = new GtkHostUiHandler(this); _uiHandler = new GtkHostUiHandler(this);
_deviceExitStatus = new AutoResetEvent(false); _deviceExitStatus = new AutoResetEvent(false);
WindowStateEvent += WindowStateEvent_Changed; WindowStateEvent += WindowStateEvent_Changed;
DeleteEvent += Window_Close; DeleteEvent += Window_Close;
FocusInEvent += MainWindow_FocusInEvent; FocusInEvent += MainWindow_FocusInEvent;
FocusOutEvent += MainWindow_FocusOutEvent; FocusOutEvent += MainWindow_FocusOutEvent;
_applicationLibrary.ApplicationAdded += Application_Added; _applicationLibrary.ApplicationAdded += Application_Added;
_applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated; _applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated;
_fileMenu.StateChanged += FileMenu_StateChanged; _fileMenu.StateChanged += FileMenu_StateChanged;
_actionMenu.StateChanged += ActionMenu_StateChanged; _actionMenu.StateChanged += ActionMenu_StateChanged;
_optionMenu.StateChanged += OptionMenu_StateChanged; _optionMenu.StateChanged += OptionMenu_StateChanged;
_gameTable.ButtonReleaseEvent += Row_Clicked; _gameTable.ButtonReleaseEvent += Row_Clicked;
_fullScreen.Activated += FullScreen_Toggled; _fullScreen.Activated += FullScreen_Toggled;
RendererWidgetBase.StatusUpdatedEvent += Update_StatusBar; RendererWidgetBase.StatusUpdatedEvent += Update_StatusBar;
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
if (ConfigurationState.Instance.Ui.StartFullscreen) if (ConfigurationState.Instance.Ui.StartFullscreen)
{ {
@ -221,43 +219,73 @@ namespace Ryujinx.Ui
_pauseEmulation.Sensitive = false; _pauseEmulation.Sensitive = false;
_resumeEmulation.Sensitive = false; _resumeEmulation.Sensitive = false;
_nspShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value; _nspShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value;
_pfs0Shown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value; _pfs0Shown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value;
_xciShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value; _xciShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value;
_ncaShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value; _ncaShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value;
_nroShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value; _nroShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value;
_nsoShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value; _nsoShown.Active = ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value;
_nspShown.Toggled += NSP_Shown_Toggled; _nspShown.Toggled += NSP_Shown_Toggled;
_pfs0Shown.Toggled += PFS0_Shown_Toggled; _pfs0Shown.Toggled += PFS0_Shown_Toggled;
_xciShown.Toggled += XCI_Shown_Toggled; _xciShown.Toggled += XCI_Shown_Toggled;
_ncaShown.Toggled += NCA_Shown_Toggled; _ncaShown.Toggled += NCA_Shown_Toggled;
_nroShown.Toggled += NRO_Shown_Toggled; _nroShown.Toggled += NRO_Shown_Toggled;
_nsoShown.Toggled += NSO_Shown_Toggled; _nsoShown.Toggled += NSO_Shown_Toggled;
_fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported; _fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported;
if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true; if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn)
if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true; {
if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true; _favToggle.Active = true;
if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _developerToggle.Active = true; }
if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _versionToggle.Active = true; if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn)
if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedToggle.Active = true; {
if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedToggle.Active = true; _iconToggle.Active = true;
if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _fileExtToggle.Active = true; }
if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeToggle.Active = true; if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn)
if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathToggle.Active = true; {
_appToggle.Active = true;
}
if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn)
{
_developerToggle.Active = true;
}
if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn)
{
_versionToggle.Active = true;
}
if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn)
{
_timePlayedToggle.Active = true;
}
if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn)
{
_lastPlayedToggle.Active = true;
}
if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn)
{
_fileExtToggle.Active = true;
}
if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn)
{
_fileSizeToggle.Active = true;
}
if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn)
{
_pathToggle.Active = true;
}
_favToggle.Toggled += Fav_Toggled; _favToggle.Toggled += Fav_Toggled;
_iconToggle.Toggled += Icon_Toggled; _iconToggle.Toggled += Icon_Toggled;
_appToggle.Toggled += App_Toggled; _appToggle.Toggled += App_Toggled;
_developerToggle.Toggled += Developer_Toggled; _developerToggle.Toggled += Developer_Toggled;
_versionToggle.Toggled += Version_Toggled; _versionToggle.Toggled += Version_Toggled;
_timePlayedToggle.Toggled += TimePlayed_Toggled; _timePlayedToggle.Toggled += TimePlayed_Toggled;
_lastPlayedToggle.Toggled += LastPlayed_Toggled; _lastPlayedToggle.Toggled += LastPlayed_Toggled;
_fileExtToggle.Toggled += FileExt_Toggled; _fileExtToggle.Toggled += FileExt_Toggled;
_fileSizeToggle.Toggled += FileSize_Toggled; _fileSizeToggle.Toggled += FileSize_Toggled;
_pathToggle.Toggled += Path_Toggled; _pathToggle.Toggled += Path_Toggled;
_gameTable.Model = _tableStore = new ListStore( _gameTable.Model = _tableStore = new ListStore(
typeof(bool), typeof(bool),
@ -276,7 +304,7 @@ namespace Ryujinx.Ui
_tableStore.SetSortFunc(6, SortHelper.LastPlayedSort); _tableStore.SetSortFunc(6, SortHelper.LastPlayedSort);
_tableStore.SetSortFunc(8, SortHelper.FileSizeSort); _tableStore.SetSortFunc(8, SortHelper.FileSizeSort);
int columnId = ConfigurationState.Instance.Ui.ColumnSort.SortColumnId; int columnId = ConfigurationState.Instance.Ui.ColumnSort.SortColumnId;
bool ascending = ConfigurationState.Instance.Ui.ColumnSort.SortAscending; bool ascending = ConfigurationState.Instance.Ui.ColumnSort.SortAscending;
_tableStore.SetSortColumnId(columnId, ascending ? SortType.Ascending : SortType.Descending); _tableStore.SetSortColumnId(columnId, ascending ? SortType.Ascending : SortType.Descending);
@ -321,10 +349,7 @@ namespace Ryujinx.Ui
private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e) private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e)
{ {
if (_emulationContext != null) _emulationContext?.System.ChangeDockedModeState(e.NewValue);
{
_emulationContext.System.ChangeDockedModeState(e.NewValue);
}
} }
private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e) private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
@ -354,19 +379,49 @@ namespace Ryujinx.Ui
_gameTable.RemoveColumn(column); _gameTable.RemoveColumn(column);
} }
CellRendererToggle favToggle = new CellRendererToggle(); CellRendererToggle favToggle = new();
favToggle.Toggled += FavToggle_Toggled; favToggle.Toggled += FavToggle_Toggled;
if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _gameTable.AppendColumn("Fav", favToggle, "active", 0); if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn)
if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1); {
if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2); _gameTable.AppendColumn("Fav", favToggle, "active", 0);
if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3); }
if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4); if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn)
if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5); {
if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6); _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1);
if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7); }
if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8); if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn)
if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9); {
_gameTable.AppendColumn("Application", new CellRendererText(), "text", 2);
}
if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn)
{
_gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3);
}
if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn)
{
_gameTable.AppendColumn("Version", new CellRendererText(), "text", 4);
}
if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn)
{
_gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5);
}
if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn)
{
_gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6);
}
if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn)
{
_gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7);
}
if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn)
{
_gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8);
}
if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn)
{
_gameTable.AppendColumn("Path", new CellRendererText(), "text", 9);
}
foreach (TreeViewColumn column in _gameTable.Columns) foreach (TreeViewColumn column in _gameTable.Columns)
{ {
@ -426,11 +481,11 @@ namespace Ryujinx.Ui
if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan)
{ {
string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
renderer = new VulkanRenderer(Vk.GetApi(), CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); renderer = new Graphics.Vulkan.VulkanRenderer(Vk.GetApi(), CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu);
} }
else else
{ {
renderer = new OpenGLRenderer(); renderer = new Graphics.OpenGL.OpenGLRenderer();
} }
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
@ -570,38 +625,38 @@ namespace Ryujinx.Ui
IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(_virtualFileSystem, HLE.HLEConfiguration configuration = new(_virtualFileSystem,
_libHacHorizonManager, _libHacHorizonManager,
_contentManager, _contentManager,
_accountManager, _accountManager,
_userChannelPersistence, _userChannelPersistence,
renderer, renderer,
deviceDriver, deviceDriver,
memoryConfiguration, memoryConfiguration,
_uiHandler, _uiHandler,
(SystemLanguage)ConfigurationState.Instance.System.Language.Value, (SystemLanguage)ConfigurationState.Instance.System.Language.Value,
(RegionCode)ConfigurationState.Instance.System.Region.Value, (RegionCode)ConfigurationState.Instance.System.Region.Value,
ConfigurationState.Instance.Graphics.EnableVsync, ConfigurationState.Instance.Graphics.EnableVsync,
ConfigurationState.Instance.System.EnableDockedMode, ConfigurationState.Instance.System.EnableDockedMode,
ConfigurationState.Instance.System.EnablePtc, ConfigurationState.Instance.System.EnablePtc,
ConfigurationState.Instance.System.EnableInternetAccess, ConfigurationState.Instance.System.EnableInternetAccess,
fsIntegrityCheckLevel, fsIntegrityCheckLevel,
ConfigurationState.Instance.System.FsGlobalAccessLogMode, ConfigurationState.Instance.System.FsGlobalAccessLogMode,
ConfigurationState.Instance.System.SystemTimeOffset, ConfigurationState.Instance.System.SystemTimeOffset,
ConfigurationState.Instance.System.TimeZone, ConfigurationState.Instance.System.TimeZone,
ConfigurationState.Instance.System.MemoryManagerMode, ConfigurationState.Instance.System.MemoryManagerMode,
ConfigurationState.Instance.System.IgnoreMissingServices, ConfigurationState.Instance.System.IgnoreMissingServices,
ConfigurationState.Instance.Graphics.AspectRatio, ConfigurationState.Instance.Graphics.AspectRatio,
ConfigurationState.Instance.System.AudioVolume, ConfigurationState.Instance.System.AudioVolume,
ConfigurationState.Instance.System.UseHypervisor, ConfigurationState.Instance.System.UseHypervisor,
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);
_emulationContext = new HLE.Switch(configuration); _emulationContext = new HLE.Switch(configuration);
} }
private SurfaceKHR CreateVulkanSurface(Instance instance, Vk vk) private SurfaceKHR CreateVulkanSurface(Instance instance, Vk vk)
{ {
return new SurfaceKHR((ulong)((VKRenderer)RendererWidget).CreateWindowSurface(instance.Handle)); return new SurfaceKHR((ulong)((VulkanRenderer)RendererWidget).CreateWindowSurface(instance.Handle));
} }
private void SetupProgressUiHandlers() private void SetupProgressUiHandlers()
@ -655,14 +710,16 @@ namespace Ryujinx.Ui
_tableStore.Clear(); _tableStore.Clear();
Thread applicationLibraryThread = new Thread(() => Thread applicationLibraryThread = new(() =>
{ {
_applicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, ConfigurationState.Instance.System.Language); _applicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, ConfigurationState.Instance.System.Language);
_updatingGameTable = false; _updatingGameTable = false;
}); })
applicationLibraryThread.Name = "GUI.ApplicationLibraryThread"; {
applicationLibraryThread.IsBackground = true; Name = "GUI.ApplicationLibraryThread",
IsBackground = true,
};
applicationLibraryThread.Start(); applicationLibraryThread.Start();
} }
@ -671,11 +728,11 @@ namespace Ryujinx.Ui
{ {
if (ConfigurationState.Instance.Logger.EnableTrace.Value) if (ConfigurationState.Instance.Logger.EnableTrace.Value)
{ {
MessageDialog debugWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) MessageDialog debugWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
{ {
Title = "Ryujinx - Warning", Title = "Ryujinx - Warning",
Text = "You have trace logging enabled, which is designed to be used by developers only.", Text = "You have trace logging enabled, which is designed to be used by developers only.",
SecondaryText = "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?" SecondaryText = "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?",
}; };
if (debugWarningDialog.Run() == (int)ResponseType.Yes) if (debugWarningDialog.Run() == (int)ResponseType.Yes)
@ -689,11 +746,11 @@ namespace Ryujinx.Ui
if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value)) if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
{ {
MessageDialog shadersDumpWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) MessageDialog shadersDumpWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
{ {
Title = "Ryujinx - Warning", Title = "Ryujinx - Warning",
Text = "You have shader dumping enabled, which is designed to be used by developers only.", Text = "You have shader dumping enabled, which is designed to be used by developers only.",
SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?" SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?",
}; };
if (shadersDumpWarningDialog.Run() == (int)ResponseType.Yes) if (shadersDumpWarningDialog.Run() == (int)ResponseType.Yes)
@ -857,18 +914,18 @@ namespace Ryujinx.Ui
Thread windowThread = new(CreateGameWindow) Thread windowThread = new(CreateGameWindow)
{ {
Name = "GUI.WindowThread" Name = "GUI.WindowThread",
}; };
windowThread.Start(); windowThread.Start();
_gameLoaded = true; _gameLoaded = true;
_actionMenu.Sensitive = true; _actionMenu.Sensitive = true;
UpdateMenuItem.Sensitive = false; UpdateMenuItem.Sensitive = false;
_lastScannedAmiiboId = ""; _lastScannedAmiiboId = "";
_firmwareInstallFile.Sensitive = false; _firmwareInstallFile.Sensitive = false;
_firmwareInstallDirectory.Sensitive = false; _firmwareInstallDirectory.Sensitive = false;
DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Processes.ActiveApplication.ProgramIdText, DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Processes.ActiveApplication.ProgramIdText,
@ -885,11 +942,11 @@ namespace Ryujinx.Ui
{ {
if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan)
{ {
return new VKRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); return new VulkanRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
} }
else else
{ {
return new GlRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); return new OpenGLRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
} }
} }
@ -1030,20 +1087,20 @@ namespace Ryujinx.Ui
} }
} }
public void UpdateGraphicsConfig() public static void UpdateGraphicsConfig()
{ {
int resScale = ConfigurationState.Instance.Graphics.ResScale; int resScale = ConfigurationState.Instance.Graphics.ResScale;
float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom; float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom;
Graphics.Gpu.GraphicsConfig.ResScale = (resScale == -1) ? resScaleCustom : resScale; Graphics.Gpu.GraphicsConfig.ResScale = (resScale == -1) ? resScaleCustom : resScale;
Graphics.Gpu.GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; Graphics.Gpu.GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
Graphics.Gpu.GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; Graphics.Gpu.GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
} }
public void SaveConfig() public static void SaveConfig()
{ {
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
} }
@ -1105,7 +1162,7 @@ namespace Ryujinx.Ui
Application.Invoke(delegate Application.Invoke(delegate
{ {
_progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded"; _progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded";
float barValue = 0; float barValue = 0;
if (args.NumAppsFound != 0) if (args.NumAppsFound != 0)
{ {
@ -1126,12 +1183,12 @@ namespace Ryujinx.Ui
{ {
Application.Invoke(delegate Application.Invoke(delegate
{ {
_gameStatus.Text = args.GameStatus; _gameStatus.Text = args.GameStatus;
_fifoStatus.Text = args.FifoStatus; _fifoStatus.Text = args.FifoStatus;
_gpuName.Text = args.GpuName; _gpuName.Text = args.GpuName;
_dockedMode.Text = args.DockedMode; _dockedMode.Text = args.DockedMode;
_aspectRatio.Text = args.AspectRatio; _aspectRatio.Text = args.AspectRatio;
_gpuBackend.Text = args.GpuBackend; _gpuBackend.Text = args.GpuBackend;
_volumeStatus.Text = GetVolumeLabelText(args.Volume); _volumeStatus.Text = GetVolumeLabelText(args.Volume);
if (args.VSyncEnabled) if (args.VSyncEnabled)
@ -1151,8 +1208,8 @@ namespace Ryujinx.Ui
{ {
_tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path)); _tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path));
string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
bool newToggleValue = !(bool)_tableStore.GetValue(treeIter, 0); bool newToggleValue = !(bool)_tableStore.GetValue(treeIter, 0);
_tableStore.SetValue(treeIter, 0, newToggleValue); _tableStore.SetValue(treeIter, 0, newToggleValue);
@ -1166,7 +1223,7 @@ namespace Ryujinx.Ui
{ {
TreeViewColumn column = (TreeViewColumn)sender; TreeViewColumn column = (TreeViewColumn)sender;
ConfigurationState.Instance.Ui.ColumnSort.SortColumnId.Value = column.SortColumnId; ConfigurationState.Instance.Ui.ColumnSort.SortColumnId.Value = column.SortColumnId;
ConfigurationState.Instance.Ui.ColumnSort.SortAscending.Value = column.SortOrder == SortType.Ascending; ConfigurationState.Instance.Ui.ColumnSort.SortAscending.Value = column.SortOrder == SortType.Ascending;
SaveConfig(); SaveConfig();
@ -1193,7 +1250,7 @@ namespace Ryujinx.Ui
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
} }
private string GetVolumeLabelText(float volume) private static string GetVolumeLabelText(float volume)
{ {
string icon = volume == 0 ? "🔇" : "🔊"; string icon = volume == 0 ? "🔇" : "🔊";
@ -1237,8 +1294,8 @@ namespace Ryujinx.Ui
} }
string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString(); string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString();
string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0]; string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0];
string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10); BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10);
@ -1247,43 +1304,41 @@ namespace Ryujinx.Ui
private void Load_Application_File(object sender, EventArgs args) private void Load_Application_File(object sender, EventArgs args)
{ {
using (FileChooserNative fileChooser = new FileChooserNative("Choose the file to open", this, FileChooserAction.Open, "Open", "Cancel")) using FileChooserNative fileChooser = new("Choose the file to open", this, FileChooserAction.Open, "Open", "Cancel");
FileFilter filter = new()
{ {
FileFilter filter = new FileFilter() Name = "Switch Executables",
{ };
Name = "Switch Executables" filter.AddPattern("*.xci");
}; filter.AddPattern("*.nsp");
filter.AddPattern("*.xci"); filter.AddPattern("*.pfs0");
filter.AddPattern("*.nsp"); filter.AddPattern("*.nca");
filter.AddPattern("*.pfs0"); filter.AddPattern("*.nro");
filter.AddPattern("*.nca"); filter.AddPattern("*.nso");
filter.AddPattern("*.nro");
filter.AddPattern("*.nso");
fileChooser.AddFilter(filter); fileChooser.AddFilter(filter);
if (fileChooser.Run() == (int)ResponseType.Accept) if (fileChooser.Run() == (int)ResponseType.Accept)
{ {
RunApplication(fileChooser.Filename); RunApplication(fileChooser.Filename);
}
} }
} }
private void Load_Application_Folder(object sender, EventArgs args) private void Load_Application_Folder(object sender, EventArgs args)
{ {
using (FileChooserNative fileChooser = new FileChooserNative("Choose the folder to open", this, FileChooserAction.SelectFolder, "Open", "Cancel")) using FileChooserNative fileChooser = new("Choose the folder to open", this, FileChooserAction.SelectFolder, "Open", "Cancel");
if (fileChooser.Run() == (int)ResponseType.Accept)
{ {
if (fileChooser.Run() == (int)ResponseType.Accept) RunApplication(fileChooser.Filename);
{
RunApplication(fileChooser.Filename);
}
} }
} }
private void FileMenu_StateChanged(object o, StateChangedArgs args) private void FileMenu_StateChanged(object o, StateChangedArgs args)
{ {
_appletMenu.Sensitive = _emulationContext == null && _contentManager.GetCurrentFirmwareVersion() != null && _contentManager.GetCurrentFirmwareVersion().Major > 3; _appletMenu.Sensitive = _emulationContext == null && _contentManager.GetCurrentFirmwareVersion() != null && _contentManager.GetCurrentFirmwareVersion().Major > 3;
_loadApplicationFile.Sensitive = _emulationContext == null; _loadApplicationFile.Sensitive = _emulationContext == null;
_loadApplicationFolder.Sensitive = _emulationContext == null; _loadApplicationFolder.Sensitive = _emulationContext == null;
} }
@ -1332,7 +1387,7 @@ namespace Ryujinx.Ui
private void SetWindowSizePosition() private void SetWindowSizePosition()
{ {
DefaultWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth; DefaultWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth;
DefaultHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight; DefaultHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight;
Move(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); Move(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY);
@ -1399,11 +1454,11 @@ namespace Ryujinx.Ui
private void Installer_File_Pressed(object o, EventArgs args) private void Installer_File_Pressed(object o, EventArgs args)
{ {
FileChooserNative fileChooser = new FileChooserNative("Choose the firmware file to open", this, FileChooserAction.Open, "Open", "Cancel"); FileChooserNative fileChooser = new("Choose the firmware file to open", this, FileChooserAction.Open, "Open", "Cancel");
FileFilter filter = new FileFilter FileFilter filter = new()
{ {
Name = "Switch Firmware Files" Name = "Switch Firmware Files",
}; };
filter.AddPattern("*.zip"); filter.AddPattern("*.zip");
filter.AddPattern("*.xci"); filter.AddPattern("*.xci");
@ -1415,7 +1470,7 @@ namespace Ryujinx.Ui
private void Installer_Directory_Pressed(object o, EventArgs args) private void Installer_Directory_Pressed(object o, EventArgs args)
{ {
FileChooserNative directoryChooser = new FileChooserNative("Choose the firmware directory to open", this, FileChooserAction.SelectFolder, "Open", "Cancel"); FileChooserNative directoryChooser = new("Choose the firmware directory to open", this, FileChooserAction.SelectFolder, "Open", "Cancel");
HandleInstallerDialog(directoryChooser); HandleInstallerDialog(directoryChooser);
} }
@ -1460,7 +1515,7 @@ namespace Ryujinx.Ui
{ {
Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}"); Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}");
Thread thread = new Thread(() => Thread thread = new(() =>
{ {
Application.Invoke(delegate Application.Invoke(delegate
{ {
@ -1483,7 +1538,7 @@ namespace Ryujinx.Ui
// Purge Applet Cache. // Purge Applet Cache.
DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); DirectoryInfo miiEditorCacheFolder = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
if (miiEditorCacheFolder.Exists) if (miiEditorCacheFolder.Exists)
{ {
@ -1504,9 +1559,10 @@ namespace Ryujinx.Ui
{ {
RefreshFirmwareLabel(); RefreshFirmwareLabel();
} }
}); })
{
thread.Name = "GUI.FirmwareInstallerThread"; Name = "GUI.FirmwareInstallerThread",
};
thread.Start(); thread.Start();
} }
} }
@ -1571,7 +1627,7 @@ namespace Ryujinx.Ui
else else
{ {
// otherwise, clear state. // otherwise, clear state.
_userChannelPersistence = new UserChannelPersistence(); _userChannelPersistence = new UserChannelPersistence();
_currentEmulatedGamePath = null; _currentEmulatedGamePath = null;
_actionMenu.Sensitive = false; _actionMenu.Sensitive = false;
_firmwareInstallFile.Sensitive = true; _firmwareInstallFile.Sensitive = true;
@ -1616,7 +1672,7 @@ namespace Ryujinx.Ui
private void Settings_Pressed(object sender, EventArgs args) private void Settings_Pressed(object sender, EventArgs args)
{ {
SettingsWindow settingsWindow = new SettingsWindow(this, _virtualFileSystem, _contentManager); SettingsWindow settingsWindow = new(this, _virtualFileSystem, _contentManager);
settingsWindow.SetSizeRequest((int)(settingsWindow.DefaultWidth * Program.WindowScaleFactor), (int)(settingsWindow.DefaultHeight * Program.WindowScaleFactor)); settingsWindow.SetSizeRequest((int)(settingsWindow.DefaultWidth * Program.WindowScaleFactor), (int)(settingsWindow.DefaultHeight * Program.WindowScaleFactor));
settingsWindow.Show(); settingsWindow.Show();
@ -1648,7 +1704,7 @@ namespace Ryujinx.Ui
private void ManageUserProfiles_Pressed(object sender, EventArgs args) private void ManageUserProfiles_Pressed(object sender, EventArgs args)
{ {
UserProfilesManagerWindow userProfilesManagerWindow = new UserProfilesManagerWindow(_accountManager, _contentManager, _virtualFileSystem); UserProfilesManagerWindow userProfilesManagerWindow = new(_accountManager, _contentManager, _virtualFileSystem);
userProfilesManagerWindow.SetSizeRequest((int)(userProfilesManagerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(userProfilesManagerWindow.DefaultHeight * Program.WindowScaleFactor)); userProfilesManagerWindow.SetSizeRequest((int)(userProfilesManagerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(userProfilesManagerWindow.DefaultHeight * Program.WindowScaleFactor));
userProfilesManagerWindow.Show(); userProfilesManagerWindow.Show();
@ -1656,15 +1712,12 @@ namespace Ryujinx.Ui
private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args) private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args)
{ {
if (_emulationContext != null) _emulationContext?.System.SimulateWakeUpMessage();
{
_emulationContext.System.SimulateWakeUpMessage();
}
} }
private void ActionMenu_StateChanged(object o, StateChangedArgs args) private void ActionMenu_StateChanged(object o, StateChangedArgs args)
{ {
_scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _); _scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _);
_takeScreenshot.Sensitive = _emulationContext != null; _takeScreenshot.Sensitive = _emulationContext != null;
} }
@ -1672,12 +1725,12 @@ namespace Ryujinx.Ui
{ {
if (_emulationContext.System.SearchingForAmiibo(out int deviceId)) if (_emulationContext.System.SearchingForAmiibo(out int deviceId))
{ {
AmiiboWindow amiiboWindow = new AmiiboWindow AmiiboWindow amiiboWindow = new()
{ {
LastScannedAmiiboShowAll = _lastScannedAmiiboShowAll, LastScannedAmiiboShowAll = _lastScannedAmiiboShowAll,
LastScannedAmiiboId = _lastScannedAmiiboId, LastScannedAmiiboId = _lastScannedAmiiboId,
DeviceId = deviceId, DeviceId = deviceId,
TitleId = _emulationContext.Processes.ActiveApplication.ProgramIdText.ToUpper() TitleId = _emulationContext.Processes.ActiveApplication.ProgramIdText.ToUpper(),
}; };
amiiboWindow.DeleteEvent += AmiiboWindow_DeleteEvent; amiiboWindow.DeleteEvent += AmiiboWindow_DeleteEvent;
@ -1702,7 +1755,7 @@ namespace Ryujinx.Ui
{ {
if (((AmiiboWindow)sender).AmiiboId != "" && ((AmiiboWindow)sender).Response == ResponseType.Ok) if (((AmiiboWindow)sender).AmiiboId != "" && ((AmiiboWindow)sender).Response == ResponseType.Ok)
{ {
_lastScannedAmiiboId = ((AmiiboWindow)sender).AmiiboId; _lastScannedAmiiboId = ((AmiiboWindow)sender).AmiiboId;
_lastScannedAmiiboShowAll = ((AmiiboWindow)sender).LastScannedAmiiboShowAll; _lastScannedAmiiboShowAll = ((AmiiboWindow)sender).LastScannedAmiiboShowAll;
_emulationContext.System.ScanAmiibo(((AmiiboWindow)sender).DeviceId, ((AmiiboWindow)sender).AmiiboId, ((AmiiboWindow)sender).UseRandomUuid); _emulationContext.System.ScanAmiibo(((AmiiboWindow)sender).DeviceId, ((AmiiboWindow)sender).AmiiboId, ((AmiiboWindow)sender).UseRandomUuid);
@ -1722,7 +1775,7 @@ namespace Ryujinx.Ui
private void About_Pressed(object sender, EventArgs args) private void About_Pressed(object sender, EventArgs args)
{ {
AboutWindow aboutWindow = new AboutWindow(); AboutWindow aboutWindow = new();
aboutWindow.SetSizeRequest((int)(aboutWindow.DefaultWidth * Program.WindowScaleFactor), (int)(aboutWindow.DefaultHeight * Program.WindowScaleFactor)); aboutWindow.SetSizeRequest((int)(aboutWindow.DefaultWidth * Program.WindowScaleFactor), (int)(aboutWindow.DefaultHeight * Program.WindowScaleFactor));
aboutWindow.Show(); aboutWindow.Show();
@ -1824,7 +1877,7 @@ namespace Ryujinx.Ui
UpdateGameTable(); UpdateGameTable();
} }
private void XCI_Shown_Toggled (object sender, EventArgs args) private void XCI_Shown_Toggled(object sender, EventArgs args)
{ {
ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = _xciShown.Active; ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = _xciShown.Active;
@ -1832,7 +1885,7 @@ namespace Ryujinx.Ui
UpdateGameTable(); UpdateGameTable();
} }
private void NCA_Shown_Toggled (object sender, EventArgs args) private void NCA_Shown_Toggled(object sender, EventArgs args)
{ {
ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = _ncaShown.Active; ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = _ncaShown.Active;
@ -1840,7 +1893,7 @@ namespace Ryujinx.Ui
UpdateGameTable(); UpdateGameTable();
} }
private void NRO_Shown_Toggled (object sender, EventArgs args) private void NRO_Shown_Toggled(object sender, EventArgs args)
{ {
ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = _nroShown.Active; ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = _nroShown.Active;
@ -1848,7 +1901,7 @@ namespace Ryujinx.Ui
UpdateGameTable(); UpdateGameTable();
} }
private void NSO_Shown_Toggled (object sender, EventArgs args) private void NSO_Shown_Toggled(object sender, EventArgs args)
{ {
ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = _nsoShown.Active; ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = _nsoShown.Active;

View file

@ -1,7 +1,6 @@
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using SPB.Graphics; using SPB.Graphics;
using SPB.Graphics.Exceptions; using SPB.Graphics.Exceptions;
@ -15,16 +14,16 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Ui namespace Ryujinx.Ui
{ {
public partial class GlRenderer : RendererWidgetBase public partial class OpenGLRenderer : RendererWidgetBase
{ {
private GraphicsDebugLevel _glLogLevel; private readonly GraphicsDebugLevel _glLogLevel;
private bool _initializedOpenGL; private bool _initializedOpenGL;
private OpenGLContextBase _openGLContext; private OpenGLContextBase _openGLContext;
private SwappableNativeWindowBase _nativeWindow; private SwappableNativeWindowBase _nativeWindow;
public GlRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) public OpenGLRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel)
{ {
_glLogLevel = glLogLevel; _glLogLevel = glLogLevel;
} }
@ -93,7 +92,7 @@ namespace Ryujinx.Ui
public override void InitializeRenderer() public override void InitializeRenderer()
{ {
// First take exclusivity on the OpenGL context. // First take exclusivity on the OpenGL context.
((OpenGLRenderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext)); ((Graphics.OpenGL.OpenGLRenderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext));
_openGLContext.MakeCurrent(_nativeWindow); _openGLContext.MakeCurrent(_nativeWindow);

View file

@ -5,7 +5,7 @@ namespace Ryujinx.Ui
{ {
public class OpenToolkitBindingsContext : OpenTK.IBindingsContext public class OpenToolkitBindingsContext : OpenTK.IBindingsContext
{ {
private IBindingsContext _bindingContext; private readonly IBindingsContext _bindingContext;
public OpenToolkitBindingsContext(IBindingsContext bindingsContext) public OpenToolkitBindingsContext(IBindingsContext bindingsContext)
{ {

View file

@ -21,14 +21,13 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Image = SixLabors.ImageSharp.Image;
using Key = Ryujinx.Input.Key;
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
using Switch = Ryujinx.HLE.Switch;
namespace Ryujinx.Ui namespace Ryujinx.Ui
{ {
using Image = SixLabors.ImageSharp.Image;
using Key = Input.Key;
using ScalingFilter = Graphics.GAL.ScalingFilter;
using Switch = HLE.Switch;
public abstract class RendererWidgetBase : DrawingArea public abstract class RendererWidgetBase : DrawingArea
{ {
private const int SwitchPanelWidth = 1280; private const int SwitchPanelWidth = 1280;
@ -71,12 +70,12 @@ namespace Ryujinx.Ui
// Hide Cursor // Hide Cursor
const int CursorHideIdleTime = 5; // seconds const int CursorHideIdleTime = 5; // seconds
private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor); private static readonly Cursor _invisibleCursor = new(Display.Default, CursorType.BlankCursor);
private long _lastCursorMoveTime; private long _lastCursorMoveTime;
private HideCursorMode _hideCursorMode; private HideCursorMode _hideCursorMode;
private InputManager _inputManager; private readonly InputManager _inputManager;
private IKeyboard _keyboardInterface; private readonly IKeyboard _keyboardInterface;
private GraphicsDebugLevel _glLogLevel; private readonly GraphicsDebugLevel _glLogLevel;
private string _gpuBackendName; private string _gpuBackendName;
private string _gpuVendorName; private string _gpuVendorName;
private bool _isMouseInClient; private bool _isMouseInClient;
@ -165,7 +164,7 @@ namespace Ryujinx.Ui
Window.Cursor = _invisibleCursor; Window.Cursor = _invisibleCursor;
break; break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException(nameof(state));
} }
}); });
} }
@ -379,12 +378,12 @@ namespace Ryujinx.Ui
{ {
lock (this) lock (this)
{ {
var currentTime = DateTime.Now; var currentTime = DateTime.Now;
string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
string directory = AppDataManager.Mode switch string directory = AppDataManager.Mode switch
{ {
AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"), AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
_ => System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") _ => System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"),
}; };
string path = System.IO.Path.Combine(directory, filename); string path = System.IO.Path.Combine(directory, filename);
@ -415,7 +414,7 @@ namespace Ryujinx.Ui
image.SaveAsPng(path, new PngEncoder() image.SaveAsPng(path, new PngEncoder()
{ {
ColorType = PngColorType.Rgb ColorType = PngColorType.Rgb,
}); });
image.Dispose(); image.Dispose();
@ -524,30 +523,30 @@ namespace Ryujinx.Ui
{ {
parent.Present(); parent.Present();
var activeProcess = Device.Processes.ActiveApplication; var activeProcess = Device.Processes.ActiveApplication;
string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}"; string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}";
string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}"; string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}";
string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})"; string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})";
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)"; string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
parent.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; parent.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
}); });
Thread renderLoopThread = new Thread(Render) Thread renderLoopThread = new(Render)
{ {
Name = "GUI.RenderLoop" Name = "GUI.RenderLoop",
}; };
renderLoopThread.Start(); renderLoopThread.Start();
Thread nvStutterWorkaround = null; Thread nvidiaStutterWorkaround = null;
if (Renderer is Graphics.OpenGL.OpenGLRenderer) if (Renderer is Graphics.OpenGL.OpenGLRenderer)
{ {
nvStutterWorkaround = new Thread(NVStutterWorkaround) nvidiaStutterWorkaround = new Thread(NvidiaStutterWorkaround)
{ {
Name = "GUI.NVStutterWorkaround" Name = "GUI.NvidiaStutterWorkaround",
}; };
nvStutterWorkaround.Start(); nvidiaStutterWorkaround.Start();
} }
MainLoop(); MainLoop();
@ -556,7 +555,7 @@ namespace Ryujinx.Ui
// We only need to wait for all commands submitted during the main gpu loop to be processed. // We only need to wait for all commands submitted during the main gpu loop to be processed.
_gpuDoneEvent.WaitOne(); _gpuDoneEvent.WaitOne();
_gpuDoneEvent.Dispose(); _gpuDoneEvent.Dispose();
nvStutterWorkaround?.Join(); nvidiaStutterWorkaround?.Join();
Exit(); Exit();
} }
@ -584,7 +583,7 @@ namespace Ryujinx.Ui
} }
} }
private void NVStutterWorkaround() private void NvidiaStutterWorkaround()
{ {
while (_isActive) while (_isActive)
{ {
@ -752,7 +751,7 @@ namespace Ryujinx.Ui
ResScaleUp = 1 << 5, ResScaleUp = 1 << 5,
ResScaleDown = 1 << 6, ResScaleDown = 1 << 6,
VolumeUp = 1 << 7, VolumeUp = 1 << 7,
VolumeDown = 1 << 8 VolumeDown = 1 << 8,
} }
private KeyboardHotkeyState GetHotkeyState() private KeyboardHotkeyState GetHotkeyState()

View file

@ -9,8 +9,8 @@ namespace Ryujinx.Ui
{ {
class SPBOpenGLContext : IOpenGLContext class SPBOpenGLContext : IOpenGLContext
{ {
private OpenGLContextBase _context; private readonly OpenGLContextBase _context;
private NativeWindowBase _window; private readonly NativeWindowBase _window;
private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window) private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window)
{ {

View file

@ -4,8 +4,8 @@ namespace Ryujinx.Ui
{ {
public class StatusUpdatedEventArgs : EventArgs public class StatusUpdatedEventArgs : EventArgs
{ {
public bool VSyncEnabled; public bool VSyncEnabled;
public float Volume; public float Volume;
public string DockedMode; public string DockedMode;
public string AspectRatio; public string AspectRatio;
public string GameStatus; public string GameStatus;
@ -16,13 +16,13 @@ namespace Ryujinx.Ui
public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName)
{ {
VSyncEnabled = vSyncEnabled; VSyncEnabled = vSyncEnabled;
Volume = volume; Volume = volume;
GpuBackend = gpuBackend; GpuBackend = gpuBackend;
DockedMode = dockedMode; DockedMode = dockedMode;
AspectRatio = aspectRatio; AspectRatio = aspectRatio;
GameStatus = gameStatus; GameStatus = gameStatus;
FifoStatus = fifoStatus; FifoStatus = fifoStatus;
GpuName = gpuName; GpuName = gpuName;
} }
} }
} }

View file

@ -12,12 +12,12 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Ui namespace Ryujinx.Ui
{ {
public partial class VKRenderer : RendererWidgetBase public partial class VulkanRenderer : RendererWidgetBase
{ {
public NativeWindowBase NativeWindow { get; private set; } public NativeWindowBase NativeWindow { get; private set; }
private UpdateBoundsCallbackDelegate _updateBoundsCallback; private UpdateBoundsCallbackDelegate _updateBoundsCallback;
public VKRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { } public VulkanRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { }
private NativeWindowBase RetrieveNativeWindow() private NativeWindowBase RetrieveNativeWindow()
{ {

View file

@ -12,12 +12,12 @@ namespace Ryujinx.Ui.Widgets
private MenuItem _manageCheatMenuItem; private MenuItem _manageCheatMenuItem;
private MenuItem _openTitleModDirMenuItem; private MenuItem _openTitleModDirMenuItem;
private MenuItem _openTitleSdModDirMenuItem; private MenuItem _openTitleSdModDirMenuItem;
private Menu _extractSubMenu; private Menu _extractSubMenu;
private MenuItem _extractMenuItem; private MenuItem _extractMenuItem;
private MenuItem _extractRomFsMenuItem; private MenuItem _extractRomFsMenuItem;
private MenuItem _extractExeFsMenuItem; private MenuItem _extractExeFsMenuItem;
private MenuItem _extractLogoMenuItem; private MenuItem _extractLogoMenuItem;
private Menu _manageSubMenu; private Menu _manageSubMenu;
private MenuItem _manageCacheMenuItem; private MenuItem _manageCacheMenuItem;
private MenuItem _purgePtcCacheMenuItem; private MenuItem _purgePtcCacheMenuItem;
private MenuItem _purgeShaderCacheMenuItem; private MenuItem _purgeShaderCacheMenuItem;
@ -31,7 +31,7 @@ namespace Ryujinx.Ui.Widgets
// //
_openSaveUserDirMenuItem = new MenuItem("Open User Save Directory") _openSaveUserDirMenuItem = new MenuItem("Open User Save Directory")
{ {
TooltipText = "Open the directory which contains Application's User Saves." TooltipText = "Open the directory which contains Application's User Saves.",
}; };
_openSaveUserDirMenuItem.Activated += OpenSaveUserDir_Clicked; _openSaveUserDirMenuItem.Activated += OpenSaveUserDir_Clicked;
@ -40,7 +40,7 @@ namespace Ryujinx.Ui.Widgets
// //
_openSaveDeviceDirMenuItem = new MenuItem("Open Device Save Directory") _openSaveDeviceDirMenuItem = new MenuItem("Open Device Save Directory")
{ {
TooltipText = "Open the directory which contains Application's Device Saves." TooltipText = "Open the directory which contains Application's Device Saves.",
}; };
_openSaveDeviceDirMenuItem.Activated += OpenSaveDeviceDir_Clicked; _openSaveDeviceDirMenuItem.Activated += OpenSaveDeviceDir_Clicked;
@ -49,7 +49,7 @@ namespace Ryujinx.Ui.Widgets
// //
_openSaveBcatDirMenuItem = new MenuItem("Open BCAT Save Directory") _openSaveBcatDirMenuItem = new MenuItem("Open BCAT Save Directory")
{ {
TooltipText = "Open the directory which contains Application's BCAT Saves." TooltipText = "Open the directory which contains Application's BCAT Saves.",
}; };
_openSaveBcatDirMenuItem.Activated += OpenSaveBcatDir_Clicked; _openSaveBcatDirMenuItem.Activated += OpenSaveBcatDir_Clicked;
@ -58,7 +58,7 @@ namespace Ryujinx.Ui.Widgets
// //
_manageTitleUpdatesMenuItem = new MenuItem("Manage Title Updates") _manageTitleUpdatesMenuItem = new MenuItem("Manage Title Updates")
{ {
TooltipText = "Open the Title Update management window" TooltipText = "Open the Title Update management window",
}; };
_manageTitleUpdatesMenuItem.Activated += ManageTitleUpdates_Clicked; _manageTitleUpdatesMenuItem.Activated += ManageTitleUpdates_Clicked;
@ -67,7 +67,7 @@ namespace Ryujinx.Ui.Widgets
// //
_manageDlcMenuItem = new MenuItem("Manage DLC") _manageDlcMenuItem = new MenuItem("Manage DLC")
{ {
TooltipText = "Open the DLC management window" TooltipText = "Open the DLC management window",
}; };
_manageDlcMenuItem.Activated += ManageDlc_Clicked; _manageDlcMenuItem.Activated += ManageDlc_Clicked;
@ -76,7 +76,7 @@ namespace Ryujinx.Ui.Widgets
// //
_manageCheatMenuItem = new MenuItem("Manage Cheats") _manageCheatMenuItem = new MenuItem("Manage Cheats")
{ {
TooltipText = "Open the Cheat management window" TooltipText = "Open the Cheat management window",
}; };
_manageCheatMenuItem.Activated += ManageCheats_Clicked; _manageCheatMenuItem.Activated += ManageCheats_Clicked;
@ -85,7 +85,7 @@ namespace Ryujinx.Ui.Widgets
// //
_openTitleModDirMenuItem = new MenuItem("Open Mods Directory") _openTitleModDirMenuItem = new MenuItem("Open Mods Directory")
{ {
TooltipText = "Open the directory which contains Application's Mods." TooltipText = "Open the directory which contains Application's Mods.",
}; };
_openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked; _openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked;
@ -94,7 +94,7 @@ namespace Ryujinx.Ui.Widgets
// //
_openTitleSdModDirMenuItem = new MenuItem("Open Atmosphere Mods Directory") _openTitleSdModDirMenuItem = new MenuItem("Open Atmosphere Mods Directory")
{ {
TooltipText = "Open the alternative SD card atmosphere directory which contains the Application's Mods." TooltipText = "Open the alternative SD card atmosphere directory which contains the Application's Mods.",
}; };
_openTitleSdModDirMenuItem.Activated += OpenTitleSdModDir_Clicked; _openTitleSdModDirMenuItem.Activated += OpenTitleSdModDir_Clicked;
@ -116,7 +116,7 @@ namespace Ryujinx.Ui.Widgets
// //
_extractRomFsMenuItem = new MenuItem("RomFS") _extractRomFsMenuItem = new MenuItem("RomFS")
{ {
TooltipText = "Extract the RomFS section from Application's current config (including updates)." TooltipText = "Extract the RomFS section from Application's current config (including updates).",
}; };
_extractRomFsMenuItem.Activated += ExtractRomFs_Clicked; _extractRomFsMenuItem.Activated += ExtractRomFs_Clicked;
@ -125,7 +125,7 @@ namespace Ryujinx.Ui.Widgets
// //
_extractExeFsMenuItem = new MenuItem("ExeFS") _extractExeFsMenuItem = new MenuItem("ExeFS")
{ {
TooltipText = "Extract the ExeFS section from Application's current config (including updates)." TooltipText = "Extract the ExeFS section from Application's current config (including updates).",
}; };
_extractExeFsMenuItem.Activated += ExtractExeFs_Clicked; _extractExeFsMenuItem.Activated += ExtractExeFs_Clicked;
@ -134,7 +134,7 @@ namespace Ryujinx.Ui.Widgets
// //
_extractLogoMenuItem = new MenuItem("Logo") _extractLogoMenuItem = new MenuItem("Logo")
{ {
TooltipText = "Extract the Logo section from Application's current config (including updates)." TooltipText = "Extract the Logo section from Application's current config (including updates).",
}; };
_extractLogoMenuItem.Activated += ExtractLogo_Clicked; _extractLogoMenuItem.Activated += ExtractLogo_Clicked;
@ -148,7 +148,7 @@ namespace Ryujinx.Ui.Widgets
// //
_manageCacheMenuItem = new MenuItem("Cache Management") _manageCacheMenuItem = new MenuItem("Cache Management")
{ {
Submenu = _manageSubMenu Submenu = _manageSubMenu,
}; };
// //
@ -156,7 +156,7 @@ namespace Ryujinx.Ui.Widgets
// //
_purgePtcCacheMenuItem = new MenuItem("Queue PPTC Rebuild") _purgePtcCacheMenuItem = new MenuItem("Queue PPTC Rebuild")
{ {
TooltipText = "Trigger PPTC to rebuild at boot time on the next game launch." TooltipText = "Trigger PPTC to rebuild at boot time on the next game launch.",
}; };
_purgePtcCacheMenuItem.Activated += PurgePtcCache_Clicked; _purgePtcCacheMenuItem.Activated += PurgePtcCache_Clicked;
@ -165,7 +165,7 @@ namespace Ryujinx.Ui.Widgets
// //
_purgeShaderCacheMenuItem = new MenuItem("Purge Shader Cache") _purgeShaderCacheMenuItem = new MenuItem("Purge Shader Cache")
{ {
TooltipText = "Delete the Application's shader cache." TooltipText = "Delete the Application's shader cache.",
}; };
_purgeShaderCacheMenuItem.Activated += PurgeShaderCache_Clicked; _purgeShaderCacheMenuItem.Activated += PurgeShaderCache_Clicked;
@ -174,7 +174,7 @@ namespace Ryujinx.Ui.Widgets
// //
_openPtcDirMenuItem = new MenuItem("Open PPTC Directory") _openPtcDirMenuItem = new MenuItem("Open PPTC Directory")
{ {
TooltipText = "Open the directory which contains the Application's PPTC cache." TooltipText = "Open the directory which contains the Application's PPTC cache.",
}; };
_openPtcDirMenuItem.Activated += OpenPtcDir_Clicked; _openPtcDirMenuItem.Activated += OpenPtcDir_Clicked;
@ -183,7 +183,7 @@ namespace Ryujinx.Ui.Widgets
// //
_openShaderCacheDirMenuItem = new MenuItem("Open Shader Cache Directory") _openShaderCacheDirMenuItem = new MenuItem("Open Shader Cache Directory")
{ {
TooltipText = "Open the directory which contains the Application's shader cache." TooltipText = "Open the directory which contains the Application's shader cache.",
}; };
_openShaderCacheDirMenuItem.Activated += OpenShaderCacheDir_Clicked; _openShaderCacheDirMenuItem.Activated += OpenShaderCacheDir_Clicked;

View file

@ -31,19 +31,19 @@ namespace Ryujinx.Ui.Widgets
{ {
public partial class GameTableContextMenu : Menu public partial class GameTableContextMenu : Menu
{ {
private readonly MainWindow _parent; private readonly MainWindow _parent;
private readonly VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private readonly AccountManager _accountManager; private readonly AccountManager _accountManager;
private readonly HorizonClient _horizonClient; private readonly HorizonClient _horizonClient;
private readonly BlitStruct<ApplicationControlProperty> _controlData; private readonly BlitStruct<ApplicationControlProperty> _controlData;
private readonly string _titleFilePath; private readonly string _titleFilePath;
private readonly string _titleName; private readonly string _titleName;
private readonly string _titleIdText; private readonly string _titleIdText;
private readonly ulong _titleId; private readonly ulong _titleId;
private MessageDialog _dialog; private MessageDialog _dialog;
private bool _cancel; private bool _cancel;
public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData) public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData)
{ {
@ -52,12 +52,12 @@ namespace Ryujinx.Ui.Widgets
InitializeComponent(); InitializeComponent();
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
_accountManager = accountManager; _accountManager = accountManager;
_horizonClient = horizonClient; _horizonClient = horizonClient;
_titleFilePath = titleFilePath; _titleFilePath = titleFilePath;
_titleName = titleName; _titleName = titleName;
_titleIdText = titleId; _titleIdText = titleId;
_controlData = controlData; _controlData = controlData;
if (!ulong.TryParse(_titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _titleId)) if (!ulong.TryParse(_titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _titleId))
{ {
@ -66,16 +66,16 @@ namespace Ryujinx.Ui.Widgets
return; return;
} }
_openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0; _openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0;
_openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0; _openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0;
_openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0; _openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0;
string fileExt = System.IO.Path.GetExtension(_titleFilePath).ToLower(); string fileExt = System.IO.Path.GetExtension(_titleFilePath).ToLower();
bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci"; bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci";
_extractRomFsMenuItem.Sensitive = hasNca; _extractRomFsMenuItem.Sensitive = hasNca;
_extractExeFsMenuItem.Sensitive = hasNca; _extractExeFsMenuItem.Sensitive = hasNca;
_extractLogoMenuItem.Sensitive = hasNca; _extractLogoMenuItem.Sensitive = hasNca;
PopupAtPointer(null); PopupAtPointer(null);
} }
@ -99,13 +99,13 @@ namespace Ryujinx.Ui.Widgets
control = ref new BlitStruct<ApplicationControlProperty>(1).Value; control = ref new BlitStruct<ApplicationControlProperty>(1).Value;
// The set sizes don't actually matter as long as they're non-zero because we use directory savedata. // The set sizes don't actually matter as long as they're non-zero because we use directory savedata.
control.UserAccountSaveDataSize = 0x4000; control.UserAccountSaveDataSize = 0x4000;
control.UserAccountSaveDataJournalSize = 0x4000; control.UserAccountSaveDataJournalSize = 0x4000;
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."); 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.");
} }
Uid user = new Uid((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user); result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user);
@ -148,7 +148,7 @@ namespace Ryujinx.Ui.Widgets
} }
string committedPath = System.IO.Path.Combine(saveRootPath, "0"); string committedPath = System.IO.Path.Combine(saveRootPath, "0");
string workingPath = System.IO.Path.Combine(saveRootPath, "1"); string workingPath = System.IO.Path.Combine(saveRootPath, "1");
// If the committed directory exists, that path will be loaded the next time the savedata is mounted // If the committed directory exists, that path will be loaded the next time the savedata is mounted
if (Directory.Exists(committedPath)) if (Directory.Exists(committedPath))
@ -170,25 +170,25 @@ namespace Ryujinx.Ui.Widgets
private void ExtractSection(NcaSectionType ncaSectionType, int programIndex = 0) private void ExtractSection(NcaSectionType ncaSectionType, int programIndex = 0)
{ {
FileChooserNative fileChooser = new FileChooserNative("Choose the folder to extract into", _parent, FileChooserAction.SelectFolder, "Extract", "Cancel"); FileChooserNative fileChooser = new("Choose the folder to extract into", _parent, FileChooserAction.SelectFolder, "Extract", "Cancel");
ResponseType response = (ResponseType)fileChooser.Run(); ResponseType response = (ResponseType)fileChooser.Run();
string destination = fileChooser.Filename; string destination = fileChooser.Filename;
fileChooser.Dispose(); fileChooser.Dispose();
if (response == ResponseType.Accept) if (response == ResponseType.Accept)
{ {
Thread extractorThread = new Thread(() => Thread extractorThread = new(() =>
{ {
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.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"), Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"),
SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...", SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...",
WindowPosition = WindowPosition.Center WindowPosition = WindowPosition.Center,
}; };
int dialogResponse = _dialog.Run(); int dialogResponse = _dialog.Run();
@ -199,139 +199,140 @@ namespace Ryujinx.Ui.Widgets
} }
}); });
using (FileStream file = new FileStream(_titleFilePath, FileMode.Open, FileAccess.Read)) using FileStream file = new(_titleFilePath, FileMode.Open, FileAccess.Read);
Nca mainNca = null;
Nca patchNca = null;
if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") ||
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") ||
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci"))
{ {
Nca mainNca = null; PartitionFileSystem pfs;
Nca patchNca = null;
if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") || if (System.IO.Path.GetExtension(_titleFilePath) == ".xci")
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") ||
(System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci"))
{ {
PartitionFileSystem pfs; Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
if (System.IO.Path.GetExtension(_titleFilePath) == ".xci") pfs = xci.OpenPartition(XciPartitionType.Secure);
}
else
{
pfs = new PartitionFileSystem(file.AsStorage());
}
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Release().AsStorage());
if (nca.Header.ContentType == NcaContentType.Program)
{ {
Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()); int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
pfs = xci.OpenPartition(XciPartitionType.Secure); if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
}
else
{
pfs = new PartitionFileSystem(file.AsStorage());
}
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Release().AsStorage());
if (nca.Header.ContentType == NcaContentType.Program)
{ {
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); patchNca = nca;
}
if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) else
{ {
patchNca = nca; mainNca = nca;
}
else
{
mainNca = nca;
}
} }
} }
} }
else if (System.IO.Path.GetExtension(_titleFilePath).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());
}
if (mainNca == null) if (mainNca == null)
{ {
Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA is not present in the selected file."); Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA is not present in the selected file.");
Gtk.Application.Invoke(delegate Gtk.Application.Invoke(delegate
{ {
GtkDialog.CreateErrorDialog("Extraction failure. The main NCA is not present in the selected file."); GtkDialog.CreateErrorDialog("Extraction failure. The main NCA is not present in the selected file.");
}); });
return; return;
} }
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _); (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
if (updatePatchNca != null) if (updatePatchNca != null)
{ {
patchNca = updatePatchNca; patchNca = updatePatchNca;
} }
int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType);
bool sectionExistsInPatch = false; bool sectionExistsInPatch = false;
if (patchNca != null)
{
sectionExistsInPatch = patchNca.CanOpenSection(index);
}
IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) if (patchNca != null)
{
sectionExistsInPatch = patchNca.CanOpenSection(index);
}
IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
FileSystemClient fsClient = _horizonClient.Fs; FileSystemClient fsClient = _horizonClient.Fs;
string source = DateTime.Now.ToFileTime().ToString()[10..]; string source = DateTime.Now.ToFileTime().ToString()[10..];
string output = DateTime.Now.ToFileTime().ToString()[10..]; string output = DateTime.Now.ToFileTime().ToString()[10..];
using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem); using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination)); using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
(Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/"); (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/");
if (!canceled) if (!canceled)
{
if (resultCode.Value.IsFailure())
{ {
if (resultCode.Value.IsFailure()) Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}");
{
Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}");
Gtk.Application.Invoke(delegate Gtk.Application.Invoke(delegate
{ {
_dialog?.Dispose(); _dialog?.Dispose();
GtkDialog.CreateErrorDialog("Extraction failed. Read the log file for further information."); GtkDialog.CreateErrorDialog("Extraction failed. Read the log file for further information.");
}); });
} }
else if (resultCode.Value.IsSuccess()) else if (resultCode.Value.IsSuccess())
{ {
Gtk.Application.Invoke(delegate Gtk.Application.Invoke(delegate
{ {
_dialog?.Dispose(); _dialog?.Dispose();
MessageDialog dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) MessageDialog dialog = new(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null)
{ {
Title = "Ryujinx - NCA Section Extractor", Title = "Ryujinx - NCA Section Extractor",
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"), Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"),
SecondaryText = "Extraction completed successfully.", SecondaryText = "Extraction completed successfully.",
WindowPosition = WindowPosition.Center WindowPosition = WindowPosition.Center,
}; };
dialog.Run(); dialog.Run();
dialog.Dispose(); dialog.Dispose();
}); });
}
} }
fsClient.Unmount(source.ToU8Span());
fsClient.Unmount(output.ToU8Span());
} }
});
extractorThread.Name = "GUI.NcaSectionExtractorThread"; fsClient.Unmount(source.ToU8Span());
extractorThread.IsBackground = true; fsClient.Unmount(output.ToU8Span());
})
{
Name = "GUI.NcaSectionExtractorThread",
IsBackground = true,
};
extractorThread.Start(); extractorThread.Start();
} }
} }
@ -339,7 +340,10 @@ namespace Ryujinx.Ui.Widgets
private (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath) private (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath)
{ {
Result rc = fs.OpenDirectory(out DirectoryHandle sourceHandle, sourcePath.ToU8Span(), OpenDirectoryMode.All); Result rc = fs.OpenDirectory(out DirectoryHandle sourceHandle, sourcePath.ToU8Span(), OpenDirectoryMode.All);
if (rc.IsFailure()) return (rc, false); if (rc.IsFailure())
{
return (rc, false);
}
using (sourceHandle) using (sourceHandle)
{ {
@ -369,7 +373,10 @@ namespace Ryujinx.Ui.Widgets
fs.CreateOrOverwriteFile(subDstPath, entry.Size); fs.CreateOrOverwriteFile(subDstPath, entry.Size);
rc = CopyFile(fs, subSrcPath, subDstPath); rc = CopyFile(fs, subSrcPath, subDstPath);
if (rc.IsFailure()) return (rc, false); if (rc.IsFailure())
{
return (rc, false);
}
} }
} }
} }
@ -377,22 +384,31 @@ namespace Ryujinx.Ui.Widgets
return (Result.Success, false); return (Result.Success, false);
} }
public Result CopyFile(FileSystemClient fs, string sourcePath, string destPath) public static Result CopyFile(FileSystemClient fs, string sourcePath, string destPath)
{ {
Result rc = fs.OpenFile(out FileHandle sourceHandle, sourcePath.ToU8Span(), OpenMode.Read); Result rc = fs.OpenFile(out FileHandle sourceHandle, sourcePath.ToU8Span(), OpenMode.Read);
if (rc.IsFailure()) return rc; if (rc.IsFailure())
{
return rc;
}
using (sourceHandle) using (sourceHandle)
{ {
rc = fs.OpenFile(out FileHandle destHandle, destPath.ToU8Span(), OpenMode.Write | OpenMode.AllowAppend); rc = fs.OpenFile(out FileHandle destHandle, destPath.ToU8Span(), OpenMode.Write | OpenMode.AllowAppend);
if (rc.IsFailure()) return rc; if (rc.IsFailure())
{
return rc;
}
using (destHandle) using (destHandle)
{ {
const int MaxBufferSize = 1024 * 1024; const int MaxBufferSize = 1024 * 1024;
rc = fs.GetFileSize(out long fileSize, sourceHandle); rc = fs.GetFileSize(out long fileSize, sourceHandle);
if (rc.IsFailure()) return rc; if (rc.IsFailure())
{
return rc;
}
int bufferSize = (int)Math.Min(MaxBufferSize, fileSize); int bufferSize = (int)Math.Min(MaxBufferSize, fileSize);
@ -405,10 +421,16 @@ namespace Ryujinx.Ui.Widgets
Span<byte> buf = buffer.AsSpan(0, toRead); Span<byte> buf = buffer.AsSpan(0, toRead);
rc = fs.ReadFile(out long _, sourceHandle, offset, buf); rc = fs.ReadFile(out long _, sourceHandle, offset, buf);
if (rc.IsFailure()) return rc; if (rc.IsFailure())
{
return rc;
}
rc = fs.WriteFile(destHandle, offset, buf, WriteOption.None); rc = fs.WriteFile(destHandle, offset, buf, WriteOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure())
{
return rc;
}
} }
} }
finally finally
@ -417,7 +439,10 @@ namespace Ryujinx.Ui.Widgets
} }
rc = fs.FlushFile(destHandle); rc = fs.FlushFile(destHandle);
if (rc.IsFailure()) return rc; if (rc.IsFailure())
{
return rc;
}
} }
} }
@ -466,7 +491,7 @@ namespace Ryujinx.Ui.Widgets
private void OpenTitleModDir_Clicked(object sender, EventArgs args) private void OpenTitleModDir_Clicked(object sender, EventArgs args)
{ {
string modsBasePath = ModLoader.GetModsBasePath(); string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText); string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText);
OpenHelper.OpenFolder(titleModsPath); OpenHelper.OpenFolder(titleModsPath);
@ -474,8 +499,8 @@ namespace Ryujinx.Ui.Widgets
private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) private void OpenTitleSdModDir_Clicked(object sender, EventArgs args)
{ {
string sdModsBasePath = ModLoader.GetSdModsBasePath(); string sdModsBasePath = ModLoader.GetSdModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText); string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText);
OpenHelper.OpenFolder(titleModsPath); OpenHelper.OpenFolder(titleModsPath);
} }
@ -497,9 +522,9 @@ namespace Ryujinx.Ui.Widgets
private void OpenPtcDir_Clicked(object sender, EventArgs args) private void OpenPtcDir_Clicked(object sender, EventArgs args)
{ {
string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu"); string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "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");
if (!Directory.Exists(ptcDir)) if (!Directory.Exists(ptcDir))
@ -526,12 +551,12 @@ namespace Ryujinx.Ui.Widgets
private void PurgePtcCache_Clicked(object sender, EventArgs args) private void PurgePtcCache_Clicked(object sender, EventArgs args)
{ {
DirectoryInfo mainDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0")); DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0"));
DirectoryInfo backupDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1")); DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1"));
MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?"); MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
List<FileInfo> cacheFiles = new List<FileInfo>(); List<FileInfo> cacheFiles = new();
if (mainDir.Exists) if (mainDir.Exists)
{ {
@ -551,7 +576,7 @@ namespace Ryujinx.Ui.Widgets
{ {
file.Delete(); file.Delete();
} }
catch(Exception e) catch (Exception e)
{ {
GtkDialog.CreateErrorDialog($"Error purging PPTC cache {file.Name}: {e}"); GtkDialog.CreateErrorDialog($"Error purging PPTC cache {file.Name}: {e}");
} }
@ -563,12 +588,12 @@ namespace Ryujinx.Ui.Widgets
private void PurgeShaderCache_Clicked(object sender, EventArgs args) private void PurgeShaderCache_Clicked(object sender, EventArgs args)
{ {
DirectoryInfo shaderCacheDir = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader")); DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader"));
using 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?"); using 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?");
List<DirectoryInfo> oldCacheDirectories = new List<DirectoryInfo>(); List<DirectoryInfo> oldCacheDirectories = new();
List<FileInfo> newCacheFiles = new List<FileInfo>(); List<FileInfo> newCacheFiles = new();
if (shaderCacheDir.Exists) if (shaderCacheDir.Exists)
{ {

View file

@ -13,11 +13,11 @@ namespace Ryujinx.Ui.Widgets
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.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
Text = mainText; Text = mainText;
SecondaryText = secondaryText; SecondaryText = secondaryText;
WindowPosition = WindowPosition.Center; WindowPosition = WindowPosition.Center;
SecondaryUseMarkup = true; SecondaryUseMarkup = true;
Response += GtkDialog_Response; Response += GtkDialog_Response;
@ -80,7 +80,7 @@ namespace Ryujinx.Ui.Widgets
internal static ResponseType CreateCustomDialog(string title, string mainText, string secondaryText, Dictionary<int, string> buttons, MessageType messageType = MessageType.Other) internal static ResponseType CreateCustomDialog(string title, string mainText, string secondaryText, Dictionary<int, string> buttons, MessageType messageType = MessageType.Other)
{ {
GtkDialog gtkDialog = new GtkDialog(title, mainText, secondaryText, messageType, ButtonsType.None); GtkDialog gtkDialog = new(title, mainText, secondaryText, messageType, ButtonsType.None);
foreach (var button in buttons) foreach (var button in buttons)
{ {
@ -92,9 +92,9 @@ namespace Ryujinx.Ui.Widgets
internal static string CreateInputDialog(Window parent, string title, string mainText, uint inputMax) internal static string CreateInputDialog(Window parent, string title, string mainText, uint inputMax)
{ {
GtkInputDialog gtkDialog = new GtkInputDialog(parent, title, mainText, inputMax); GtkInputDialog gtkDialog = new(parent, title, mainText, inputMax);
ResponseType response = (ResponseType)gtkDialog.Run(); ResponseType response = (ResponseType)gtkDialog.Run();
string responseText = gtkDialog.InputEntry.Text.TrimEnd(); string responseText = gtkDialog.InputEntry.Text.TrimEnd();
gtkDialog.Dispose(); gtkDialog.Dispose();

View file

@ -12,23 +12,23 @@ namespace Ryujinx.Ui.Widgets
Title = title; Title = title;
Label mainTextLabel = new Label Label mainTextLabel = new()
{ {
Text = mainText Text = mainText,
}; };
InputEntry = new Entry InputEntry = new Entry
{ {
MaxLength = (int)inputMax MaxLength = (int)inputMax,
}; };
Label inputMaxTextLabel = new Label Label inputMaxTextLabel = new()
{ {
Text = $"(Max length: {inputMax})" Text = $"(Max length: {inputMax})",
}; };
((Box)MessageArea).PackStart(mainTextLabel, true, true, 0); ((Box)MessageArea).PackStart(mainTextLabel, true, true, 0);
((Box)MessageArea).PackStart(InputEntry, true, true, 5); ((Box)MessageArea).PackStart(InputEntry, true, true, 5);
((Box)MessageArea).PackStart(inputMaxTextLabel, true, true, 0); ((Box)MessageArea).PackStart(inputMaxTextLabel, true, true, 0);
ShowAll(); ShowAll();

View file

@ -10,7 +10,7 @@ namespace Ryujinx.Ui.Widgets
{ {
public string FileName { get; private set; } public string FileName { get; private set; }
#pragma warning disable CS0649, IDE0044 #pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier
[GUI] Entry _profileEntry; [GUI] Entry _profileEntry;
[GUI] Label _errorMessage; [GUI] Label _errorMessage;
#pragma warning restore CS0649, IDE0044 #pragma warning restore CS0649, IDE0044

View file

@ -6,9 +6,9 @@ namespace Ryujinx.Ui.Widgets
{ {
internal class UserErrorDialog : MessageDialog internal class UserErrorDialog : MessageDialog
{ {
private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"; private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide";
private const int OkResponseId = 0; private const int OkResponseId = 0;
private const int SetupGuideResponseId = 1; private const int SetupGuideResponseId = 1;
private readonly UserError _userError; private readonly UserError _userError;
@ -16,7 +16,7 @@ namespace Ryujinx.Ui.Widgets
{ {
_userError = error; _userError = error;
WindowPosition = WindowPosition.Center; WindowPosition = WindowPosition.Center;
SecondaryUseMarkup = true; SecondaryUseMarkup = true;
Response += UserErrorDialog_Response; Response += UserErrorDialog_Response;
@ -36,8 +36,8 @@ namespace Ryujinx.Ui.Widgets
SecondaryUseMarkup = true; SecondaryUseMarkup = true;
Title = $"Ryujinx error ({errorCode})"; Title = $"Ryujinx error ({errorCode})";
Text = $"{errorCode}: {GetErrorTitle(error)}"; Text = $"{errorCode}: {GetErrorTitle(error)}";
SecondaryText = GetErrorDescription(error); SecondaryText = GetErrorDescription(error);
if (isInSetupGuide) if (isInSetupGuide)
@ -46,34 +46,34 @@ namespace Ryujinx.Ui.Widgets
} }
} }
private string GetErrorCode(UserError error) private static string GetErrorCode(UserError error)
{ {
return $"RYU-{(uint)error:X4}"; return $"RYU-{(uint)error:X4}";
} }
private string GetErrorTitle(UserError error) private static string GetErrorTitle(UserError error)
{ {
return error switch return error switch
{ {
UserError.NoKeys => "Keys not found", UserError.NoKeys => "Keys not found",
UserError.NoFirmware => "Firmware not found", UserError.NoFirmware => "Firmware not found",
UserError.FirmwareParsingFailed => "Firmware parsing error", UserError.FirmwareParsingFailed => "Firmware parsing error",
UserError.ApplicationNotFound => "Application not found", UserError.ApplicationNotFound => "Application not found",
UserError.Unknown => "Unknown error", UserError.Unknown => "Unknown error",
_ => "Undefined error", _ => "Undefined error",
}; };
} }
private string GetErrorDescription(UserError error) private static string GetErrorDescription(UserError error)
{ {
return error switch return error switch
{ {
UserError.NoKeys => "Ryujinx was unable to find your 'prod.keys' file", UserError.NoKeys => "Ryujinx was unable to find your 'prod.keys' file",
UserError.NoFirmware => "Ryujinx was unable to find any firmwares installed", 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.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.ApplicationNotFound => "Ryujinx couldn't find a valid application at the given path.",
UserError.Unknown => "An unknown error occured!", UserError.Unknown => "An unknown error occured!",
_ => "An undefined error occured! This shouldn't happen, please contact a dev!", _ => "An undefined error occured! This shouldn't happen, please contact a dev!",
}; };
} }
@ -84,7 +84,7 @@ namespace Ryujinx.Ui.Widgets
UserError.NoKeys or UserError.NoKeys or
UserError.NoFirmware or UserError.NoFirmware or
UserError.FirmwareParsingFailed => true, UserError.FirmwareParsingFailed => true,
_ => false, _ => false,
}; };
} }
@ -97,9 +97,9 @@ namespace Ryujinx.Ui.Widgets
return error switch return error switch
{ {
UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys", UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys",
UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware", UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware",
_ => SetupGuideUrl, _ => SetupGuideUrl,
}; };
} }

View file

@ -7,65 +7,63 @@ namespace Ryujinx.Ui.Windows
{ {
public partial class AboutWindow : Window public partial class AboutWindow : Window
{ {
private Box _mainBox; private Box _mainBox;
private Box _leftBox; private Box _leftBox;
private Box _logoBox; private Box _logoBox;
private Image _ryujinxLogo; private Image _ryujinxLogo;
private Box _logoTextBox; private Box _logoTextBox;
private Label _ryujinxLabel; private Label _ryujinxLabel;
private Label _ryujinxPhoneticLabel; private Label _ryujinxPhoneticLabel;
private EventBox _ryujinxLink; private EventBox _ryujinxLink;
private Label _ryujinxLinkLabel; private Label _ryujinxLinkLabel;
private Label _versionLabel; private Label _versionLabel;
private Label _disclaimerLabel; private Label _disclaimerLabel;
private EventBox _amiiboApiLink; private EventBox _amiiboApiLink;
private Label _amiiboApiLinkLabel; private Label _amiiboApiLinkLabel;
private Box _socialBox; private Box _socialBox;
private EventBox _patreonEventBox; private EventBox _patreonEventBox;
private Box _patreonBox; private Box _patreonBox;
private Image _patreonLogo; private Image _patreonLogo;
private Label _patreonLabel; private Label _patreonLabel;
private EventBox _githubEventBox; private EventBox _githubEventBox;
private Box _githubBox; private Box _githubBox;
private Image _githubLogo; private Image _githubLogo;
private Label _githubLabel; private Label _githubLabel;
private Box _discordBox; private Box _discordBox;
private EventBox _discordEventBox; private EventBox _discordEventBox;
private Image _discordLogo; private Image _discordLogo;
private Label _discordLabel; private Label _discordLabel;
private EventBox _twitterEventBox; private EventBox _twitterEventBox;
private Box _twitterBox; private Box _twitterBox;
private Image _twitterLogo; private Image _twitterLogo;
private Label _twitterLabel; private Label _twitterLabel;
private Separator _separator; private Separator _separator;
private Box _rightBox; private Box _rightBox;
private Label _aboutLabel; private Label _aboutLabel;
private Label _aboutDescriptionLabel; private Label _aboutDescriptionLabel;
private Label _createdByLabel; private Label _createdByLabel;
private TextView _createdByText; private TextView _createdByText;
private EventBox _contributorsEventBox; private EventBox _contributorsEventBox;
private Label _contributorsLinkLabel; private Label _contributorsLinkLabel;
private Label _patreonNamesLabel; private Label _patreonNamesLabel;
private ScrolledWindow _patreonNamesScrolled; private ScrolledWindow _patreonNamesScrolled;
private TextView _patreonNamesText; private TextView _patreonNamesText;
private EventBox _changelogEventBox; private EventBox _changelogEventBox;
private Label _changelogLinkLabel; private Label _changelogLinkLabel;
private void InitializeComponent() private void InitializeComponent()
{ {
#pragma warning disable CS0612
// //
// AboutWindow // AboutWindow
// //
CanFocus = false; CanFocus = false;
Resizable = false; Resizable = false;
Modal = true; Modal = true;
WindowPosition = WindowPosition.Center; WindowPosition = WindowPosition.Center;
DefaultWidth = 800; DefaultWidth = 800;
DefaultHeight = 450; DefaultHeight = 450;
TypeHint = Gdk.WindowTypeHint.Dialog; TypeHint = Gdk.WindowTypeHint.Dialog;
// //
// _mainBox // _mainBox
@ -77,9 +75,9 @@ namespace Ryujinx.Ui.Windows
// //
_leftBox = new Box(Orientation.Vertical, 0) _leftBox = new Box(Orientation.Vertical, 0)
{ {
Margin = 15, Margin = 15,
MarginLeft = 30, MarginStart = 30,
MarginRight = 0 MarginEnd = 0,
}; };
// //
@ -92,8 +90,8 @@ namespace Ryujinx.Ui.Windows
// //
_ryujinxLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png", 100, 100)) _ryujinxLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png", 100, 100))
{ {
Margin = 10, Margin = 10,
MarginLeft = 15 MarginStart = 15,
}; };
// //
@ -106,9 +104,9 @@ namespace Ryujinx.Ui.Windows
// //
_ryujinxLabel = new Label("Ryujinx") _ryujinxLabel = new Label("Ryujinx")
{ {
MarginTop = 15, MarginTop = 15,
Justify = Justification.Center, Justify = Justification.Center,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_ryujinxLabel.Attributes.Insert(new Pango.AttrScale(2.7f)); _ryujinxLabel.Attributes.Insert(new Pango.AttrScale(2.7f));
@ -117,7 +115,7 @@ namespace Ryujinx.Ui.Windows
// //
_ryujinxPhoneticLabel = new Label("(REE-YOU-JINX)") _ryujinxPhoneticLabel = new Label("(REE-YOU-JINX)")
{ {
Justify = Justification.Center Justify = Justification.Center,
}; };
// //
@ -135,8 +133,8 @@ namespace Ryujinx.Ui.Windows
_ryujinxLinkLabel = new Label("www.ryujinx.org") _ryujinxLinkLabel = new Label("www.ryujinx.org")
{ {
TooltipText = "Click to open the Ryujinx website in your default browser.", TooltipText = "Click to open the Ryujinx website in your default browser.",
Justify = Justification.Center, Justify = Justification.Center,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_ryujinxLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); _ryujinxLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
@ -145,9 +143,9 @@ namespace Ryujinx.Ui.Windows
// //
_versionLabel = new Label(Program.Version) _versionLabel = new Label(Program.Version)
{ {
Expand = true, Expand = true,
Justify = Justification.Center, Justify = Justification.Center,
Margin = 5 Margin = 5,
}; };
// //
@ -163,7 +161,7 @@ namespace Ryujinx.Ui.Windows
{ {
TooltipText = "Click to open the changelog for this version in your default browser.", TooltipText = "Click to open the changelog for this version in your default browser.",
Justify = Justification.Center, Justify = Justification.Center,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_changelogLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); _changelogLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
@ -172,10 +170,10 @@ namespace Ryujinx.Ui.Windows
// //
_disclaimerLabel = new Label("Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.") _disclaimerLabel = new Label("Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.")
{ {
Expand = true, Expand = true,
Justify = Justification.Center, Justify = Justification.Center,
Margin = 5, Margin = 5,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_disclaimerLabel.Attributes.Insert(new Pango.AttrScale(0.8f)); _disclaimerLabel.Attributes.Insert(new Pango.AttrScale(0.8f));
@ -184,7 +182,7 @@ namespace Ryujinx.Ui.Windows
// //
_amiiboApiLink = new EventBox() _amiiboApiLink = new EventBox()
{ {
Margin = 5 Margin = 5,
}; };
_amiiboApiLink.ButtonPressEvent += AmiiboApiButton_Pressed; _amiiboApiLink.ButtonPressEvent += AmiiboApiButton_Pressed;
@ -194,8 +192,8 @@ namespace Ryujinx.Ui.Windows
_amiiboApiLinkLabel = new Label("AmiiboAPI (www.amiiboapi.com) is used\nin our Amiibo emulation.") _amiiboApiLinkLabel = new Label("AmiiboAPI (www.amiiboapi.com) is used\nin our Amiibo emulation.")
{ {
TooltipText = "Click to open the AmiiboAPI website in your default browser.", TooltipText = "Click to open the AmiiboAPI website in your default browser.",
Justify = Justification.Center, Justify = Justification.Center,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_amiiboApiLinkLabel.Attributes.Insert(new Pango.AttrScale(0.9f)); _amiiboApiLinkLabel.Attributes.Insert(new Pango.AttrScale(0.9f));
@ -204,8 +202,8 @@ namespace Ryujinx.Ui.Windows
// //
_socialBox = new Box(Orientation.Horizontal, 0) _socialBox = new Box(Orientation.Horizontal, 0)
{ {
Margin = 25, Margin = 25,
MarginBottom = 10 MarginBottom = 10,
}; };
// //
@ -213,7 +211,7 @@ namespace Ryujinx.Ui.Windows
// //
_patreonEventBox = new EventBox() _patreonEventBox = new EventBox()
{ {
TooltipText = "Click to open the Ryujinx Patreon page in your default browser." TooltipText = "Click to open the Ryujinx Patreon page in your default browser.",
}; };
_patreonEventBox.ButtonPressEvent += PatreonButton_Pressed; _patreonEventBox.ButtonPressEvent += PatreonButton_Pressed;
@ -227,7 +225,7 @@ namespace Ryujinx.Ui.Windows
// //
_patreonLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png", 30, 30)) _patreonLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png", 30, 30))
{ {
Margin = 10 Margin = 10,
}; };
// //
@ -235,7 +233,7 @@ namespace Ryujinx.Ui.Windows
// //
_patreonLabel = new Label("Patreon") _patreonLabel = new Label("Patreon")
{ {
Justify = Justification.Center Justify = Justification.Center,
}; };
// //
@ -243,7 +241,7 @@ namespace Ryujinx.Ui.Windows
// //
_githubEventBox = new EventBox() _githubEventBox = new EventBox()
{ {
TooltipText = "Click to open the Ryujinx GitHub page in your default browser." TooltipText = "Click to open the Ryujinx GitHub page in your default browser.",
}; };
_githubEventBox.ButtonPressEvent += GitHubButton_Pressed; _githubEventBox.ButtonPressEvent += GitHubButton_Pressed;
@ -257,7 +255,7 @@ namespace Ryujinx.Ui.Windows
// //
_githubLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png", 30, 30)) _githubLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png", 30, 30))
{ {
Margin = 10 Margin = 10,
}; };
// //
@ -265,7 +263,7 @@ namespace Ryujinx.Ui.Windows
// //
_githubLabel = new Label("GitHub") _githubLabel = new Label("GitHub")
{ {
Justify = Justification.Center Justify = Justification.Center,
}; };
// //
@ -278,7 +276,7 @@ namespace Ryujinx.Ui.Windows
// //
_discordEventBox = new EventBox() _discordEventBox = new EventBox()
{ {
TooltipText = "Click to open an invite to the Ryujinx Discord server in your default browser." TooltipText = "Click to open an invite to the Ryujinx Discord server in your default browser.",
}; };
_discordEventBox.ButtonPressEvent += DiscordButton_Pressed; _discordEventBox.ButtonPressEvent += DiscordButton_Pressed;
@ -287,7 +285,7 @@ namespace Ryujinx.Ui.Windows
// //
_discordLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png", 30, 30)) _discordLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png", 30, 30))
{ {
Margin = 10 Margin = 10,
}; };
// //
@ -295,7 +293,7 @@ namespace Ryujinx.Ui.Windows
// //
_discordLabel = new Label("Discord") _discordLabel = new Label("Discord")
{ {
Justify = Justification.Center Justify = Justification.Center,
}; };
// //
@ -303,7 +301,7 @@ namespace Ryujinx.Ui.Windows
// //
_twitterEventBox = new EventBox() _twitterEventBox = new EventBox()
{ {
TooltipText = "Click to open the Ryujinx Twitter page in your default browser." TooltipText = "Click to open the Ryujinx Twitter page in your default browser.",
}; };
_twitterEventBox.ButtonPressEvent += TwitterButton_Pressed; _twitterEventBox.ButtonPressEvent += TwitterButton_Pressed;
@ -317,7 +315,7 @@ namespace Ryujinx.Ui.Windows
// //
_twitterLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png", 30, 30)) _twitterLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png", 30, 30))
{ {
Margin = 10 Margin = 10,
}; };
// //
@ -325,7 +323,7 @@ namespace Ryujinx.Ui.Windows
// //
_twitterLabel = new Label("Twitter") _twitterLabel = new Label("Twitter")
{ {
Justify = Justification.Center Justify = Justification.Center,
}; };
// //
@ -333,7 +331,7 @@ namespace Ryujinx.Ui.Windows
// //
_separator = new Separator(Orientation.Vertical) _separator = new Separator(Orientation.Vertical)
{ {
Margin = 15 Margin = 15,
}; };
// //
@ -341,8 +339,8 @@ namespace Ryujinx.Ui.Windows
// //
_rightBox = new Box(Orientation.Vertical, 0) _rightBox = new Box(Orientation.Vertical, 0)
{ {
Margin = 15, Margin = 15,
MarginTop = 40 MarginTop = 40,
}; };
// //
@ -350,8 +348,8 @@ namespace Ryujinx.Ui.Windows
// //
_aboutLabel = new Label("About :") _aboutLabel = new Label("About :")
{ {
Halign = Align.Start, Halign = Align.Start,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_aboutLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); _aboutLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
_aboutLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); _aboutLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
@ -365,7 +363,7 @@ namespace Ryujinx.Ui.Windows
"Developers interested in contributing can find out more on our GitHub or Discord.") "Developers interested in contributing can find out more on our GitHub or Discord.")
{ {
Margin = 15, Margin = 15,
Halign = Align.Start Halign = Align.Start,
}; };
// //
@ -373,8 +371,8 @@ namespace Ryujinx.Ui.Windows
// //
_createdByLabel = new Label("Maintained by :") _createdByLabel = new Label("Maintained by :")
{ {
Halign = Align.Start, Halign = Align.Start,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_createdByLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); _createdByLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
_createdByLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); _createdByLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
@ -384,11 +382,11 @@ namespace Ryujinx.Ui.Windows
// //
_createdByText = new TextView() _createdByText = new TextView()
{ {
WrapMode = Gtk.WrapMode.Word, WrapMode = Gtk.WrapMode.Word,
Editable = false, Editable = false,
CursorVisible = false, CursorVisible = false,
Margin = 15, Margin = 15,
MarginRight = 30 MarginEnd = 30,
}; };
_createdByText.Buffer.Text = "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD« and more..."; _createdByText.Buffer.Text = "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD« and more...";
@ -404,9 +402,9 @@ namespace Ryujinx.Ui.Windows
_contributorsLinkLabel = new Label("See All Contributors...") _contributorsLinkLabel = new Label("See All Contributors...")
{ {
TooltipText = "Click to open the Contributors page in your default browser.", TooltipText = "Click to open the Contributors page in your default browser.",
MarginRight = 30, MarginEnd = 30,
Halign = Align.End, Halign = Align.End,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_contributorsLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); _contributorsLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
@ -415,8 +413,8 @@ namespace Ryujinx.Ui.Windows
// //
_patreonNamesLabel = new Label("Supported on Patreon by :") _patreonNamesLabel = new Label("Supported on Patreon by :")
{ {
Halign = Align.Start, Halign = Align.Start,
Attributes = new AttrList() Attributes = new AttrList(),
}; };
_patreonNamesLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); _patreonNamesLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
_patreonNamesLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); _patreonNamesLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single));
@ -426,10 +424,10 @@ namespace Ryujinx.Ui.Windows
// //
_patreonNamesScrolled = new ScrolledWindow() _patreonNamesScrolled = new ScrolledWindow()
{ {
Margin = 15, Margin = 15,
MarginRight = 30, MarginEnd = 30,
Expand = true, Expand = true,
ShadowType = ShadowType.In ShadowType = ShadowType.In,
}; };
_patreonNamesScrolled.SetPolicy(PolicyType.Never, PolicyType.Automatic); _patreonNamesScrolled.SetPolicy(PolicyType.Never, PolicyType.Automatic);
@ -438,13 +436,11 @@ namespace Ryujinx.Ui.Windows
// //
_patreonNamesText = new TextView() _patreonNamesText = new TextView()
{ {
WrapMode = Gtk.WrapMode.Word WrapMode = Gtk.WrapMode.Word,
}; };
_patreonNamesText.Buffer.Text = "Loading..."; _patreonNamesText.Buffer.Text = "Loading...";
_patreonNamesText.SetProperty("editable", new GLib.Value(false)); _patreonNamesText.SetProperty("editable", new GLib.Value(false));
#pragma warning restore CS0612
ShowComponent(); ShowComponent();
} }

View file

@ -25,7 +25,7 @@ namespace Ryujinx.Ui.Windows
_patreonNamesText.Buffer.Text = "Connection Error."; _patreonNamesText.Buffer.Text = "Connection Error.";
} }
HttpClient httpClient = new HttpClient(); HttpClient httpClient = new();
try try
{ {

View file

@ -4,37 +4,35 @@ namespace Ryujinx.Ui.Windows
{ {
public partial class AmiiboWindow : Window public partial class AmiiboWindow : Window
{ {
private Box _mainBox; private Box _mainBox;
private ButtonBox _buttonBox; private ButtonBox _buttonBox;
private Button _scanButton; private Button _scanButton;
private Button _cancelButton; private Button _cancelButton;
private CheckButton _randomUuidCheckBox; private CheckButton _randomUuidCheckBox;
private Box _amiiboBox; private Box _amiiboBox;
private Box _amiiboHeadBox; private Box _amiiboHeadBox;
private Box _amiiboSeriesBox; private Box _amiiboSeriesBox;
private Label _amiiboSeriesLabel; private Label _amiiboSeriesLabel;
private ComboBoxText _amiiboSeriesComboBox; private ComboBoxText _amiiboSeriesComboBox;
private Box _amiiboCharsBox; private Box _amiiboCharsBox;
private Label _amiiboCharsLabel; private Label _amiiboCharsLabel;
private ComboBoxText _amiiboCharsComboBox; private ComboBoxText _amiiboCharsComboBox;
private CheckButton _showAllCheckBox; private CheckButton _showAllCheckBox;
private Image _amiiboImage; private Image _amiiboImage;
private Label _gameUsageLabel; private Label _gameUsageLabel;
private void InitializeComponent() private void InitializeComponent()
{ {
#pragma warning disable CS0612
// //
// AmiiboWindow // AmiiboWindow
// //
CanFocus = false; CanFocus = false;
Resizable = false; Resizable = false;
Modal = true; Modal = true;
WindowPosition = WindowPosition.Center; WindowPosition = WindowPosition.Center;
DefaultWidth = 600; DefaultWidth = 600;
DefaultHeight = 470; DefaultHeight = 470;
TypeHint = Gdk.WindowTypeHint.Dialog; TypeHint = Gdk.WindowTypeHint.Dialog;
// //
// _mainBox // _mainBox
@ -46,8 +44,8 @@ namespace Ryujinx.Ui.Windows
// //
_buttonBox = new ButtonBox(Orientation.Horizontal) _buttonBox = new ButtonBox(Orientation.Horizontal)
{ {
Margin = 20, Margin = 20,
LayoutStyle = ButtonBoxStyle.End LayoutStyle = ButtonBoxStyle.End,
}; };
// //
@ -55,10 +53,10 @@ namespace Ryujinx.Ui.Windows
// //
_scanButton = new Button() _scanButton = new Button()
{ {
Label = "Scan It!", Label = "Scan It!",
CanFocus = true, CanFocus = true,
ReceivesDefault = true, ReceivesDefault = true,
MarginLeft = 10 MarginStart = 10,
}; };
_scanButton.Clicked += ScanButton_Pressed; _scanButton.Clicked += ScanButton_Pressed;
@ -67,8 +65,8 @@ namespace Ryujinx.Ui.Windows
// //
_randomUuidCheckBox = new CheckButton() _randomUuidCheckBox = new CheckButton()
{ {
Label = "Hack: Use Random Tag Uuid", Label = "Hack: Use Random Tag Uuid",
TooltipText = "This allows multiple scans of a single Amiibo.\n(used in The Legend of Zelda: Breath of the Wild)" TooltipText = "This allows multiple scans of a single Amiibo.\n(used in The Legend of Zelda: Breath of the Wild)",
}; };
// //
@ -76,10 +74,10 @@ namespace Ryujinx.Ui.Windows
// //
_cancelButton = new Button() _cancelButton = new Button()
{ {
Label = "Cancel", Label = "Cancel",
CanFocus = true, CanFocus = true,
ReceivesDefault = true, ReceivesDefault = true,
MarginLeft = 10 MarginStart = 10,
}; };
_cancelButton.Clicked += CancelButton_Pressed; _cancelButton.Clicked += CancelButton_Pressed;
@ -94,7 +92,7 @@ namespace Ryujinx.Ui.Windows
_amiiboHeadBox = new Box(Orientation.Horizontal, 0) _amiiboHeadBox = new Box(Orientation.Horizontal, 0)
{ {
Margin = 20, Margin = 20,
Hexpand = true Hexpand = true,
}; };
// //
@ -102,7 +100,7 @@ namespace Ryujinx.Ui.Windows
// //
_amiiboSeriesBox = new Box(Orientation.Horizontal, 0) _amiiboSeriesBox = new Box(Orientation.Horizontal, 0)
{ {
Hexpand = true Hexpand = true,
}; };
// //
@ -120,7 +118,7 @@ namespace Ryujinx.Ui.Windows
// //
_amiiboCharsBox = new Box(Orientation.Horizontal, 0) _amiiboCharsBox = new Box(Orientation.Horizontal, 0)
{ {
Hexpand = true Hexpand = true,
}; };
// //
@ -138,7 +136,7 @@ namespace Ryujinx.Ui.Windows
// //
_showAllCheckBox = new CheckButton() _showAllCheckBox = new CheckButton()
{ {
Label = "Show All Amiibo" Label = "Show All Amiibo",
}; };
// //
@ -147,7 +145,7 @@ namespace Ryujinx.Ui.Windows
_amiiboImage = new Image() _amiiboImage = new Image()
{ {
HeightRequest = 350, HeightRequest = 350,
WidthRequest = 350 WidthRequest = 350,
}; };
// //
@ -155,11 +153,9 @@ namespace Ryujinx.Ui.Windows
// //
_gameUsageLabel = new Label("") _gameUsageLabel = new Label("")
{ {
MarginTop = 20 MarginTop = 20,
}; };
#pragma warning restore CS0612
ShowComponent(); ShowComponent();
} }

View file

@ -14,21 +14,19 @@ using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AmiiboApi = Ryujinx.Ui.Common.Models.Amiibo.AmiiboApi;
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
namespace Ryujinx.Ui.Windows namespace Ryujinx.Ui.Windows
{ {
public partial class AmiiboWindow : Window public partial class AmiiboWindow : Window
{ {
private const string DEFAULT_JSON = "{ \"amiibo\": [] }"; private const string DefaultJson = "{ \"amiibo\": [] }";
public string AmiiboId { get; private set; } public string AmiiboId { get; private set; }
public int DeviceId { get; set; } public int DeviceId { get; set; }
public string TitleId { get; set; } public string TitleId { get; set; }
public string LastScannedAmiiboId { get; set; } public string LastScannedAmiiboId { get; set; }
public bool LastScannedAmiiboShowAll { get; set; } public bool LastScannedAmiiboShowAll { get; set; }
public ResponseType Response { get; private set; } public ResponseType Response { get; private set; }
@ -41,13 +39,13 @@ namespace Ryujinx.Ui.Windows
} }
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private readonly string _amiiboJsonPath; private readonly string _amiiboJsonPath;
private readonly byte[] _amiiboLogoBytes; private readonly byte[] _amiiboLogoBytes;
private List<AmiiboApi> _amiiboList; private List<AmiiboApi> _amiiboList;
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo") public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo")
{ {
@ -57,18 +55,18 @@ namespace Ryujinx.Ui.Windows
_httpClient = new HttpClient() _httpClient = new HttpClient()
{ {
Timeout = TimeSpan.FromSeconds(30) Timeout = TimeSpan.FromSeconds(30),
}; };
Directory.CreateDirectory(System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); Directory.CreateDirectory(System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
_amiiboJsonPath = System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json"); _amiiboJsonPath = System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
_amiiboList = new List<AmiiboApi>(); _amiiboList = new List<AmiiboApi>();
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png"); _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
_amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes); _amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes);
_scanButton.Sensitive = false; _scanButton.Sensitive = false;
_randomUuidCheckBox.Sensitive = false; _randomUuidCheckBox.Sensitive = false;
_ = LoadContentAsync(); _ = LoadContentAsync();
@ -76,13 +74,13 @@ namespace Ryujinx.Ui.Windows
private async Task LoadContentAsync() private async Task LoadContentAsync()
{ {
string amiiboJsonString = DEFAULT_JSON; string amiiboJsonString = DefaultJson;
if (File.Exists(_amiiboJsonPath)) if (File.Exists(_amiiboJsonPath))
{ {
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).LastUpdated))
{ {
amiiboJsonString = await DownloadAmiiboJson(); amiiboJsonString = await DownloadAmiiboJson();
} }
@ -103,7 +101,7 @@ namespace Ryujinx.Ui.Windows
} }
} }
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; _amiiboList = JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).Amiibo;
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
if (LastScannedAmiiboShowAll) if (LastScannedAmiiboShowAll)
@ -118,7 +116,7 @@ namespace Ryujinx.Ui.Windows
private void ParseAmiiboData() private void ParseAmiiboData()
{ {
List<string> comboxItemList = new List<string>(); List<string> comboxItemList = new();
for (int i = 0; i < _amiiboList.Count; i++) for (int i = 0; i < _amiiboList.Count; i++)
{ {
@ -149,7 +147,7 @@ namespace Ryujinx.Ui.Windows
} }
_amiiboSeriesComboBox.Changed += SeriesComboBox_Changed; _amiiboSeriesComboBox.Changed += SeriesComboBox_Changed;
_amiiboCharsComboBox.Changed += CharacterComboBox_Changed; _amiiboCharsComboBox.Changed += CharacterComboBox_Changed;
if (LastScannedAmiiboId != "") if (LastScannedAmiiboId != "")
{ {
@ -219,7 +217,7 @@ namespace Ryujinx.Ui.Windows
Close(); Close();
} }
return DEFAULT_JSON; return DefaultJson;
} }
private async Task UpdateAmiiboPreview(string imageUrl) private async Task UpdateAmiiboPreview(string imageUrl)
@ -228,14 +226,14 @@ namespace Ryujinx.Ui.Windows
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync(); byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync();
Gdk.Pixbuf amiiboPreview = new Gdk.Pixbuf(amiiboPreviewBytes); Gdk.Pixbuf amiiboPreview = new(amiiboPreviewBytes);
float ratio = Math.Min((float)_amiiboImage.AllocatedWidth / amiiboPreview.Width, float ratio = Math.Min((float)_amiiboImage.AllocatedWidth / amiiboPreview.Width,
(float)_amiiboImage.AllocatedHeight / amiiboPreview.Height); (float)_amiiboImage.AllocatedHeight / amiiboPreview.Height);
int resizeHeight = (int)(amiiboPreview.Height * ratio); int resizeHeight = (int)(amiiboPreview.Height * ratio);
int resizeWidth = (int)(amiiboPreview.Width * ratio); int resizeWidth = (int)(amiiboPreview.Width * ratio);
_amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, Gdk.InterpType.Bilinear); _amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, Gdk.InterpType.Bilinear);
} }
@ -245,7 +243,7 @@ namespace Ryujinx.Ui.Windows
} }
} }
private void ShowInfoDialog() private static void ShowInfoDialog()
{ {
GtkDialog.CreateInfoDialog($"Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online."); GtkDialog.CreateInfoDialog($"Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online.");
} }
@ -261,7 +259,7 @@ namespace Ryujinx.Ui.Windows
List<AmiiboApi> amiiboSortedList = _amiiboList.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeriesComboBox.ActiveId).OrderBy(amiibo => amiibo.Name).ToList(); List<AmiiboApi> amiiboSortedList = _amiiboList.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeriesComboBox.ActiveId).OrderBy(amiibo => amiibo.Name).ToList();
List<string> comboxItemList = new List<string>(); List<string> comboxItemList = new();
for (int i = 0; i < amiiboSortedList.Count; i++) for (int i = 0; i < amiiboSortedList.Count; i++)
{ {
@ -295,7 +293,7 @@ namespace Ryujinx.Ui.Windows
_amiiboCharsComboBox.Active = 0; _amiiboCharsComboBox.Active = 0;
_scanButton.Sensitive = true; _scanButton.Sensitive = true;
_randomUuidCheckBox.Sensitive = true; _randomUuidCheckBox.Sensitive = true;
} }
@ -346,12 +344,12 @@ namespace Ryujinx.Ui.Windows
_amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes); _amiiboImage.Pixbuf = new Gdk.Pixbuf(_amiiboLogoBytes);
_amiiboSeriesComboBox.Changed -= SeriesComboBox_Changed; _amiiboSeriesComboBox.Changed -= SeriesComboBox_Changed;
_amiiboCharsComboBox.Changed -= CharacterComboBox_Changed; _amiiboCharsComboBox.Changed -= CharacterComboBox_Changed;
_amiiboSeriesComboBox.RemoveAll(); _amiiboSeriesComboBox.RemoveAll();
_amiiboCharsComboBox.RemoveAll(); _amiiboCharsComboBox.RemoveAll();
_scanButton.Sensitive = false; _scanButton.Sensitive = false;
_randomUuidCheckBox.Sensitive = false; _randomUuidCheckBox.Sensitive = false;
new Task(() => ParseAmiiboData()).Start(); new Task(() => ParseAmiiboData()).Start();
@ -368,8 +366,8 @@ namespace Ryujinx.Ui.Windows
private void CancelButton_Pressed(object sender, EventArgs args) private void CancelButton_Pressed(object sender, EventArgs args)
{ {
AmiiboId = ""; AmiiboId = "";
LastScannedAmiiboId = ""; LastScannedAmiiboId = "";
LastScannedAmiiboShowAll = false; LastScannedAmiiboShowAll = false;
Response = ResponseType.Cancel; Response = ResponseType.Cancel;

View file

@ -18,7 +18,6 @@ using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using Image = SixLabors.ImageSharp.Image; using Image = SixLabors.ImageSharp.Image;
namespace Ryujinx.Ui.Windows namespace Ryujinx.Ui.Windows
@ -26,23 +25,23 @@ namespace Ryujinx.Ui.Windows
public class AvatarWindow : Window public class AvatarWindow : Window
{ {
public byte[] SelectedProfileImage; public byte[] SelectedProfileImage;
public bool NewUser; public bool NewUser;
private static Dictionary<string, byte[]> _avatarDict = new Dictionary<string, byte[]>(); private static readonly Dictionary<string, byte[]> _avatarDict = new();
private ListStore _listStore; private readonly ListStore _listStore;
private IconView _iconView; private readonly IconView _iconView;
private Button _setBackgroungColorButton; private readonly Button _setBackgroungColorButton;
private Gdk.RGBA _backgroundColor; private Gdk.RGBA _backgroundColor;
public AvatarWindow() : base($"Ryujinx {Program.Version} - Manage Accounts - Avatar") public AvatarWindow() : base($"Ryujinx {Program.Version} - Manage Accounts - Avatar")
{ {
Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
CanFocus = false; CanFocus = false;
Resizable = false; Resizable = false;
Modal = true; Modal = true;
TypeHint = Gdk.WindowTypeHint.Dialog; TypeHint = Gdk.WindowTypeHint.Dialog;
SetDefaultSize(740, 400); SetDefaultSize(740, 400);
SetPosition(WindowPosition.Center); SetPosition(WindowPosition.Center);
@ -50,54 +49,56 @@ namespace Ryujinx.Ui.Windows
Box vbox = new(Orientation.Vertical, 0); Box vbox = new(Orientation.Vertical, 0);
Add(vbox); Add(vbox);
ScrolledWindow scrolledWindow = new ScrolledWindow ScrolledWindow scrolledWindow = new()
{ {
ShadowType = ShadowType.EtchedIn ShadowType = ShadowType.EtchedIn,
}; };
scrolledWindow.SetPolicy(PolicyType.Automatic, PolicyType.Automatic); scrolledWindow.SetPolicy(PolicyType.Automatic, PolicyType.Automatic);
Box hbox = new(Orientation.Horizontal, 0); Box hbox = new(Orientation.Horizontal, 0);
Button chooseButton = new Button() Button chooseButton = new()
{ {
Label = "Choose", Label = "Choose",
CanFocus = true, CanFocus = true,
ReceivesDefault = true ReceivesDefault = true,
}; };
chooseButton.Clicked += ChooseButton_Pressed; chooseButton.Clicked += ChooseButton_Pressed;
_setBackgroungColorButton = new Button() _setBackgroungColorButton = new Button()
{ {
Label = "Set Background Color", Label = "Set Background Color",
CanFocus = true CanFocus = true,
}; };
_setBackgroungColorButton.Clicked += SetBackgroungColorButton_Pressed; _setBackgroungColorButton.Clicked += SetBackgroungColorButton_Pressed;
_backgroundColor.Red = 1; _backgroundColor.Red = 1;
_backgroundColor.Green = 1; _backgroundColor.Green = 1;
_backgroundColor.Blue = 1; _backgroundColor.Blue = 1;
_backgroundColor.Alpha = 1; _backgroundColor.Alpha = 1;
Button closeButton = new Button() Button closeButton = new()
{ {
Label = "Close", Label = "Close",
CanFocus = true CanFocus = true,
}; };
closeButton.Clicked += CloseButton_Pressed; closeButton.Clicked += CloseButton_Pressed;
vbox.PackStart(scrolledWindow, true, true, 0); vbox.PackStart(scrolledWindow, true, true, 0);
hbox.PackStart(chooseButton, true, true, 0); hbox.PackStart(chooseButton, true, true, 0);
hbox.PackStart(_setBackgroungColorButton, true, true, 0); hbox.PackStart(_setBackgroungColorButton, true, true, 0);
hbox.PackStart(closeButton, true, true, 0); hbox.PackStart(closeButton, true, true, 0);
vbox.PackStart(hbox, false, false, 0); vbox.PackStart(hbox, false, false, 0);
_listStore = new ListStore(typeof(string), typeof(Gdk.Pixbuf)); _listStore = new ListStore(typeof(string), typeof(Gdk.Pixbuf));
_listStore.SetSortColumnId(0, SortType.Ascending); _listStore.SetSortColumnId(0, SortType.Ascending);
_iconView = new IconView(_listStore); _iconView = new IconView(_listStore)
_iconView.ItemWidth = 64; {
_iconView.ItemPadding = 10; ItemWidth = 64,
_iconView.PixbufColumn = 1; ItemPadding = 10,
PixbufColumn = 1,
};
_iconView.SelectionChanged += IconView_SelectionChanged; _iconView.SelectionChanged += IconView_SelectionChanged;
@ -118,39 +119,36 @@ namespace Ryujinx.Ui.Windows
} }
string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data); string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data);
string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath); string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath);
if (!string.IsNullOrWhiteSpace(avatarPath)) if (!string.IsNullOrWhiteSpace(avatarPath))
{ {
using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open)) using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open);
Nca nca = new(virtualFileSystem.KeySet, ncaFileStream);
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
foreach (var item in romfs.EnumerateEntries())
{ {
Nca nca = new Nca(virtualFileSystem.KeySet, ncaFileStream); // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
foreach (var item in romfs.EnumerateEntries()) if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs"))
{ {
// TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. using var file = new UniqueRef<IFile>();
if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
{
using var file = new UniqueRef<IFile>();
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
using MemoryStream streamPng = MemoryStreamManager.Shared.GetStream();
file.Get.AsStream().CopyTo(stream);
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream()) stream.Position = 0;
using (MemoryStream streamPng = MemoryStreamManager.Shared.GetStream())
{
file.Get.AsStream().CopyTo(stream);
stream.Position = 0; Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256); avatarImage.SaveAsPng(streamPng);
avatarImage.SaveAsPng(streamPng); _avatarDict.Add(item.FullPath, streamPng.ToArray());
_avatarDict.Add(item.FullPath, streamPng.ToArray());
}
}
} }
} }
} }
@ -165,23 +163,24 @@ namespace Ryujinx.Ui.Windows
_listStore.AppendValues(avatar.Key, new Gdk.Pixbuf(ProcessImage(avatar.Value), 96, 96)); _listStore.AppendValues(avatar.Key, new Gdk.Pixbuf(ProcessImage(avatar.Value), 96, 96));
} }
_iconView.SelectPath(new TreePath(new int[] { 0 })); _iconView.SelectPath(new TreePath(new[] { 0 }));
} }
private byte[] ProcessImage(byte[] data) private byte[] ProcessImage(byte[] data)
{ {
using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream()) using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
{
Image avatarImage = Image.Load(data, new PngDecoder());
avatarImage.Mutate(x => x.BackgroundColor(new Rgba32((byte)(_backgroundColor.Red * 255), Image avatarImage = Image.Load(data, new PngDecoder());
(byte)(_backgroundColor.Green * 255),
(byte)(_backgroundColor.Blue * 255),
(byte)(_backgroundColor.Alpha * 255))));
avatarImage.SaveAsJpeg(streamJpg);
return streamJpg.ToArray(); avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
} (byte)(_backgroundColor.Red * 255),
(byte)(_backgroundColor.Green * 255),
(byte)(_backgroundColor.Blue * 255),
(byte)(_backgroundColor.Alpha * 255)
)));
avatarImage.SaveAsJpeg(streamJpg);
return streamJpg.ToArray();
} }
private void CloseButton_Pressed(object sender, EventArgs e) private void CloseButton_Pressed(object sender, EventArgs e)
@ -203,20 +202,19 @@ namespace Ryujinx.Ui.Windows
private void SetBackgroungColorButton_Pressed(object sender, EventArgs e) private void SetBackgroungColorButton_Pressed(object sender, EventArgs e)
{ {
using (ColorChooserDialog colorChooserDialog = new ColorChooserDialog("Set Background Color", this)) using ColorChooserDialog colorChooserDialog = new("Set Background Color", this);
colorChooserDialog.UseAlpha = false;
colorChooserDialog.Rgba = _backgroundColor;
if (colorChooserDialog.Run() == (int)ResponseType.Ok)
{ {
colorChooserDialog.UseAlpha = false; _backgroundColor = colorChooserDialog.Rgba;
colorChooserDialog.Rgba = _backgroundColor;
if (colorChooserDialog.Run() == (int)ResponseType.Ok) ProcessAvatars();
{
_backgroundColor = colorChooserDialog.Rgba;
ProcessAvatars();
}
colorChooserDialog.Hide();
} }
colorChooserDialog.Hide();
} }
private void ChooseButton_Pressed(object sender, EventArgs e) private void ChooseButton_Pressed(object sender, EventArgs e)
@ -226,69 +224,68 @@ namespace Ryujinx.Ui.Windows
private static byte[] DecompressYaz0(Stream stream) private static byte[] DecompressYaz0(Stream stream)
{ {
using (BinaryReader reader = new BinaryReader(stream)) using BinaryReader reader = new(stream);
reader.ReadInt32(); // Magic
uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
reader.ReadInt64(); // Padding
byte[] input = new byte[stream.Length - stream.Position];
stream.Read(input, 0, input.Length);
long inputOffset = 0;
byte[] output = new byte[decodedLength];
long outputOffset = 0;
ushort mask = 0;
byte header = 0;
while (outputOffset < decodedLength)
{ {
reader.ReadInt32(); // Magic if ((mask >>= 1) == 0)
uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
reader.ReadInt64(); // Padding
byte[] input = new byte[stream.Length - stream.Position];
stream.Read(input, 0, input.Length);
long inputOffset = 0;
byte[] output = new byte[decodedLength];
long outputOffset = 0;
ushort mask = 0;
byte header = 0;
while (outputOffset < decodedLength)
{ {
if ((mask >>= 1) == 0) header = input[inputOffset++];
mask = 0x80;
}
if ((header & mask) > 0)
{
if (outputOffset == output.Length)
{ {
header = input[inputOffset++]; break;
mask = 0x80;
} }
if ((header & mask) > 0) output[outputOffset++] = input[inputOffset++];
{ }
if (outputOffset == output.Length) else
{ {
break; byte byte1 = input[inputOffset++];
} byte byte2 = input[inputOffset++];
output[outputOffset++] = input[inputOffset++]; int dist = ((byte1 & 0xF) << 8) | byte2;
int position = (int)outputOffset - (dist + 1);
int length = byte1 >> 4;
if (length == 0)
{
length = input[inputOffset++] + 0x12;
} }
else else
{ {
byte byte1 = input[inputOffset++]; length += 2;
byte byte2 = input[inputOffset++]; }
int dist = ((byte1 & 0xF) << 8) | byte2; while (length-- > 0)
int position = (int)outputOffset - (dist + 1); {
output[outputOffset++] = output[position++];
int length = byte1 >> 4;
if (length == 0)
{
length = input[inputOffset++] + 0x12;
}
else
{
length += 2;
}
while (length-- > 0)
{
output[outputOffset++] = output[position++];
}
} }
} }
return output;
} }
return output;
} }
} }
} }

View file

@ -6,7 +6,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;
namespace Ryujinx.Ui.Windows namespace Ryujinx.Ui.Windows
@ -16,11 +15,11 @@ namespace Ryujinx.Ui.Windows
private readonly string _enabledCheatsPath; private readonly string _enabledCheatsPath;
private readonly bool _noCheatsFound; private readonly bool _noCheatsFound;
#pragma warning disable CS0649, IDE0044 #pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier
[GUI] Label _baseTitleInfoLabel; [GUI] Label _baseTitleInfoLabel;
[GUI] TextView _buildIdTextView; [GUI] TextView _buildIdTextView;
[GUI] TreeView _cheatTreeView; [GUI] TreeView _cheatTreeView;
[GUI] Button _saveButton; [GUI] Button _saveButton;
#pragma warning restore CS0649, IDE0044 #pragma warning restore CS0649, IDE0044
public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { } public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { }
@ -31,14 +30,14 @@ namespace Ryujinx.Ui.Windows
_baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]";
_buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}"; _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}";
string modsBasePath = ModLoader.GetModsBasePath(); string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16")); string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16"));
_enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt"); _enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt");
_cheatTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string), typeof(string)); _cheatTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string), typeof(string));
CellRendererToggle enableToggle = new CellRendererToggle(); CellRendererToggle enableToggle = new();
enableToggle.Toggled += (sender, args) => enableToggle.Toggled += (sender, args) =>
{ {
_cheatTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); _cheatTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path));
@ -62,7 +61,7 @@ namespace Ryujinx.Ui.Windows
var buildIdColumn = _cheatTreeView.AppendColumn("Build Id", new CellRendererText(), "text", 3); var buildIdColumn = _cheatTreeView.AppendColumn("Build Id", new CellRendererText(), "text", 3);
buildIdColumn.Visible = false; buildIdColumn.Visible = false;
string[] enabled = { }; string[] enabled = Array.Empty<string>();
if (File.Exists(_enabledCheatsPath)) if (File.Exists(_enabledCheatsPath))
{ {
@ -90,7 +89,7 @@ namespace Ryujinx.Ui.Windows
parentIter = ((TreeStore)_cheatTreeView.Model).AppendValues(false, buildId, parentPath, ""); parentIter = ((TreeStore)_cheatTreeView.Model).AppendValues(false, buildId, parentPath, "");
} }
string cleanName = cheat.Name.Substring(1, cheat.Name.Length - 8); string cleanName = cheat.Name[1..^7];
((TreeStore)_cheatTreeView.Model).AppendValues(parentIter, enabled.Contains($"{buildId}-{cheat.Name}"), cleanName, "", buildId); ((TreeStore)_cheatTreeView.Model).AppendValues(parentIter, enabled.Contains($"{buildId}-{cheat.Name}"), cleanName, "", buildId);
cheatAdded++; cheatAdded++;
@ -116,7 +115,7 @@ namespace Ryujinx.Ui.Windows
return; return;
} }
List<string> enabledCheats = new List<string>(); List<string> enabledCheats = new();
if (_cheatTreeView.Model.GetIterFirst(out TreeIter parentIter)) if (_cheatTreeView.Model.GetIterFirst(out TreeIter parentIter))
{ {

File diff suppressed because it is too large Load diff

View file

@ -19,16 +19,16 @@ namespace Ryujinx.Ui.Windows
{ {
public class DlcWindow : Window public class DlcWindow : Window
{ {
private readonly VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private readonly string _titleId; private readonly string _titleId;
private readonly string _dlcJsonPath; private readonly string _dlcJsonPath;
private readonly List<DownloadableContentContainer> _dlcContainerList; private readonly List<DownloadableContentContainer> _dlcContainerList;
private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
#pragma warning disable CS0649, IDE0044 #pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier
[GUI] Label _baseTitleInfoLabel; [GUI] Label _baseTitleInfoLabel;
[GUI] TreeView _dlcTreeView; [GUI] TreeView _dlcTreeView;
[GUI] TreeSelection _dlcTreeSelection; [GUI] TreeSelection _dlcTreeSelection;
#pragma warning restore CS0649, IDE0044 #pragma warning restore CS0649, IDE0044
@ -38,14 +38,14 @@ namespace Ryujinx.Ui.Windows
{ {
builder.Autoconnect(this); builder.Autoconnect(this);
_titleId = titleId; _titleId = titleId;
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json"); _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json");
_baseTitleInfoLabel.Text = $"DLC Available for {titleName} [{titleId.ToUpper()}]"; _baseTitleInfoLabel.Text = $"DLC Available for {titleName} [{titleId.ToUpper()}]";
try try
{ {
_dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, SerializerContext.ListDownloadableContentContainer); _dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, _serializerContext.ListDownloadableContentContainer);
} }
catch catch
{ {
@ -54,7 +54,7 @@ namespace Ryujinx.Ui.Windows
_dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string)); _dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string));
CellRendererToggle enableToggle = new CellRendererToggle(); CellRendererToggle enableToggle = new();
enableToggle.Toggled += (sender, args) => enableToggle.Toggled += (sender, args) =>
{ {
_dlcTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); _dlcTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path));
@ -71,9 +71,9 @@ namespace Ryujinx.Ui.Windows
} }
}; };
_dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0); _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0);
_dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1); _dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1);
_dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2);
foreach (DownloadableContentContainer dlcContainer in _dlcContainerList) foreach (DownloadableContentContainer dlcContainer in _dlcContainerList)
{ {
@ -85,8 +85,11 @@ namespace Ryujinx.Ui.Windows
// "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca. // "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca.
bool areAllContentPacksEnabled = dlcContainer.DownloadableContentNcaList.TrueForAll((nca) => nca.Enabled); bool areAllContentPacksEnabled = dlcContainer.DownloadableContentNcaList.TrueForAll((nca) => nca.Enabled);
TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.ContainerPath); TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.ContainerPath);
using FileStream containerFile = File.OpenRead(dlcContainer.ContainerPath); using FileStream containerFile = File.OpenRead(dlcContainer.ContainerPath);
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
PartitionFileSystem pfs = new(containerFile.AsStorage());
_virtualFileSystem.ImportTickets(pfs); _virtualFileSystem.ImportTickets(pfs);
foreach (DownloadableContentNca dlcNca in dlcContainer.DownloadableContentNcaList) foreach (DownloadableContentNca dlcNca in dlcContainer.DownloadableContentNcaList)
@ -126,14 +129,14 @@ namespace Ryujinx.Ui.Windows
private void AddButton_Clicked(object sender, EventArgs args) private void AddButton_Clicked(object sender, EventArgs args)
{ {
FileChooserNative fileChooser = new FileChooserNative("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel") FileChooserNative fileChooser = new("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel")
{ {
SelectMultiple = true SelectMultiple = true,
}; };
FileFilter filter = new FileFilter() FileFilter filter = new()
{ {
Name = "Switch Game DLCs" Name = "Switch Game DLCs",
}; };
filter.AddPattern("*.nsp"); filter.AddPattern("*.nsp");
@ -148,44 +151,46 @@ namespace Ryujinx.Ui.Windows
return; return;
} }
using (FileStream containerFile = File.OpenRead(containerPath)) using FileStream containerFile = File.OpenRead(containerPath);
PartitionFileSystem pfs = new(containerFile.AsStorage());
bool containsDlc = false;
_virtualFileSystem.ImportTickets(pfs);
TreeIter? parentIter = null;
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{ {
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); using var ncaFile = new UniqueRef<IFile>();
bool containsDlc = false;
_virtualFileSystem.ImportTickets(pfs); pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
TreeIter? parentIter = null; Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), containerPath);
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) if (nca == null)
{ {
using var ncaFile = new UniqueRef<IFile>(); continue;
}
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); if (nca.Header.ContentType == NcaContentType.PublicData)
{
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), containerPath); if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId)
if (nca == null) continue;
if (nca.Header.ContentType == NcaContentType.PublicData)
{ {
if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId) break;
{
break;
}
parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath);
((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath);
containsDlc = true;
} }
}
if (!containsDlc) parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath);
{
GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath);
containsDlc = true;
} }
} }
if (!containsDlc)
{
GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!");
}
} }
} }
@ -209,7 +214,7 @@ namespace Ryujinx.Ui.Windows
private void RemoveAllButton_Clicked(object sender, EventArgs args) private void RemoveAllButton_Clicked(object sender, EventArgs args)
{ {
List<TreeIter> toRemove = new List<TreeIter>(); List<TreeIter> toRemove = new();
if (_dlcTreeView.Model.GetIterFirst(out TreeIter iter)) if (_dlcTreeView.Model.GetIterFirst(out TreeIter iter))
{ {
@ -237,19 +242,19 @@ namespace Ryujinx.Ui.Windows
{ {
if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, parentIter)) if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, parentIter))
{ {
DownloadableContentContainer dlcContainer = new DownloadableContentContainer DownloadableContentContainer dlcContainer = new()
{ {
ContainerPath = (string)_dlcTreeView.Model.GetValue(parentIter, 2), ContainerPath = (string)_dlcTreeView.Model.GetValue(parentIter, 2),
DownloadableContentNcaList = new List<DownloadableContentNca>() DownloadableContentNcaList = new List<DownloadableContentNca>(),
}; };
do do
{ {
dlcContainer.DownloadableContentNcaList.Add(new DownloadableContentNca dlcContainer.DownloadableContentNcaList.Add(new DownloadableContentNca
{ {
Enabled = (bool)_dlcTreeView.Model.GetValue(childIter, 0), Enabled = (bool)_dlcTreeView.Model.GetValue(childIter, 0),
TitleId = Convert.ToUInt64(_dlcTreeView.Model.GetValue(childIter, 1).ToString(), 16), TitleId = Convert.ToUInt64(_dlcTreeView.Model.GetValue(childIter, 1).ToString(), 16),
FullPath = (string)_dlcTreeView.Model.GetValue(childIter, 2) FullPath = (string)_dlcTreeView.Model.GetValue(childIter, 2),
}); });
} }
while (_dlcTreeView.Model.IterNext(ref childIter)); while (_dlcTreeView.Model.IterNext(ref childIter));
@ -260,7 +265,7 @@ namespace Ryujinx.Ui.Windows
while (_dlcTreeView.Model.IterNext(ref parentIter)); while (_dlcTreeView.Model.IterNext(ref parentIter));
} }
JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, SerializerContext.ListDownloadableContentContainer); JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, _serializerContext.ListDownloadableContentContainer);
Dispose(); Dispose();
} }

View file

@ -6,14 +6,12 @@ using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Graphics.Vulkan;
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.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Common.Configuration.System;
using Ryujinx.Ui.Helper; using Ryujinx.Ui.Helper;
using Ryujinx.Ui.Widgets; using Ryujinx.Ui.Widgets;
using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -27,95 +25,95 @@ namespace Ryujinx.Ui.Windows
{ {
public class SettingsWindow : Window public class SettingsWindow : Window
{ {
private readonly MainWindow _parent; 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;
private readonly HashSet<string> _validTzRegions; private readonly HashSet<string> _validTzRegions;
private long _systemTimeOffset; private long _systemTimeOffset;
private float _previousVolumeLevel; private float _previousVolumeLevel;
private bool _directoryChanged = false; private bool _directoryChanged = false;
#pragma warning disable CS0649, IDE0044 #pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier
[GUI] CheckButton _traceLogToggle; [GUI] CheckButton _traceLogToggle;
[GUI] CheckButton _errorLogToggle; [GUI] CheckButton _errorLogToggle;
[GUI] CheckButton _warningLogToggle; [GUI] CheckButton _warningLogToggle;
[GUI] CheckButton _infoLogToggle; [GUI] CheckButton _infoLogToggle;
[GUI] CheckButton _stubLogToggle; [GUI] CheckButton _stubLogToggle;
[GUI] CheckButton _debugLogToggle; [GUI] CheckButton _debugLogToggle;
[GUI] CheckButton _fileLogToggle; [GUI] CheckButton _fileLogToggle;
[GUI] CheckButton _guestLogToggle; [GUI] CheckButton _guestLogToggle;
[GUI] CheckButton _fsAccessLogToggle; [GUI] CheckButton _fsAccessLogToggle;
[GUI] Adjustment _fsLogSpinAdjustment; [GUI] Adjustment _fsLogSpinAdjustment;
[GUI] ComboBoxText _graphicsDebugLevel; [GUI] ComboBoxText _graphicsDebugLevel;
[GUI] CheckButton _dockedModeToggle; [GUI] CheckButton _dockedModeToggle;
[GUI] CheckButton _discordToggle; [GUI] CheckButton _discordToggle;
[GUI] CheckButton _checkUpdatesToggle; [GUI] CheckButton _checkUpdatesToggle;
[GUI] CheckButton _showConfirmExitToggle; [GUI] CheckButton _showConfirmExitToggle;
[GUI] RadioButton _hideCursorNever; [GUI] RadioButton _hideCursorNever;
[GUI] RadioButton _hideCursorOnIdle; [GUI] RadioButton _hideCursorOnIdle;
[GUI] RadioButton _hideCursorAlways; [GUI] RadioButton _hideCursorAlways;
[GUI] CheckButton _vSyncToggle; [GUI] CheckButton _vSyncToggle;
[GUI] CheckButton _shaderCacheToggle; [GUI] CheckButton _shaderCacheToggle;
[GUI] CheckButton _textureRecompressionToggle; [GUI] CheckButton _textureRecompressionToggle;
[GUI] CheckButton _macroHLEToggle; [GUI] CheckButton _macroHLEToggle;
[GUI] CheckButton _ptcToggle; [GUI] CheckButton _ptcToggle;
[GUI] CheckButton _internetToggle; [GUI] CheckButton _internetToggle;
[GUI] CheckButton _fsicToggle; [GUI] CheckButton _fsicToggle;
[GUI] RadioButton _mmSoftware; [GUI] RadioButton _mmSoftware;
[GUI] RadioButton _mmHost; [GUI] RadioButton _mmHost;
[GUI] RadioButton _mmHostUnsafe; [GUI] RadioButton _mmHostUnsafe;
[GUI] CheckButton _expandRamToggle; [GUI] CheckButton _expandRamToggle;
[GUI] CheckButton _ignoreToggle; [GUI] CheckButton _ignoreToggle;
[GUI] CheckButton _directKeyboardAccess; [GUI] CheckButton _directKeyboardAccess;
[GUI] CheckButton _directMouseAccess; [GUI] CheckButton _directMouseAccess;
[GUI] ComboBoxText _systemLanguageSelect; [GUI] ComboBoxText _systemLanguageSelect;
[GUI] ComboBoxText _systemRegionSelect; [GUI] ComboBoxText _systemRegionSelect;
[GUI] Entry _systemTimeZoneEntry; [GUI] Entry _systemTimeZoneEntry;
[GUI] EntryCompletion _systemTimeZoneCompletion; [GUI] EntryCompletion _systemTimeZoneCompletion;
[GUI] Box _audioBackendBox; [GUI] Box _audioBackendBox;
[GUI] ComboBox _audioBackendSelect; [GUI] ComboBox _audioBackendSelect;
[GUI] Label _audioVolumeLabel; [GUI] Label _audioVolumeLabel;
[GUI] Scale _audioVolumeSlider; [GUI] Scale _audioVolumeSlider;
[GUI] SpinButton _systemTimeYearSpin; [GUI] SpinButton _systemTimeYearSpin;
[GUI] SpinButton _systemTimeMonthSpin; [GUI] SpinButton _systemTimeMonthSpin;
[GUI] SpinButton _systemTimeDaySpin; [GUI] SpinButton _systemTimeDaySpin;
[GUI] SpinButton _systemTimeHourSpin; [GUI] SpinButton _systemTimeHourSpin;
[GUI] SpinButton _systemTimeMinuteSpin; [GUI] SpinButton _systemTimeMinuteSpin;
[GUI] Adjustment _systemTimeYearSpinAdjustment; [GUI] Adjustment _systemTimeYearSpinAdjustment;
[GUI] Adjustment _systemTimeMonthSpinAdjustment; [GUI] Adjustment _systemTimeMonthSpinAdjustment;
[GUI] Adjustment _systemTimeDaySpinAdjustment; [GUI] Adjustment _systemTimeDaySpinAdjustment;
[GUI] Adjustment _systemTimeHourSpinAdjustment; [GUI] Adjustment _systemTimeHourSpinAdjustment;
[GUI] Adjustment _systemTimeMinuteSpinAdjustment; [GUI] Adjustment _systemTimeMinuteSpinAdjustment;
[GUI] ComboBoxText _multiLanSelect; [GUI] ComboBoxText _multiLanSelect;
[GUI] CheckButton _custThemeToggle; [GUI] CheckButton _custThemeToggle;
[GUI] Entry _custThemePath; [GUI] Entry _custThemePath;
[GUI] ToggleButton _browseThemePath; [GUI] ToggleButton _browseThemePath;
[GUI] Label _custThemePathLabel; [GUI] Label _custThemePathLabel;
[GUI] TreeView _gameDirsBox; [GUI] TreeView _gameDirsBox;
[GUI] Entry _addGameDirBox; [GUI] Entry _addGameDirBox;
[GUI] ComboBoxText _galThreading; [GUI] ComboBoxText _galThreading;
[GUI] Entry _graphicsShadersDumpPath; [GUI] Entry _graphicsShadersDumpPath;
[GUI] ComboBoxText _anisotropy; [GUI] ComboBoxText _anisotropy;
[GUI] ComboBoxText _aspectRatio; [GUI] ComboBoxText _aspectRatio;
[GUI] ComboBoxText _antiAliasing; [GUI] ComboBoxText _antiAliasing;
[GUI] ComboBoxText _scalingFilter; [GUI] ComboBoxText _scalingFilter;
[GUI] ComboBoxText _graphicsBackend; [GUI] ComboBoxText _graphicsBackend;
[GUI] ComboBoxText _preferredGpu; [GUI] ComboBoxText _preferredGpu;
[GUI] ComboBoxText _resScaleCombo; [GUI] ComboBoxText _resScaleCombo;
[GUI] Entry _resScaleText; [GUI] Entry _resScaleText;
[GUI] Adjustment _scalingFilterLevel; [GUI] Adjustment _scalingFilterLevel;
[GUI] Scale _scalingFilterSlider; [GUI] Scale _scalingFilterSlider;
[GUI] ToggleButton _configureController1; [GUI] ToggleButton _configureController1;
[GUI] ToggleButton _configureController2; [GUI] ToggleButton _configureController2;
[GUI] ToggleButton _configureController3; [GUI] ToggleButton _configureController3;
[GUI] ToggleButton _configureController4; [GUI] ToggleButton _configureController4;
[GUI] ToggleButton _configureController5; [GUI] ToggleButton _configureController5;
[GUI] ToggleButton _configureController6; [GUI] ToggleButton _configureController6;
[GUI] ToggleButton _configureController7; [GUI] ToggleButton _configureController7;
[GUI] ToggleButton _configureController8; [GUI] ToggleButton _configureController8;
[GUI] ToggleButton _configureControllerH; [GUI] ToggleButton _configureControllerH;
#pragma warning restore CS0649, IDE0044 #pragma warning restore CS0649, IDE0044
@ -316,11 +314,11 @@ namespace Ryujinx.Ui.Windows
} }
// Custom EntryCompletion Columns. If added to glade, need to override more signals // Custom EntryCompletion Columns. If added to glade, need to override more signals
ListStore tzList = new ListStore(typeof(string), typeof(string), typeof(string)); ListStore tzList = new(typeof(string), typeof(string), typeof(string));
_systemTimeZoneCompletion.Model = tzList; _systemTimeZoneCompletion.Model = tzList;
CellRendererText offsetCol = new CellRendererText(); CellRendererText offsetCol = new();
CellRendererText abbrevCol = new CellRendererText(); CellRendererText abbrevCol = new();
_systemTimeZoneCompletion.PackStart(offsetCol, false); _systemTimeZoneCompletion.PackStart(offsetCol, false);
_systemTimeZoneCompletion.AddAttribute(offsetCol, "text", 0); _systemTimeZoneCompletion.AddAttribute(offsetCol, "text", 0);
@ -364,17 +362,17 @@ namespace Ryujinx.Ui.Windows
PopulateNetworkInterfaces(); PopulateNetworkInterfaces();
_multiLanSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); _multiLanSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);
_custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath; _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
_resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString(); _resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString();
_scalingFilterLevel.Value = ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value; _scalingFilterLevel.Value = ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value;
_resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; _resScaleText.Visible = _resScaleCombo.ActiveId == "-1";
_scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2";
_graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath; _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
_fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode; _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
_systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset; _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset;
_gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0); _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0);
_gameDirsBoxStore = new ListStore(typeof(string)); _gameDirsBoxStore = new ListStore(typeof(string));
_gameDirsBox.Model = _gameDirsBoxStore; _gameDirsBox.Model = _gameDirsBoxStore;
foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value) foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value)
@ -384,9 +382,9 @@ namespace Ryujinx.Ui.Windows
if (_custThemeToggle.Active == false) if (_custThemeToggle.Active == false)
{ {
_custThemePath.Sensitive = false; _custThemePath.Sensitive = false;
_custThemePathLabel.Sensitive = false; _custThemePathLabel.Sensitive = false;
_browseThemePath.Sensitive = false; _browseThemePath.Sensitive = false;
} }
// Setup system time spinners // Setup system time spinners
@ -394,10 +392,10 @@ namespace Ryujinx.Ui.Windows
_audioBackendStore = new ListStore(typeof(string), typeof(AudioBackend)); _audioBackendStore = new ListStore(typeof(string), typeof(AudioBackend));
TreeIter openAlIter = _audioBackendStore.AppendValues("OpenAL", AudioBackend.OpenAl); TreeIter openAlIter = _audioBackendStore.AppendValues("OpenAL", AudioBackend.OpenAl);
TreeIter soundIoIter = _audioBackendStore.AppendValues("SoundIO", AudioBackend.SoundIo); TreeIter soundIoIter = _audioBackendStore.AppendValues("SoundIO", AudioBackend.SoundIo);
TreeIter sdl2Iter = _audioBackendStore.AppendValues("SDL2", AudioBackend.SDL2); TreeIter sdl2Iter = _audioBackendStore.AppendValues("SDL2", AudioBackend.SDL2);
TreeIter dummyIter = _audioBackendStore.AppendValues("Dummy", AudioBackend.Dummy); TreeIter dummyIter = _audioBackendStore.AppendValues("Dummy", AudioBackend.Dummy);
_audioBackendSelect = ComboBox.NewWithModelAndEntry(_audioBackendStore); _audioBackendSelect = ComboBox.NewWithModelAndEntry(_audioBackendStore);
_audioBackendSelect.EntryTextColumn = 0; _audioBackendSelect.EntryTextColumn = 0;
@ -418,35 +416,35 @@ namespace Ryujinx.Ui.Windows
_audioBackendSelect.SetActiveIter(dummyIter); _audioBackendSelect.SetActiveIter(dummyIter);
break; break;
default: default:
throw new ArgumentOutOfRangeException(); throw new InvalidOperationException($"{nameof(ConfigurationState.Instance.System.AudioBackend)} contains an invalid value: {ConfigurationState.Instance.System.AudioBackend.Value}");
} }
_audioBackendBox.Add(_audioBackendSelect); _audioBackendBox.Add(_audioBackendSelect);
_audioBackendSelect.Show(); _audioBackendSelect.Show();
_previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume; _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume;
_audioVolumeLabel = new Label("Volume: "); _audioVolumeLabel = new Label("Volume: ");
_audioVolumeSlider = new Scale(Orientation.Horizontal, 0, 100, 1); _audioVolumeSlider = new Scale(Orientation.Horizontal, 0, 100, 1);
_audioVolumeLabel.MarginStart = 10; _audioVolumeLabel.MarginStart = 10;
_audioVolumeSlider.ValuePos = PositionType.Right; _audioVolumeSlider.ValuePos = PositionType.Right;
_audioVolumeSlider.WidthRequest = 200; _audioVolumeSlider.WidthRequest = 200;
_audioVolumeSlider.Value = _previousVolumeLevel * 100; _audioVolumeSlider.Value = _previousVolumeLevel * 100;
_audioVolumeSlider.ValueChanged += VolumeSlider_OnChange; _audioVolumeSlider.ValueChanged += VolumeSlider_OnChange;
_audioBackendBox.Add(_audioVolumeLabel); _audioBackendBox.Add(_audioVolumeLabel);
_audioBackendBox.Add(_audioVolumeSlider); _audioBackendBox.Add(_audioVolumeSlider);
_audioVolumeLabel.Show(); _audioVolumeLabel.Show();
_audioVolumeSlider.Show(); _audioVolumeSlider.Show();
bool openAlIsSupported = false; bool openAlIsSupported = false;
bool soundIoIsSupported = false; bool soundIoIsSupported = false;
bool sdl2IsSupported = false; bool sdl2IsSupported = false;
Task.Run(() => Task.Run(() =>
{ {
openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported; openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported;
soundIoIsSupported = !OperatingSystem.IsMacOS() && SoundIoHardwareDeviceDriver.IsSupported; soundIoIsSupported = !OperatingSystem.IsMacOS() && SoundIoHardwareDeviceDriver.IsSupported;
sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported; sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported;
}); });
// This function runs whenever the dropdown is opened // This function runs whenever the dropdown is opened
@ -454,18 +452,18 @@ namespace Ryujinx.Ui.Windows
{ {
cell.Sensitive = ((AudioBackend)_audioBackendStore.GetValue(iter, 1)) switch cell.Sensitive = ((AudioBackend)_audioBackendStore.GetValue(iter, 1)) switch
{ {
AudioBackend.OpenAl => openAlIsSupported, AudioBackend.OpenAl => openAlIsSupported,
AudioBackend.SoundIo => soundIoIsSupported, AudioBackend.SoundIo => soundIoIsSupported,
AudioBackend.SDL2 => sdl2IsSupported, AudioBackend.SDL2 => sdl2IsSupported,
AudioBackend.Dummy => true, AudioBackend.Dummy => true,
_ => throw new ArgumentOutOfRangeException() _ => throw new InvalidOperationException($"{nameof(_audioBackendStore)} contains an invalid value for iteration {iter}: {_audioBackendStore.GetValue(iter, 1)}"),
}; };
}); });
if (OperatingSystem.IsMacOS()) if (OperatingSystem.IsMacOS())
{ {
var store = (_graphicsBackend.Model as ListStore); var store = (_graphicsBackend.Model as ListStore);
store.GetIter(out TreeIter openglIter, new TreePath(new int[] {1})); store.GetIter(out TreeIter openglIter, new TreePath(new[] { 1 }));
store.Remove(ref openglIter); store.Remove(ref openglIter);
_graphicsBackend.Model = store; _graphicsBackend.Model = store;
@ -478,15 +476,15 @@ namespace Ryujinx.Ui.Windows
if (Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan) if (Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan)
{ {
var devices = VulkanRenderer.GetPhysicalDevices(); var devices = Graphics.Vulkan.VulkanRenderer.GetPhysicalDevices();
string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value; string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
string preferredGpuId = preferredGpuIdFromConfig; string preferredGpuId = preferredGpuIdFromConfig;
bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig); bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig);
foreach (var device in devices) foreach (var device in devices)
{ {
string dGPU = device.IsDiscrete ? " (dGPU)" : ""; string dGpu = device.IsDiscrete ? " (dGPU)" : "";
_preferredGpu.Append(device.Id, $"{device.Name}{dGPU}"); _preferredGpu.Append(device.Id, $"{device.Name}{dGpu}");
// If there's no GPU selected yet, we just pick the first GPU. // If there's no GPU selected yet, we just pick the first GPU.
// If there's a discrete GPU available, we always prefer that over the previous selection, // If there's a discrete GPU available, we always prefer that over the previous selection,
@ -521,33 +519,33 @@ namespace Ryujinx.Ui.Windows
private void UpdateSystemTimeSpinners() private void UpdateSystemTimeSpinners()
{ {
//Bind system time events //Bind system time events
_systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged; _systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
_systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged; _systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
_systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged; _systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged;
_systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged; _systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
_systemTimeMinuteSpin.ValueChanged -= SystemTimeSpin_ValueChanged; _systemTimeMinuteSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
//Apply actual system time + SystemTimeOffset to system time spin buttons //Apply actual system time + SystemTimeOffset to system time spin buttons
DateTime systemTime = DateTime.Now.AddSeconds(_systemTimeOffset); DateTime systemTime = DateTime.Now.AddSeconds(_systemTimeOffset);
_systemTimeYearSpinAdjustment.Value = systemTime.Year; _systemTimeYearSpinAdjustment.Value = systemTime.Year;
_systemTimeMonthSpinAdjustment.Value = systemTime.Month; _systemTimeMonthSpinAdjustment.Value = systemTime.Month;
_systemTimeDaySpinAdjustment.Value = systemTime.Day; _systemTimeDaySpinAdjustment.Value = systemTime.Day;
_systemTimeHourSpinAdjustment.Value = systemTime.Hour; _systemTimeHourSpinAdjustment.Value = systemTime.Hour;
_systemTimeMinuteSpinAdjustment.Value = systemTime.Minute; _systemTimeMinuteSpinAdjustment.Value = systemTime.Minute;
//Format spin buttons text to include leading zeros //Format spin buttons text to include leading zeros
_systemTimeYearSpin.Text = systemTime.Year.ToString("0000"); _systemTimeYearSpin.Text = systemTime.Year.ToString("0000");
_systemTimeMonthSpin.Text = systemTime.Month.ToString("00"); _systemTimeMonthSpin.Text = systemTime.Month.ToString("00");
_systemTimeDaySpin.Text = systemTime.Day.ToString("00"); _systemTimeDaySpin.Text = systemTime.Day.ToString("00");
_systemTimeHourSpin.Text = systemTime.Hour.ToString("00"); _systemTimeHourSpin.Text = systemTime.Hour.ToString("00");
_systemTimeMinuteSpin.Text = systemTime.Minute.ToString("00"); _systemTimeMinuteSpin.Text = systemTime.Minute.ToString("00");
//Bind system time events //Bind system time events
_systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged; _systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged;
_systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged; _systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged;
_systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged; _systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged;
_systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged; _systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged;
_systemTimeMinuteSpin.ValueChanged += SystemTimeSpin_ValueChanged; _systemTimeMinuteSpin.ValueChanged += SystemTimeSpin_ValueChanged;
} }
@ -555,7 +553,7 @@ namespace Ryujinx.Ui.Windows
{ {
if (_directoryChanged) if (_directoryChanged)
{ {
List<string> gameDirs = new List<string>(); List<string> gameDirs = new();
_gameDirsBoxStore.GetIterFirst(out TreeIter treeIter); _gameDirsBoxStore.GetIterFirst(out TreeIter treeIter);
@ -611,52 +609,52 @@ namespace Ryujinx.Ui.Windows
DriverUtilities.ToggleOGLThreading(backendThreading == BackendThreading.Off); DriverUtilities.ToggleOGLThreading(backendThreading == BackendThreading.Off);
} }
ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active; ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active;
ConfigurationState.Instance.Logger.EnableTrace.Value = _traceLogToggle.Active; ConfigurationState.Instance.Logger.EnableTrace.Value = _traceLogToggle.Active;
ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active; ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active;
ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active; ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active;
ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active; ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active;
ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active; ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active;
ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active; ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active;
ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active; ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active;
ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active; ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active;
ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value = Enum.Parse<GraphicsDebugLevel>(_graphicsDebugLevel.ActiveId); ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value = Enum.Parse<GraphicsDebugLevel>(_graphicsDebugLevel.ActiveId);
ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active; ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active;
ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active; ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active;
ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active; ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active;
ConfigurationState.Instance.HideCursor.Value = hideCursor; ConfigurationState.Instance.HideCursor.Value = hideCursor;
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active; ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active;
ConfigurationState.Instance.Graphics.EnableTextureRecompression.Value = _textureRecompressionToggle.Active; ConfigurationState.Instance.Graphics.EnableTextureRecompression.Value = _textureRecompressionToggle.Active;
ConfigurationState.Instance.Graphics.EnableMacroHLE.Value = _macroHLEToggle.Active; ConfigurationState.Instance.Graphics.EnableMacroHLE.Value = _macroHLEToggle.Active;
ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active; ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active;
ConfigurationState.Instance.System.EnableInternetAccess.Value = _internetToggle.Active; ConfigurationState.Instance.System.EnableInternetAccess.Value = _internetToggle.Active;
ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active; ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
ConfigurationState.Instance.System.MemoryManagerMode.Value = memoryMode; ConfigurationState.Instance.System.MemoryManagerMode.Value = memoryMode;
ConfigurationState.Instance.System.ExpandRam.Value = _expandRamToggle.Active; ConfigurationState.Instance.System.ExpandRam.Value = _expandRamToggle.Active;
ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active; ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active; ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active;
ConfigurationState.Instance.Hid.EnableMouse.Value = _directMouseAccess.Active; ConfigurationState.Instance.Hid.EnableMouse.Value = _directMouseAccess.Active;
ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active; ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active;
ConfigurationState.Instance.System.Language.Value = Enum.Parse<Language>(_systemLanguageSelect.ActiveId); ConfigurationState.Instance.System.Language.Value = Enum.Parse<Language>(_systemLanguageSelect.ActiveId);
ConfigurationState.Instance.System.Region.Value = Enum.Parse<Common.Configuration.System.Region>(_systemRegionSelect.ActiveId); ConfigurationState.Instance.System.Region.Value = Enum.Parse<Common.Configuration.System.Region>(_systemRegionSelect.ActiveId);
ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset; ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset;
ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text; ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text;
ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text; ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text;
ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value; ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value;
ConfigurationState.Instance.Graphics.MaxAnisotropy.Value = float.Parse(_anisotropy.ActiveId, CultureInfo.InvariantCulture); ConfigurationState.Instance.Graphics.MaxAnisotropy.Value = float.Parse(_anisotropy.ActiveId, CultureInfo.InvariantCulture);
ConfigurationState.Instance.Graphics.AspectRatio.Value = Enum.Parse<AspectRatio>(_aspectRatio.ActiveId); ConfigurationState.Instance.Graphics.AspectRatio.Value = Enum.Parse<AspectRatio>(_aspectRatio.ActiveId);
ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading; ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading;
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId); ConfigurationState.Instance.Graphics.GraphicsBackend.Value = Enum.Parse<GraphicsBackend>(_graphicsBackend.ActiveId);
ConfigurationState.Instance.Graphics.PreferredGpu.Value = _preferredGpu.ActiveId; ConfigurationState.Instance.Graphics.PreferredGpu.Value = _preferredGpu.ActiveId;
ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId); ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId);
ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom; ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom;
ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f; ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f;
ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse<AntiAliasing>(_antiAliasing.ActiveId); ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse<AntiAliasing>(_antiAliasing.ActiveId);
ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse<ScalingFilter>(_scalingFilter.ActiveId); ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse<ScalingFilter>(_scalingFilter.ActiveId);
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId;
_previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value; _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value;
@ -666,7 +664,7 @@ namespace Ryujinx.Ui.Windows
} }
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
_parent.UpdateGraphicsConfig(); MainWindow.UpdateGraphicsConfig();
ThemeHelper.ApplyTheme(); ThemeHelper.ApplyTheme();
} }
@ -692,10 +690,10 @@ namespace Ryujinx.Ui.Windows
private void SystemTimeSpin_ValueChanged(object sender, EventArgs e) private void SystemTimeSpin_ValueChanged(object sender, EventArgs e)
{ {
int year = _systemTimeYearSpin.ValueAsInt; int year = _systemTimeYearSpin.ValueAsInt;
int month = _systemTimeMonthSpin.ValueAsInt; int month = _systemTimeMonthSpin.ValueAsInt;
int day = _systemTimeDaySpin.ValueAsInt; int day = _systemTimeDaySpin.ValueAsInt;
int hour = _systemTimeHourSpin.ValueAsInt; int hour = _systemTimeHourSpin.ValueAsInt;
int minute = _systemTimeMinuteSpin.ValueAsInt; int minute = _systemTimeMinuteSpin.ValueAsInt;
if (!DateTime.TryParse(year + "-" + month + "-" + day + " " + hour + ":" + minute, out DateTime newTime)) if (!DateTime.TryParse(year + "-" + month + "-" + day + " " + hour + ":" + minute, out DateTime newTime))
@ -725,9 +723,9 @@ namespace Ryujinx.Ui.Windows
} }
else else
{ {
FileChooserNative fileChooser = new FileChooserNative("Choose the game directory to add to the list", this, FileChooserAction.SelectFolder, "Add", "Cancel") FileChooserNative fileChooser = new("Choose the game directory to add to the list", this, FileChooserAction.SelectFolder, "Add", "Cancel")
{ {
SelectMultiple = true SelectMultiple = true,
}; };
if (fileChooser.Run() == (int)ResponseType.Accept) if (fileChooser.Run() == (int)ResponseType.Accept)
@ -779,18 +777,18 @@ namespace Ryujinx.Ui.Windows
private void CustThemeToggle_Activated(object sender, EventArgs args) private void CustThemeToggle_Activated(object sender, EventArgs args)
{ {
_custThemePath.Sensitive = _custThemeToggle.Active; _custThemePath.Sensitive = _custThemeToggle.Active;
_custThemePathLabel.Sensitive = _custThemeToggle.Active; _custThemePathLabel.Sensitive = _custThemeToggle.Active;
_browseThemePath.Sensitive = _custThemeToggle.Active; _browseThemePath.Sensitive = _custThemeToggle.Active;
} }
private void BrowseThemeDir_Pressed(object sender, EventArgs args) private void BrowseThemeDir_Pressed(object sender, EventArgs args)
{ {
using (FileChooserNative fileChooser = new FileChooserNative("Choose the theme to load", this, FileChooserAction.Open, "Select", "Cancel")) using (FileChooserNative fileChooser = new("Choose the theme to load", this, FileChooserAction.Open, "Select", "Cancel"))
{ {
FileFilter filter = new FileFilter() FileFilter filter = new()
{ {
Name = "Theme Files" Name = "Theme Files",
}; };
filter.AddPattern("*.css"); filter.AddPattern("*.css");
@ -809,7 +807,7 @@ namespace Ryujinx.Ui.Windows
{ {
((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true);
ControllerWindow controllerWindow = new ControllerWindow(_parent, playerIndex); ControllerWindow controllerWindow = new(_parent, playerIndex);
controllerWindow.SetSizeRequest((int)(controllerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(controllerWindow.DefaultHeight * Program.WindowScaleFactor)); controllerWindow.SetSizeRequest((int)(controllerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(controllerWindow.DefaultHeight * Program.WindowScaleFactor));
controllerWindow.Show(); controllerWindow.Show();

View file

@ -9,33 +9,32 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Widgets; using Ryujinx.Ui.Widgets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;
using SpanHelpers = LibHac.Common.SpanHelpers; using SpanHelpers = LibHac.Common.SpanHelpers;
namespace Ryujinx.Ui.Windows namespace Ryujinx.Ui.Windows
{ {
public class TitleUpdateWindow : Window public class TitleUpdateWindow : Window
{ {
private readonly MainWindow _parent; 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 readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary; private readonly Dictionary<RadioButton, string> _radioButtonToPathDictionary;
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
#pragma warning disable CS0649, IDE0044 #pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier
[GUI] Label _baseTitleInfoLabel; [GUI] Label _baseTitleInfoLabel;
[GUI] Box _availableUpdatesBox; [GUI] Box _availableUpdatesBox;
[GUI] RadioButton _noUpdateRadioButton; [GUI] RadioButton _noUpdateRadioButton;
#pragma warning restore CS0649, IDE0044 #pragma warning restore CS0649, IDE0044
@ -47,21 +46,21 @@ namespace Ryujinx.Ui.Windows
builder.Autoconnect(this); builder.Autoconnect(this);
_titleId = titleId; _titleId = titleId;
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "updates.json"); _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "updates.json");
_radioButtonToPathDictionary = new Dictionary<RadioButton, string>(); _radioButtonToPathDictionary = new Dictionary<RadioButton, string>();
try try
{ {
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, SerializerContext.TitleUpdateMetadata); _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, _serializerContext.TitleUpdateMetadata);
} }
catch catch
{ {
_titleUpdateWindowData = new TitleUpdateMetadata _titleUpdateWindowData = new TitleUpdateMetadata
{ {
Selected = "", Selected = "",
Paths = new List<string>() Paths = new List<string>(),
}; };
} }
@ -89,42 +88,41 @@ namespace Ryujinx.Ui.Windows
{ {
if (File.Exists(path)) if (File.Exists(path))
{ {
using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read)) using FileStream file = new(path, FileMode.Open, FileAccess.Read);
PartitionFileSystem nsp = new(file.AsStorage());
try
{ {
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0);
try if (controlNca != null && patchNca != null)
{ {
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0); ApplicationControlProperty controlData = new();
if (controlNca != null && patchNca != null) using var nacpFile = new UniqueRef<IFile>();
{
ApplicationControlProperty controlData = new ApplicationControlProperty();
using var nacpFile = new UniqueRef<IFile>(); controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); RadioButton radioButton = new($"Version {controlData.DisplayVersionString.ToString()} - {path}");
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); radioButton.JoinGroup(_noUpdateRadioButton);
RadioButton radioButton = new RadioButton($"Version {controlData.DisplayVersionString.ToString()} - {path}"); _availableUpdatesBox.Add(radioButton);
radioButton.JoinGroup(_noUpdateRadioButton); _radioButtonToPathDictionary.Add(radioButton, path);
_availableUpdatesBox.Add(radioButton); radioButton.Show();
_radioButtonToPathDictionary.Add(radioButton, path); radioButton.Active = true;
radioButton.Show();
radioButton.Active = true;
}
else
{
GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!");
}
} }
catch (Exception exception) else
{ {
GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}"); GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!");
} }
} }
catch (Exception exception)
{
GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}");
}
} }
} }
@ -143,24 +141,23 @@ namespace Ryujinx.Ui.Windows
private void AddButton_Clicked(object sender, EventArgs args) private void AddButton_Clicked(object sender, EventArgs args)
{ {
using (FileChooserNative fileChooser = new FileChooserNative("Select update files", this, FileChooserAction.Open, "Add", "Cancel")) using FileChooserNative fileChooser = new("Select update files", this, FileChooserAction.Open, "Add", "Cancel");
fileChooser.SelectMultiple = true;
FileFilter filter = new()
{ {
fileChooser.SelectMultiple = true; Name = "Switch Game Updates",
};
filter.AddPattern("*.nsp");
FileFilter filter = new FileFilter() fileChooser.AddFilter(filter);
if (fileChooser.Run() == (int)ResponseType.Accept)
{
foreach (string path in fileChooser.Filenames)
{ {
Name = "Switch Game Updates" AddUpdate(path);
};
filter.AddPattern("*.nsp");
fileChooser.AddFilter(filter);
if (fileChooser.Run() == (int)ResponseType.Accept)
{
foreach (string path in fileChooser.Filenames)
{
AddUpdate(path);
}
} }
} }
} }
@ -193,7 +190,7 @@ namespace Ryujinx.Ui.Windows
} }
} }
JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, _serializerContext.TitleUpdateMetadata);
_parent.UpdateGameTable(); _parent.UpdateGameTable();

View file

@ -1,45 +1,43 @@
using Gtk; using Gtk;
using Pango; using Pango;
using System;
namespace Ryujinx.Ui.Windows namespace Ryujinx.Ui.Windows
{ {
public partial class UserProfilesManagerWindow : Window public partial class UserProfilesManagerWindow : Window
{ {
private Box _mainBox; private Box _mainBox;
private Label _selectedLabel; private Label _selectedLabel;
private Box _selectedUserBox; private Box _selectedUserBox;
private Image _selectedUserImage; private Image _selectedUserImage;
private VBox _selectedUserInfoBox; private Box _selectedUserInfoBox;
private Entry _selectedUserNameEntry; private Entry _selectedUserNameEntry;
private Label _selectedUserIdLabel; private Label _selectedUserIdLabel;
private VBox _selectedUserButtonsBox; private Box _selectedUserButtonsBox;
private Button _saveProfileNameButton; private Button _saveProfileNameButton;
private Button _changeProfileImageButton; private Button _changeProfileImageButton;
private Box _usersTreeViewBox; private Box _usersTreeViewBox;
private Label _availableUsersLabel; private Label _availableUsersLabel;
private ScrolledWindow _usersTreeViewWindow; private ScrolledWindow _usersTreeViewWindow;
private ListStore _tableStore; private ListStore _tableStore;
private TreeView _usersTreeView; private TreeView _usersTreeView;
private Box _bottomBox; private Box _bottomBox;
private Button _addButton; private Button _addButton;
private Button _deleteButton; private Button _deleteButton;
private Button _closeButton; private Button _closeButton;
private void InitializeComponent() private void InitializeComponent()
{ {
#pragma warning disable CS0612
// //
// UserProfilesManagerWindow // UserProfilesManagerWindow
// //
CanFocus = false; CanFocus = false;
Resizable = false; Resizable = false;
Modal = true; Modal = true;
WindowPosition = WindowPosition.Center; WindowPosition = WindowPosition.Center;
DefaultWidth = 620; DefaultWidth = 620;
DefaultHeight = 548; DefaultHeight = 548;
TypeHint = Gdk.WindowTypeHint.Dialog; TypeHint = Gdk.WindowTypeHint.Dialog;
// //
// _mainBox // _mainBox
@ -51,9 +49,9 @@ namespace Ryujinx.Ui.Windows
// //
_selectedLabel = new Label("Selected User Profile:") _selectedLabel = new Label("Selected User Profile:")
{ {
Margin = 15, Margin = 15,
Attributes = new AttrList(), Attributes = new AttrList(),
Halign = Align.Start Halign = Align.Start,
}; };
_selectedLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); _selectedLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
@ -67,7 +65,7 @@ namespace Ryujinx.Ui.Windows
// //
_selectedUserBox = new Box(Orientation.Horizontal, 0) _selectedUserBox = new Box(Orientation.Horizontal, 0)
{ {
MarginLeft = 30 MarginStart = 30,
}; };
// //
@ -78,15 +76,18 @@ namespace Ryujinx.Ui.Windows
// //
// _selectedUserInfoBox // _selectedUserInfoBox
// //
_selectedUserInfoBox = new VBox(true, 0); _selectedUserInfoBox = new Box(Orientation.Vertical, 0)
{
Homogeneous = true,
};
// //
// _selectedUserNameEntry // _selectedUserNameEntry
// //
_selectedUserNameEntry = new Entry("") _selectedUserNameEntry = new Entry("")
{ {
MarginLeft = 15, MarginStart = 15,
MaxLength = (int)MaxProfileNameLength MaxLength = (int)MaxProfileNameLength,
}; };
_selectedUserNameEntry.KeyReleaseEvent += SelectedUserNameEntry_KeyReleaseEvent; _selectedUserNameEntry.KeyReleaseEvent += SelectedUserNameEntry_KeyReleaseEvent;
@ -95,16 +96,16 @@ namespace Ryujinx.Ui.Windows
// //
_selectedUserIdLabel = new Label("") _selectedUserIdLabel = new Label("")
{ {
MarginTop = 15, MarginTop = 15,
MarginLeft = 15 MarginStart = 15,
}; };
// //
// _selectedUserButtonsBox // _selectedUserButtonsBox
// //
_selectedUserButtonsBox = new VBox() _selectedUserButtonsBox = new Box(Orientation.Vertical, 0)
{ {
MarginRight = 30 MarginEnd = 30,
}; };
// //
@ -112,10 +113,10 @@ namespace Ryujinx.Ui.Windows
// //
_saveProfileNameButton = new Button() _saveProfileNameButton = new Button()
{ {
Label = "Save Profile Name", Label = "Save Profile Name",
CanFocus = true, CanFocus = true,
ReceivesDefault = true, ReceivesDefault = true,
Sensitive = false Sensitive = false,
}; };
_saveProfileNameButton.Clicked += EditProfileNameButton_Pressed; _saveProfileNameButton.Clicked += EditProfileNameButton_Pressed;
@ -124,10 +125,10 @@ namespace Ryujinx.Ui.Windows
// //
_changeProfileImageButton = new Button() _changeProfileImageButton = new Button()
{ {
Label = "Change Profile Image", Label = "Change Profile Image",
CanFocus = true, CanFocus = true,
ReceivesDefault = true, ReceivesDefault = true,
MarginTop = 10 MarginTop = 10,
}; };
_changeProfileImageButton.Clicked += ChangeProfileImageButton_Pressed; _changeProfileImageButton.Clicked += ChangeProfileImageButton_Pressed;
@ -136,9 +137,9 @@ namespace Ryujinx.Ui.Windows
// //
_availableUsersLabel = new Label("Available User Profiles:") _availableUsersLabel = new Label("Available User Profiles:")
{ {
Margin = 15, Margin = 15,
Attributes = new AttrList(), Attributes = new AttrList(),
Halign = Align.Start Halign = Align.Start,
}; };
_availableUsersLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); _availableUsersLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold));
@ -147,12 +148,12 @@ namespace Ryujinx.Ui.Windows
// //
_usersTreeViewWindow = new ScrolledWindow() _usersTreeViewWindow = new ScrolledWindow()
{ {
ShadowType = ShadowType.In, ShadowType = ShadowType.In,
CanFocus = true, CanFocus = true,
Expand = true, Expand = true,
MarginLeft = 30, MarginStart = 30,
MarginRight = 30, MarginEnd = 30,
MarginBottom = 15 MarginBottom = 15,
}; };
// //
@ -175,9 +176,9 @@ namespace Ryujinx.Ui.Windows
// //
_bottomBox = new Box(Orientation.Horizontal, 0) _bottomBox = new Box(Orientation.Horizontal, 0)
{ {
MarginLeft = 30, MarginStart = 30,
MarginRight = 30, MarginEnd = 30,
MarginBottom = 15 MarginBottom = 15,
}; };
// //
@ -185,10 +186,10 @@ namespace Ryujinx.Ui.Windows
// //
_addButton = new Button() _addButton = new Button()
{ {
Label = "Add New Profile", Label = "Add New Profile",
CanFocus = true, CanFocus = true,
ReceivesDefault = true, ReceivesDefault = true,
HeightRequest = 35 HeightRequest = 35,
}; };
_addButton.Clicked += AddButton_Pressed; _addButton.Clicked += AddButton_Pressed;
@ -197,11 +198,11 @@ namespace Ryujinx.Ui.Windows
// //
_deleteButton = new Button() _deleteButton = new Button()
{ {
Label = "Delete Selected Profile", Label = "Delete Selected Profile",
CanFocus = true, CanFocus = true,
ReceivesDefault = true, ReceivesDefault = true,
HeightRequest = 35, HeightRequest = 35,
MarginLeft = 10 MarginStart = 10,
}; };
_deleteButton.Clicked += DeleteButton_Pressed; _deleteButton.Clicked += DeleteButton_Pressed;
@ -210,16 +211,14 @@ namespace Ryujinx.Ui.Windows
// //
_closeButton = new Button() _closeButton = new Button()
{ {
Label = "Close", Label = "Close",
CanFocus = true, CanFocus = true,
ReceivesDefault = true, ReceivesDefault = true,
HeightRequest = 35, HeightRequest = 35,
WidthRequest = 80 WidthRequest = 80,
}; };
_closeButton.Clicked += CloseButton_Pressed; _closeButton.Clicked += CloseButton_Pressed;
#pragma warning restore CS0612
ShowComponent(); ShowComponent();
} }

View file

@ -13,7 +13,6 @@ using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Image = SixLabors.ImageSharp.Image; using Image = SixLabors.ImageSharp.Image;
using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
namespace Ryujinx.Ui.Windows namespace Ryujinx.Ui.Windows
{ {
@ -29,7 +28,7 @@ namespace Ryujinx.Ui.Windows
private Gdk.RGBA _selectedColor; private Gdk.RGBA _selectedColor;
private ManualResetEvent _avatarsPreloadingEvent = new ManualResetEvent(false); private readonly ManualResetEvent _avatarsPreloadingEvent = new(false);
public UserProfilesManagerWindow(AccountManager accountManager, ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage User Profiles") public UserProfilesManagerWindow(AccountManager accountManager, ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage User Profiles")
{ {
@ -37,21 +36,21 @@ namespace Ryujinx.Ui.Windows
InitializeComponent(); InitializeComponent();
_selectedColor.Red = 0.212; _selectedColor.Red = 0.212;
_selectedColor.Green = 0.843; _selectedColor.Green = 0.843;
_selectedColor.Blue = 0.718; _selectedColor.Blue = 0.718;
_selectedColor.Alpha = 1; _selectedColor.Alpha = 1;
_accountManager = accountManager; _accountManager = accountManager;
_contentManager = contentManager; _contentManager = contentManager;
CellRendererToggle userSelectedToggle = new CellRendererToggle(); CellRendererToggle userSelectedToggle = new();
userSelectedToggle.Toggled += UserSelectedToggle_Toggled; userSelectedToggle.Toggled += UserSelectedToggle_Toggled;
// NOTE: Uncomment following line when multiple selection of user profiles is supported. // NOTE: Uncomment following line when multiple selection of user profiles is supported.
//_usersTreeView.AppendColumn("Selected", userSelectedToggle, "active", 0); //_usersTreeView.AppendColumn("Selected", userSelectedToggle, "active", 0);
_usersTreeView.AppendColumn("User Icon", new CellRendererPixbuf(), "pixbuf", 1); _usersTreeView.AppendColumn("User Icon", new CellRendererPixbuf(), "pixbuf", 1);
_usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3); _usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3);
_tableStore.SetSortColumnId(0, SortType.Descending); _tableStore.SetSortColumnId(0, SortType.Descending);
@ -77,8 +76,8 @@ namespace Ryujinx.Ui.Windows
if (userProfile.AccountState == AccountState.Open) if (userProfile.AccountState == AccountState.Open)
{ {
_selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96); _selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96);
_selectedUserIdLabel.Text = userProfile.UserId.ToString(); _selectedUserIdLabel.Text = userProfile.UserId.ToString();
_selectedUserNameEntry.Text = userProfile.Name; _selectedUserNameEntry.Text = userProfile.Name;
_deleteButton.Sensitive = userProfile.UserId != AccountManager.DefaultUserId; _deleteButton.Sensitive = userProfile.UserId != AccountManager.DefaultUserId;
@ -111,7 +110,7 @@ namespace Ryujinx.Ui.Windows
Gdk.Pixbuf userPicture = (Gdk.Pixbuf)_tableStore.GetValue(selectedIter, 1); Gdk.Pixbuf userPicture = (Gdk.Pixbuf)_tableStore.GetValue(selectedIter, 1);
string userName = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[0]; string userName = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[0];
string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1]; string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1];
// Unselect the first user. // Unselect the first user.
_usersTreeView.Model.GetIterFirst(out TreeIter firstIter); _usersTreeView.Model.GetIterFirst(out TreeIter firstIter);
@ -121,9 +120,9 @@ namespace Ryujinx.Ui.Windows
// Set new informations. // Set new informations.
_tableStore.SetValue(selectedIter, 0, true); _tableStore.SetValue(selectedIter, 0, true);
_selectedUserImage.Pixbuf = userPicture; _selectedUserImage.Pixbuf = userPicture;
_selectedUserNameEntry.Text = userName; _selectedUserNameEntry.Text = userName;
_selectedUserIdLabel.Text = userId; _selectedUserIdLabel.Text = userId;
_saveProfileNameButton.Sensitive = false; _saveProfileNameButton.Sensitive = false;
// Open the selected one. // Open the selected one.
@ -178,29 +177,27 @@ namespace Ryujinx.Ui.Windows
private void ProcessProfileImage(byte[] buffer) private void ProcessProfileImage(byte[] buffer)
{ {
using (Image image = Image.Load(buffer)) using Image image = Image.Load(buffer);
{
image.Mutate(x => x.Resize(256, 256));
using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream()) image.Mutate(x => x.Resize(256, 256));
{
image.SaveAsJpeg(streamJpg);
_bufferImageProfile = streamJpg.ToArray(); using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
}
} image.SaveAsJpeg(streamJpg);
_bufferImageProfile = streamJpg.ToArray();
} }
private void ProfileImageFileChooser() private void ProfileImageFileChooser()
{ {
FileChooserNative fileChooser = new FileChooserNative("Import Custom Profile Image", this, FileChooserAction.Open, "Import", "Cancel") FileChooserNative fileChooser = new("Import Custom Profile Image", this, FileChooserAction.Open, "Import", "Cancel")
{ {
SelectMultiple = false SelectMultiple = false,
}; };
FileFilter filter = new FileFilter() FileFilter filter = new()
{ {
Name = "Custom Profile Images" Name = "Custom Profile Images",
}; };
filter.AddPattern("*.jpg"); filter.AddPattern("*.jpg");
filter.AddPattern("*.jpeg"); filter.AddPattern("*.jpeg");
@ -225,10 +222,10 @@ namespace Ryujinx.Ui.Windows
} }
else else
{ {
Dictionary<int, string> buttons = new Dictionary<int, string>() Dictionary<int, string> buttons = new()
{ {
{ 0, "Import Image File" }, { 0, "Import Image File" },
{ 1, "Select Firmware Avatar" } { 1, "Select Firmware Avatar" },
}; };
ResponseType responseDialog = GtkDialog.CreateCustomDialog("Profile Image Selection", ResponseType responseDialog = GtkDialog.CreateCustomDialog("Profile Image Selection",
@ -242,9 +239,9 @@ namespace Ryujinx.Ui.Windows
} }
else if (responseDialog == (ResponseType)1) else if (responseDialog == (ResponseType)1)
{ {
AvatarWindow avatarWindow = new AvatarWindow() AvatarWindow avatarWindow = new()
{ {
NewUser = newUser NewUser = newUser,
}; };
avatarWindow.DeleteEvent += AvatarWindow_DeleteEvent; avatarWindow.DeleteEvent += AvatarWindow_DeleteEvent;