Compare commits

..

7 Commits

Author SHA1 Message Date
gdkchan
a1a4771ac1 Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer (#4337)
* Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer

* Rename FuncPtr to FuncPointer
2023-01-23 22:37:53 +00:00
gdkchan
2fd819613f SPIR-V: Change BitfieldExtract and BitfieldInsert for SPIRV-Cross (#4336)
* SPIR-V: Change BitfieldExtract and BitfieldInsert types to make Metal MSL compiler happy

* Shader cache version bump
2023-01-23 19:20:40 -03:00
TSRBerry
ad6ff6ce99 GUI: Add option to register file types (#4250)
* Add FileAssociationHelper.cs

* Add register file types option to gtk

* Add register file types option to avalonia

* Add Windows support to FileAssociationHelper.cs

* linux: Add uninstall support for file types

* Ignore .glade~ backup files

* Rename Register/Unregister methods

* gtk: Add manage file types submenu

* ava: Add manage file types submenu

* windows: Add uninstall support for file types

* Don't invert uninstall condition (formatting change)

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Add IsTypesRegisteredWindows & Fix Windows install function

* Add AreMimeTypesRegisteredLinux()

* Fix wrong indention

Co-authored-by: AcK77 <acoustik666@gmail.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-22 17:39:00 +00:00
gnisman
dc30d94852 Handle parsing of corrupt Config.json and prevent crash on launch (#4309)
* Handle parsing of corrupt Config.json and prevent crash on launch

* Implement a cleaner solution to handle empty json object
2023-01-22 17:04:33 +01:00
merry
4f293f8cbe Arm64: Simplify TryEncodeBitMask and use for constants (#4328)
* Arm64: Simplify TryEncodeBitMask

* CodeGenerator: Use TryEncodeBitMask in GenerateConstantCopy

* Ptc: Bump version
2023-01-22 14:15:49 +00:00
merry
32a1cd83fd AvaloniaKeyboardDriver: Swallow TextInput events to avoid bell (#4320) 2023-01-22 11:21:52 +01:00
riperiperi
e3d0ccf8d5 Allow setting texture data from 1x to fix some textures resetting randomly (#2860)
* Allow setting texture data from 1x to fix some textures resetting randomly

Expected targets:

- Deltarune 1+2
- Crash Team Racing
- Those new pokemon games idk

* Allow scaling of MSAA textures, propagate scale on copy.

* Fix Rebase

Oops

* Automatic disable

* A bit more aggressive

* Without the debug log

* Actually decrement the score when writing.
2023-01-22 02:03:30 +00:00
26 changed files with 713 additions and 469 deletions

3
.gitignore vendored
View File

@@ -170,3 +170,6 @@ launchSettings.json
# NetCore Publishing Profiles
PublishProfiles/
# Glade backup files
*.glade~

View File

@@ -16,4 +16,10 @@
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Ryujinx.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project>

View File

@@ -1,5 +1,4 @@
using ARMeilleure.IntermediateRepresentation;
using System;
using System.Numerics;
namespace ARMeilleure.CodeGen.Arm64
@@ -32,9 +31,12 @@ namespace ARMeilleure.CodeGen.Arm64
public static bool TryEncodeBitMask(Operand operand, out int immN, out int immS, out int immR)
{
ulong value = operand.Value;
return TryEncodeBitMask(operand.Type, operand.Value, out immN, out immS, out immR);
}
if (operand.Type == OperandType.I32)
public static bool TryEncodeBitMask(OperandType type, ulong value, out int immN, out int immS, out int immR)
{
if (type == OperandType.I32)
{
value |= value << 32;
}
@@ -50,7 +52,7 @@ namespace ARMeilleure.CodeGen.Arm64
// Any value AND all ones will be equal itself, so it's effectively a no-op.
// Any value OR all ones will be equal all ones, so one can just use MOV.
// Any value XOR all ones will be equal its inverse, so one can just use MVN.
if (value == ulong.MaxValue)
if (value == 0 || value == ulong.MaxValue)
{
immN = 0;
immS = 0;
@@ -59,79 +61,18 @@ namespace ARMeilleure.CodeGen.Arm64
return false;
}
int bitLength = CountSequence(value);
// Normalize value, rotating it such that the LSB is 1: Ensures we get a complete element that has not
// been cut-in-half across the word boundary.
int rotation = BitOperations.TrailingZeroCount(value & (value + 1));
ulong rotatedValue = ulong.RotateRight(value, rotation);
if ((value >> bitLength) != 0)
{
bitLength += CountSequence(value >> bitLength);
}
// Now that we have a complete element in the LSB with the LSB = 1, determine size and number of ones
// in element.
int elementSize = BitOperations.TrailingZeroCount(rotatedValue & (rotatedValue + 1));
int onesInElement = BitOperations.TrailingZeroCount(~rotatedValue);
int bitLengthLog2 = BitOperations.Log2((uint)bitLength);
int bitLengthPow2 = 1 << bitLengthLog2;
if (bitLengthPow2 < bitLength)
{
bitLengthLog2++;
bitLengthPow2 <<= 1;
}
int selectedESize = 64;
int repetitions = 1;
int onesCount = BitOperations.PopCount(value);
if (bitLengthPow2 < 64 && (value >> bitLengthPow2) != 0)
{
for (int eSizeLog2 = bitLengthLog2; eSizeLog2 < 6; eSizeLog2++)
{
bool match = true;
int eSize = 1 << eSizeLog2;
ulong mask = (1UL << eSize) - 1;
ulong eValue = value & mask;
for (int e = 1; e < 64 / eSize; e++)
{
if (((value >> (e * eSize)) & mask) != eValue)
{
match = false;
break;
}
}
if (match)
{
selectedESize = eSize;
repetitions = 64 / eSize;
onesCount = BitOperations.PopCount(eValue);
break;
}
}
}
// Find rotation. We have two cases, one where the highest bit is 0
// and one where it is 1.
// If it's 1, we just need to count the number of 1 bits on the MSB to find the right rotation.
// If it's 0, we just need to count the number of 0 bits on the LSB to find the left rotation,
// then we can convert it to the right rotation shift by subtracting the value from the element size.
int rotation;
long vHigh = (long)(value << (64 - selectedESize));
if (vHigh < 0)
{
rotation = BitOperations.LeadingZeroCount(~(ulong)vHigh);
}
else
{
rotation = (selectedESize - BitOperations.TrailingZeroCount(value)) & (selectedESize - 1);
}
// Reconstruct value and see if it matches. If not, we can't encode.
ulong reconstructed = onesCount == 64 ? ulong.MaxValue : RotateRight((1UL << onesCount) - 1, rotation, selectedESize);
for (int bit = 32; bit >= selectedESize; bit >>= 1)
{
reconstructed |= reconstructed << bit;
}
if (reconstructed != value || onesCount == 0)
// Check the value is repeating; also ensures element size is a power of two.
if (ulong.RotateRight(value, elementSize) != value)
{
immN = 0;
immS = 0;
@@ -140,34 +81,11 @@ namespace ARMeilleure.CodeGen.Arm64
return false;
}
immR = rotation;
// immN indicates that there are no repetitions.
// The MSB of immS indicates the amount of repetitions, and the LSB the number of bits set.
if (repetitions == 1)
{
immN = 1;
immS = 0;
}
else
{
immN = 0;
immS = (0xf80 >> BitOperations.Log2((uint)repetitions)) & 0x3f;
}
immS |= onesCount - 1;
immN = (elementSize >> 6) & 1;
immS = (((~elementSize + 1) << 1) | (onesInElement - 1)) & 0x3f;
immR = (elementSize - rotation) & (elementSize - 1);
return true;
}
private static int CountSequence(ulong value)
{
return BitOperations.TrailingZeroCount(value) + BitOperations.TrailingZeroCount(~value);
}
private static ulong RotateRight(ulong bits, int shift, int size)
{
return (bits >> shift) | ((bits << (size - shift)) & (size == 64 ? ulong.MaxValue : (1UL << size) - 1));
}
}
}

View File

@@ -1303,7 +1303,15 @@ namespace ARMeilleure.CodeGen.Arm64
private static void GenerateConstantCopy(CodeGenContext context, Operand dest, ulong value)
{
if (value != 0)
if (value == 0)
{
context.Assembler.Mov(dest, Register(ZrRegister, dest.Type));
}
else if (CodeGenCommon.TryEncodeBitMask(dest.Type, value, out _, out _, out _))
{
context.Assembler.Orr(dest, Register(ZrRegister, dest.Type), Const(dest.Type, (long)value));
}
else
{
int hw = 0;
bool first = true;
@@ -1328,10 +1336,6 @@ namespace ARMeilleure.CodeGen.Arm64
value >>= 16;
}
}
else
{
context.Assembler.Mov(dest, Register(ZrRegister, dest.Type));
}
}
private static void GenerateAtomicCas(

View File

@@ -48,9 +48,21 @@ namespace ARMeilleure.CodeGen
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
public T Map<T>()
{
IntPtr codePtr = JitCache.Map(this);
return MapWithPointer<T>(out _);
}
return Marshal.GetDelegateForFunctionPointer<T>(codePtr);
/// <summary>
/// Maps the <see cref="CompiledFunction"/> onto the <see cref="JitCache"/> and returns a delegate of type
/// <typeparamref name="T"/> pointing to the mapped function.
/// </summary>
/// <typeparam name="T">Type of delegate</typeparam>
/// <param name="codePointer">Pointer to the function code in memory</param>
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
public T MapWithPointer<T>(out IntPtr codePointer)
{
codePointer = JitCache.Map(this);
return Marshal.GetDelegateForFunctionPointer<T>(codePointer);
}
}
}

View File

@@ -191,7 +191,7 @@ namespace ARMeilleure.Instructions
{
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
return (ulong)function.FuncPtr.ToInt64();
return (ulong)function.FuncPointer.ToInt64();
}
public static void InvalidateCacheLine(ulong address)

View File

@@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 4272; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 4328; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";
@@ -745,9 +745,9 @@ namespace ARMeilleure.Translation.PTC
bool highCq)
{
var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty);
var gFunc = cFunc.Map<GuestFunction>();
var gFunc = cFunc.MapWithPointer<GuestFunction>(out IntPtr gFuncPointer);
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
}
private void UpdateInfo(InfoEntry infoEntry)

View File

@@ -1,6 +1,5 @@
using ARMeilleure.Common;
using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
@@ -8,18 +7,18 @@ namespace ARMeilleure.Translation
{
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
public IntPtr FuncPointer { get; }
public Counter<uint> CallCounter { get; }
public ulong GuestSize { get; }
public bool HighCq { get; }
public IntPtr FuncPtr { get; }
public TranslatedFunction(GuestFunction func, Counter<uint> callCounter, ulong guestSize, bool highCq)
public TranslatedFunction(GuestFunction func, IntPtr funcPointer, Counter<uint> callCounter, ulong guestSize, bool highCq)
{
_func = func;
FuncPointer = funcPointer;
CallCounter = callCounter;
GuestSize = guestSize;
HighCq = highCq;
FuncPtr = Marshal.GetFunctionPointerForDelegate(func);
}
public ulong Execute(State.ExecutionContext context)

View File

@@ -211,7 +211,7 @@ namespace ARMeilleure.Translation
if (oldFunc != func)
{
JitCache.Unmap(func.FuncPtr);
JitCache.Unmap(func.FuncPointer);
func = oldFunc;
}
@@ -230,7 +230,7 @@ namespace ARMeilleure.Translation
{
if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq))
{
Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPtr);
Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPointer);
}
}
@@ -292,11 +292,11 @@ namespace ARMeilleure.Translation
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
}
GuestFunction func = compiledFunc.Map<GuestFunction>();
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out IntPtr funcPointer);
Allocators.ResetAll();
return new TranslatedFunction(func, counter, funcSize, highCq);
return new TranslatedFunction(func, funcPointer, counter, funcSize, highCq);
}
private void BackgroundTranslate()
@@ -537,7 +537,7 @@ namespace ARMeilleure.Translation
foreach (var func in functions)
{
JitCache.Unmap(func.FuncPtr);
JitCache.Unmap(func.FuncPointer);
func.CallCounter?.Dispose();
}
@@ -546,7 +546,7 @@ namespace ARMeilleure.Translation
while (_oldFuncs.TryDequeue(out var kv))
{
JitCache.Unmap(kv.Value.FuncPtr);
JitCache.Unmap(kv.Value.FuncPointer);
kv.Value.CallCounter?.Dispose();
}

View File

@@ -26,6 +26,9 @@
"MenuBarToolsInstallFirmware": "Install Firmware",
"MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP",
"MenuBarFileToolsInstallFirmwareFromDirectory": "Install a firmware from a directory",
"MenuBarToolsManageFileTypes": "Manage file types",
"MenuBarToolsInstallFileTypes": "Install file types",
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
"MenuBarHelp": "Help",
"MenuBarHelpCheckForUpdates": "Check for Updates",
"MenuBarHelpAbout": "About",
@@ -339,6 +342,10 @@
"DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\\nThe emulator will now start.",
"DialogFirmwareNoFirmwareInstalledMessage": "No Firmware Installed",
"DialogFirmwareInstalledMessage": "Firmware {0} was installed",
"DialogInstallFileTypesSuccessMessage": "Successfully installed file types!",
"DialogInstallFileTypesErrorMessage": "Failed to install file types.",
"DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!",
"DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.",
"DialogOpenSettingsWindowLabel": "Open Settings Window",
"DialogControllerAppletTitle": "Controller Applet",
"DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}",

View File

@@ -1,5 +1,6 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Input;
using System;
@@ -30,6 +31,7 @@ namespace Ryujinx.Ava.Input
_control.KeyDown += OnKeyPress;
_control.KeyUp += OnKeyRelease;
_control.TextInput += Control_TextInput;
_control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble);
}
private void Control_TextInput(object sender, TextInputEventArgs e)
@@ -37,6 +39,12 @@ namespace Ryujinx.Ava.Input
TextInput?.Invoke(this, e.Text);
}
private void Control_LastChanceTextInput(object sender, TextInputEventArgs e)
{
// Swallow event
e.Handled = true;
}
public event Action<string> OnGamepadConnected
{
add { }

View File

@@ -14,10 +14,8 @@ using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
namespace Ryujinx.Ava
@@ -35,48 +33,6 @@ namespace Ryujinx.Ava
private const uint MB_ICONWARNING = 0x30;
[SupportedOSPlatform("linux")]
static void RegisterMimeTypes()
{
if (ReleaseInformation.IsFlatHubBuild())
{
return;
}
string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime");
if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml")))
{
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
using Process mimeProcess = new();
mimeProcess.StartInfo.FileName = "xdg-mime";
mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}";
mimeProcess.Start();
mimeProcess.WaitForExit();
if (mimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}");
return;
}
using Process updateMimeProcess = new();
updateMimeProcess.StartInfo.FileName = "update-mime-database";
updateMimeProcess.StartInfo.Arguments = mimeDbPath;
updateMimeProcess.Start();
updateMimeProcess.WaitForExit();
if (updateMimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}");
}
}
}
public static void Main(string[] args)
{
Version = ReleaseInformation.GetVersion();
@@ -139,12 +95,6 @@ namespace Ryujinx.Ava
// Initialize the logger system.
LoggerModule.Initialize();
// Register mime types on linux.
if (OperatingSystem.IsLinux())
{
RegisterMimeTypes();
}
// Initialize Discord integration.
DiscordIntegrationModule.Initialize();

View File

@@ -683,6 +683,11 @@ namespace Ryujinx.Ava.UI.ViewModels
get => ConsoleHelper.SetConsoleWindowStateSupported;
}
public bool ManageFileTypesVisible
{
get => FileAssociationHelper.IsTypeAssociationSupported;
}
public ObservableCollection<ApplicationData> Applications
{
get => _applications;

View File

@@ -77,8 +77,7 @@
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}">
</MenuItem>
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
<Separator />
<MenuItem
Click="OpenSettings"
@@ -141,6 +140,10 @@
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
</MenuItem>
<MenuItem Header="{locale:Locale MenuBarToolsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}">
<MenuItem Header="{locale:Locale MenuBarToolsInstallFileTypes}" Click="InstallFileTypes_Click"/>
<MenuItem Header="{locale:Locale MenuBarToolsUninstallFileTypes}" Click="UninstallFileTypes_Click"/>
</MenuItem>
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
<MenuItem

View File

@@ -3,6 +3,7 @@ using Avalonia.Controls;
using Avalonia.Interactivity;
using LibHac.FsSystem;
using LibHac.Ncm;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
@@ -10,6 +11,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS;
using Ryujinx.Modules;
using Ryujinx.Ui.Common.Helper;
using System;
using System.Collections.Generic;
using System.IO;
@@ -163,6 +165,32 @@ namespace Ryujinx.Ava.UI.Views.Main
}
}
private async void InstallFileTypes_Click(object sender, RoutedEventArgs e)
{
if (FileAssociationHelper.Install())
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage],
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]);
}
}
private async void UninstallFileTypes_Click(object sender, RoutedEventArgs e)
{
if (FileAssociationHelper.Uninstall())
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage],
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]);
}
}
public async void CheckForUpdates(object sender, RoutedEventArgs e)
{
if (Updater.CanUpdate(true))

View File

@@ -349,6 +349,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
return;
}
if (srcTexture.Info.Samples > 1 || dstTexture.Info.Samples > 1)
{
srcTexture.PropagateScale(dstTexture);
}
float scale = srcTexture.ScaleFactor;
float dstScale = dstTexture.ScaleFactor;

View File

@@ -25,6 +25,12 @@ namespace Ryujinx.Graphics.Gpu.Image
// This method uses much more memory so we want to avoid it if possible.
private const int ByteComparisonSwitchThreshold = 4;
// Tuning for blacklisting textures from scaling when their data is updated from CPU.
// Each write adds the weight, each GPU modification subtracts 1.
// Exceeding the threshold blacklists the texture.
private const int ScaledSetWeight = 10;
private const int ScaledSetThreshold = 30;
private const int MinLevelsForForceAnisotropy = 5;
private struct TexturePoolOwner
@@ -122,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Image
private Target _arrayViewTarget;
private ITexture _flushHostTexture;
private ITexture _setHostTexture;
private int _scaledSetScore;
private Texture _viewStorage;
@@ -518,6 +526,25 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
/// <summary>
/// Registers when a texture has had its data set after being scaled, and
/// determines if it should be blacklisted from scaling to improve performance.
/// </summary>
/// <returns>True if setting data for a scaled texture is allowed, false if the texture has been blacklisted</returns>
private bool AllowScaledSetData()
{
_scaledSetScore += ScaledSetWeight;
if (_scaledSetScore >= ScaledSetThreshold)
{
BlacklistScale();
return false;
}
return true;
}
/// <summary>
/// Blacklists this texture from being scaled. Resets its scale to 1 if needed.
/// </summary>
@@ -554,9 +581,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
/// </summary>
/// <param name="scale">Scale factor</param>
/// <param name="copy">True if the data should be copied to the texture, false otherwise</param>
/// <param name="storage">Texture to use instead of creating one</param>
/// <returns>A host texture containing a scaled version of this texture</returns>
private ITexture GetScaledHostTexture(float scale, ITexture storage = null)
private ITexture GetScaledHostTexture(float scale, bool copy, ITexture storage = null)
{
if (storage == null)
{
@@ -564,7 +592,10 @@ namespace Ryujinx.Graphics.Gpu.Image
storage = _context.Renderer.CreateTexture(createInfo, scale);
}
if (copy)
{
HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
}
return storage;
}
@@ -595,7 +626,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ScaleFactor = scale;
ITexture newStorage = GetScaledHostTexture(ScaleFactor);
ITexture newStorage = GetScaledHostTexture(ScaleFactor, true);
Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}");
@@ -692,11 +723,6 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void SynchronizeFull()
{
if (_hasData)
{
BlacklistScale();
}
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
// If the host does not support ASTC compression, we need to do the decompression.
@@ -723,7 +749,19 @@ namespace Ryujinx.Graphics.Gpu.Image
SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
if (ScaleFactor != 1f && AllowScaledSetData())
{
// If needed, create a texture to load from 1x scale.
ITexture texture = _setHostTexture = GetScaledHostTexture(1f, false, _setHostTexture);
texture.SetData(result);
texture.CopyTo(HostTexture, new Extents2D(0, 0, texture.Width, texture.Height), new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), true);
}
else
{
HostTexture.SetData(result);
}
_hasData = true;
}
@@ -1056,7 +1094,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (ScaleFactor != 1f)
{
// If needed, create a texture to flush back to host at 1x scale.
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
texture = _flushHostTexture = GetScaledHostTexture(1f, true, _flushHostTexture);
}
return texture;
@@ -1456,6 +1494,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void SignalModified()
{
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
if (_modifiedStale || Group.HasCopyDependencies)
{
_modifiedStale = false;
@@ -1472,6 +1512,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
public void SignalModifying(bool bound)
{
if (bound)
{
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
}
if (_modifiedStale || Group.HasCopyDependencies)
{
_modifiedStale = false;
@@ -1685,6 +1730,9 @@ namespace Ryujinx.Graphics.Gpu.Image
_flushHostTexture?.Release();
_flushHostTexture = null;
_setHostTexture?.Release();
_setHostTexture = null;
}
/// <summary>

View File

@@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>True if eligible</returns>
private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale)
{
if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && !info.FormatInfo.IsCompressed)
if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray || info.Target == Target.Texture2DMultisample) && !info.FormatInfo.IsCompressed)
{
return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired;
}

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4318;
private const uint CodeGenVersion = 4336;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -261,17 +261,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static OperationResult GenerateBitfieldExtractS32(CodeGenContext context, AstOperation operation)
{
return GenerateTernaryS32(context, operation, context.Delegates.BitFieldSExtract);
return GenerateBitfieldExtractS32(context, operation, context.Delegates.BitFieldSExtract);
}
private static OperationResult GenerateBitfieldExtractU32(CodeGenContext context, AstOperation operation)
{
return GenerateTernaryS32(context, operation, context.Delegates.BitFieldUExtract);
return GenerateTernaryU32(context, operation, context.Delegates.BitFieldUExtract);
}
private static OperationResult GenerateBitfieldInsert(CodeGenContext context, AstOperation operation)
{
return GenerateQuaternaryS32(context, operation, context.Delegates.BitFieldInsert);
return GenerateBitfieldInsert(context, operation, context.Delegates.BitFieldInsert);
}
private static OperationResult GenerateBitfieldReverse(CodeGenContext context, AstOperation operation)
@@ -2290,22 +2290,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
}
private static OperationResult GenerateTernaryS32(
CodeGenContext context,
AstOperation operation,
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitS)
{
var src1 = operation.GetSource(0);
var src2 = operation.GetSource(1);
var src3 = operation.GetSource(2);
return new OperationResult(AggregateType.S32, emitS(
context.TypeS32(),
context.GetS32(src1),
context.GetS32(src2),
context.GetS32(src3)));
}
private static OperationResult GenerateTernaryU32(
CodeGenContext context,
AstOperation operation,
@@ -2322,7 +2306,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.GetU32(src3)));
}
private static OperationResult GenerateQuaternaryS32(
private static OperationResult GenerateBitfieldExtractS32(
CodeGenContext context,
AstOperation operation,
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitS)
{
var src1 = operation.GetSource(0);
var src2 = operation.GetSource(1);
var src3 = operation.GetSource(2);
return new OperationResult(AggregateType.S32, emitS(
context.TypeS32(),
context.GetS32(src1),
context.GetU32(src2),
context.GetU32(src3)));
}
private static OperationResult GenerateBitfieldInsert(
CodeGenContext context,
AstOperation operation,
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitS)
@@ -2332,12 +2332,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var src3 = operation.GetSource(2);
var src4 = operation.GetSource(3);
return new OperationResult(AggregateType.S32, emitS(
context.TypeS32(),
context.GetS32(src1),
context.GetS32(src2),
context.GetS32(src3),
context.GetS32(src4)));
return new OperationResult(AggregateType.U32, emitS(
context.TypeU32(),
context.GetU32(src1),
context.GetU32(src2),
context.GetU32(src3),
context.GetU32(src4)));
}
}
}

View File

@@ -0,0 +1,46 @@
using ARMeilleure.CodeGen.Arm64;
using NUnit.Framework;
namespace Ryujinx.Tests.Cpu
{
public class Arm64CodeGenCommonTests
{
public struct TestCase
{
public ulong Value;
public bool Valid;
public int ImmN;
public int ImmS;
public int ImmR;
}
public static readonly TestCase[] TestCases =
{
new() { Value = 0, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
new() { Value = 0x970977f35f848714, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
new() { Value = 0xffffffffffffffff, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
new() { Value = 0x5555555555555555, Valid = true, ImmN = 0, ImmS = 0x3c, ImmR = 0 },
new() { Value = 0xaaaaaaaaaaaaaaaa, Valid = true, ImmN = 0, ImmS = 0x3c, ImmR = 1 },
new() { Value = 0x6666666666666666, Valid = true, ImmN = 0, ImmS = 0x39, ImmR = 3 },
new() { Value = 0x1c1c1c1c1c1c1c1c, Valid = true, ImmN = 0, ImmS = 0x32, ImmR = 6 },
new() { Value = 0x0f0f0f0f0f0f0f0f, Valid = true, ImmN = 0, ImmS = 0x33, ImmR = 0 },
new() { Value = 0xf1f1f1f1f1f1f1f1, Valid = true, ImmN = 0, ImmS = 0x34, ImmR = 4 },
new() { Value = 0xe7e7e7e7e7e7e7e7, Valid = true, ImmN = 0, ImmS = 0x35, ImmR = 3 },
new() { Value = 0xc001c001c001c001, Valid = true, ImmN = 0, ImmS = 0x22, ImmR = 2 },
new() { Value = 0x0000038000000380, Valid = true, ImmN = 0, ImmS = 0x02, ImmR = 25 },
new() { Value = 0xffff8fffffff8fff, Valid = true, ImmN = 0, ImmS = 0x1c, ImmR = 17 },
new() { Value = 0x000000000ffff800, Valid = true, ImmN = 1, ImmS = 0x10, ImmR = 53 },
};
[Test]
public void BitImmTests([ValueSource(nameof(TestCases))] TestCase test)
{
bool valid = CodeGenCommon.TryEncodeBitMask(test.Value, out int immN, out int immS, out int immR);
Assert.That(valid, Is.EqualTo(test.Valid));
Assert.That(immN, Is.EqualTo(test.ImmN));
Assert.That(immS, Is.EqualTo(test.ImmS));
Assert.That(immR, Is.EqualTo(test.ImmR));
}
}
}

View File

@@ -340,7 +340,7 @@ namespace Ryujinx.Ui.Common.Configuration
{
configurationFileFormat = JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path);
return true;
return configurationFileFormat.Version != 0;
}
catch
{

View File

@@ -0,0 +1,198 @@
using Microsoft.Win32;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Ui.Common.Helper
{
public static partial class FileAssociationHelper
{
private static string[] _fileExtensions = new string[] { ".nca", ".nro", ".nso", ".nsp", ".xci" };
[SupportedOSPlatform("linux")]
private static string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime");
private const int SHCNE_ASSOCCHANGED = 0x8000000;
private const int SHCNF_FLUSH = 0x1000;
[LibraryImport("shell32.dll", SetLastError = true)]
public static partial void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()) && !ReleaseInformation.IsFlatHubBuild();
[SupportedOSPlatform("linux")]
private static bool AreMimeTypesRegisteredLinux() => File.Exists(Path.Combine(_mimeDbPath, "packages", "Ryujinx.xml"));
[SupportedOSPlatform("linux")]
private static bool InstallLinuxMimeTypes(bool uninstall = false)
{
string installKeyword = uninstall ? "uninstall" : "install";
if (!AreMimeTypesRegisteredLinux())
{
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
string additionalArgs = !uninstall ? "--novendor" : "";
using Process mimeProcess = new();
mimeProcess.StartInfo.FileName = "xdg-mime";
mimeProcess.StartInfo.Arguments = $"{installKeyword} {additionalArgs} --mode user {mimeTypesFile}";
mimeProcess.Start();
mimeProcess.WaitForExit();
if (mimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Unable to {installKeyword} mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}");
return false;
}
using Process updateMimeProcess = new();
updateMimeProcess.StartInfo.FileName = "update-mime-database";
updateMimeProcess.StartInfo.Arguments = _mimeDbPath;
updateMimeProcess.Start();
updateMimeProcess.WaitForExit();
if (updateMimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}");
}
}
return true;
}
[SupportedOSPlatform("windows")]
private static bool AreMimeTypesRegisteredWindows()
{
static bool CheckRegistering(string ext)
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@$"Software\Classes\{ext}");
if (key is null)
{
return false;
}
key.OpenSubKey(@"shell\open\command");
string keyValue = (string)key.GetValue("");
return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName));
}
bool registered = false;
foreach (string ext in _fileExtensions)
{
registered |= CheckRegistering(ext);
}
return registered;
}
[SupportedOSPlatform("windows")]
private static bool InstallWindowsMimeTypes(bool uninstall = false)
{
static bool RegisterExtension(string ext, bool uninstall = false)
{
string keyString = @$"Software\Classes\{ext}";
if (uninstall)
{
if (!AreMimeTypesRegisteredWindows())
{
return false;
}
Registry.CurrentUser.DeleteSubKeyTree(keyString);
}
else
{
RegistryKey key = Registry.CurrentUser.CreateSubKey(keyString);
if (key is null)
{
return false;
}
key.CreateSubKey(@"shell\open\command");
key.SetValue("", $"\"{Environment.ProcessPath}\" \"%1\"");
key.Close();
}
// Notify Explorer the file association has been changed.
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
return true;
}
bool registered = false;
foreach (string ext in _fileExtensions)
{
registered |= RegisterExtension(ext, uninstall);
}
return registered;
}
public static bool AreMimeTypesRegistered()
{
if (OperatingSystem.IsLinux())
{
return AreMimeTypesRegisteredLinux();
}
if (OperatingSystem.IsWindows())
{
return AreMimeTypesRegisteredWindows();
}
// TODO: Add macOS support.
return false;
}
public static bool Install()
{
if (OperatingSystem.IsLinux())
{
return InstallLinuxMimeTypes();
}
if (OperatingSystem.IsWindows())
{
return InstallWindowsMimeTypes();
}
// TODO: Add macOS support.
return false;
}
public static bool Uninstall()
{
if (OperatingSystem.IsLinux())
{
return InstallLinuxMimeTypes(true);
}
if (OperatingSystem.IsWindows())
{
return InstallWindowsMimeTypes(true);
}
// TODO: Add macOS support.
return false;
}
}
}

View File

@@ -18,7 +18,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
namespace Ryujinx
@@ -73,48 +72,6 @@ namespace Ryujinx
}
}
[SupportedOSPlatform("linux")]
static void RegisterMimeTypes()
{
if (ReleaseInformation.IsFlatHubBuild())
{
return;
}
string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime");
if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml")))
{
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
using Process mimeProcess = new();
mimeProcess.StartInfo.FileName = "xdg-mime";
mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}";
mimeProcess.Start();
mimeProcess.WaitForExit();
if (mimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}");
return;
}
using Process updateMimeProcess = new();
updateMimeProcess.StartInfo.FileName = "update-mime-database";
updateMimeProcess.StartInfo.Arguments = mimeDbPath;
updateMimeProcess.Start();
updateMimeProcess.WaitForExit();
if (updateMimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}");
}
}
}
static void Main(string[] args)
{
Version = ReleaseInformation.GetVersion();
@@ -189,12 +146,6 @@ namespace Ryujinx
// Initialize the logger system.
LoggerModule.Initialize();
// Register mime types on linux.
if (OperatingSystem.IsLinux())
{
RegisterMimeTypes();
}
// Initialize Discord integration.
DiscordIntegrationModule.Initialize();

View File

@@ -44,7 +44,6 @@ using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using GUI = Gtk.Builder.ObjectAttribute;
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
@@ -109,6 +108,7 @@ namespace Ryujinx.Ui
[GUI] CheckMenuItem _favToggle;
[GUI] MenuItem _firmwareInstallDirectory;
[GUI] MenuItem _firmwareInstallFile;
[GUI] MenuItem _fileTypesSubMenu;
[GUI] Label _fifoStatus;
[GUI] CheckMenuItem _iconToggle;
[GUI] CheckMenuItem _developerToggle;
@@ -220,6 +220,8 @@ namespace Ryujinx.Ui
_pauseEmulation.Sensitive = false;
_resumeEmulation.Sensitive = false;
_fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported;
if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true;
if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true;
if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true;
@@ -1500,6 +1502,30 @@ namespace Ryujinx.Ui
});
}
private void InstallFileTypes_Pressed(object sender, EventArgs e)
{
if (FileAssociationHelper.Install())
{
GtkDialog.CreateInfoDialog("Install file types", "File types successfully installed!");
}
else
{
GtkDialog.CreateErrorDialog("Failed to install file types.");
}
}
private void UninstallFileTypes_Pressed(object sender, EventArgs e)
{
if (FileAssociationHelper.Uninstall())
{
GtkDialog.CreateInfoDialog("Uninstall file types", "File types successfully uninstalled!");
}
else
{
GtkDialog.CreateErrorDialog("Failed to uninstall file types.");
}
}
private void HandleRelaunch()
{
if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart)

View File

@@ -1,67 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.21.0 -->
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkApplicationWindow" id="_mainWin">
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="title" translatable="yes">Ryujinx</property>
<property name="window_position">center</property>
<property name="window-position">center</property>
<child>
<object class="GtkBox" id="_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkMenuBar" id="_menuBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_fileMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">File</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_loadApplicationFile">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open a file explorer to choose a Switch compatible file to load</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open a file explorer to choose a Switch compatible file to load</property>
<property name="label" translatable="yes">Load Application from File</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Load_Application_File" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_loadApplicationFolder">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open a file explorer to choose a Switch compatible, unpacked application to load</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open a file explorer to choose a Switch compatible, unpacked application to load</property>
<property name="label" translatable="yes">Load Unpacked Game</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Load_Application_Folder" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_appletMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Load Applet</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="LoadMiiEditApplet">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open Mii Editor Applet in Standalone mode</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open Mii Editor Applet in Standalone mode</property>
<property name="label" translatable="yes">Mii Editor</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Load_Mii_Edit_Applet" swapped="no"/>
</object>
</child>
@@ -72,42 +72,42 @@
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="OpenRyuFolder">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open Ryujinx filesystem folder</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open Ryujinx filesystem folder</property>
<property name="label" translatable="yes">Open Ryujinx Folder</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Open_Ryu_Folder" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="OpenLogsFolder">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Opens the folder where logs are written to.</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Opens the folder where logs are written to.</property>
<property name="label" translatable="yes">Open Logs Folder</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="OpenLogsFolder_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="ExitMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Exit Ryujinx</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Exit Ryujinx</property>
<property name="label" translatable="yes">Exit</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Exit_Pressed" swapped="no"/>
</object>
</child>
@@ -118,144 +118,144 @@
<child>
<object class="GtkMenuItem" id="_optionMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Options</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_fullScreen">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Enter Fullscreen</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_startFullScreen">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Start Games in Fullscreen Mode</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="toggled" handler="StartFullScreen_Toggled" swapped="no"/>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_showConsole">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Show Log Console</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="toggled" handler="ShowConsole_Toggled" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="GUIColumns">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Select which GUI columns to enable</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Select which GUI columns to enable</property>
<property name="label" translatable="yes">Enable GUI Columns</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkCheckMenuItem" id="_favToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Favorite Games Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable Favorite Games Column in the game list</property>
<property name="label" translatable="yes">Enable Favorite Games Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_iconToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Icon Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable Icon Column in the game list</property>
<property name="label" translatable="yes">Enable Icon Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_appToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Title Name/ID Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable Title Name/ID Column in the game list</property>
<property name="label" translatable="yes">Enable Title Name/ID Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_developerToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Developer Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable Developer Column in the game list</property>
<property name="label" translatable="yes">Enable Developer Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_versionToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Version Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable Version Column in the game list</property>
<property name="label" translatable="yes">Enable Version Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_timePlayedToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Time Played Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable Time Played Column in the game list</property>
<property name="label" translatable="yes">Enable Time Played Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_lastPlayedToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Last Played Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable Last Played Column in the game list</property>
<property name="label" translatable="yes">Enable Last Played Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_fileExtToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable file extension column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable file extension column in the game list</property>
<property name="label" translatable="yes">Enable File Ext Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_fileSizeToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable File Size Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable File Size Column in the game list</property>
<property name="label" translatable="yes">Enable File Size Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_pathToggle">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Path Column in the game list</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Enable or Disable Path Column in the game list</property>
<property name="label" translatable="yes">Enable Path Column</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
</object>
@@ -265,26 +265,26 @@
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="SettingsMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open settings window</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open settings window</property>
<property name="label" translatable="yes">Settings</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Settings_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_manageUserProfiles">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open User Profiles Manager window</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open User Profiles Manager window</property>
<property name="label" translatable="yes">Manage User Profiles</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="ManageUserProfiles_Pressed" swapped="no"/>
</object>
</child>
@@ -295,74 +295,74 @@
<child>
<object class="GtkMenuItem" id="_actionMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Actions</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_pauseEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Pause emulation</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Pause emulation</property>
<property name="label" translatable="yes">Pause Emulation</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="PauseEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_resumeEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Resume emulation</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Resume emulation</property>
<property name="label" translatable="yes">Resume Emulation</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="ResumeEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_stopEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Stop emulation of the current game and return to game selection</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Stop emulation of the current game and return to game selection</property>
<property name="label" translatable="yes">Stop Emulation</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="StopEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_simulateWakeUpMessage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Simulate a Wake-up Message</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Simulate a Wake-up Message</property>
<property name="label" translatable="yes">Simulate Wake-up Message</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Simulate_WakeUp_Message_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_scanAmiibo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Scan an Amiibo</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Scan an Amiibo</property>
<property name="label" translatable="yes">Scan an Amiibo</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Scan_Amiibo" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_takeScreenshot">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Take a screenshot</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Take a screenshot</property>
<property name="label" translatable="yes">Take Screenshot</property>
<signal name="activate" handler="Take_Screenshot" swapped="no"/>
</object>
@@ -370,16 +370,16 @@
<child>
<object class="GtkMenuItem" id="_hideUi">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Hide UI (SHOWUIKEY to show)</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="HideUi_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_manageCheats">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Manage Cheats</property>
<signal name="activate" handler="ManageCheats_Pressed" swapped="no"/>
</object>
@@ -391,38 +391,38 @@
<child>
<object class="GtkMenuItem" id="_toolsMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Tools</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="FirmwareSubMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Install Firmware</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_firmwareInstallFile">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Install a firmware from XCI or ZIP</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Installer_File_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_firmwareInstallDirectory">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Install a firmware from a directory</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Installer_Directory_Pressed" swapped="no"/>
</object>
</child>
@@ -430,6 +430,36 @@
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_fileTypesSubMenu">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Manage file types</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_installFileTypes">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Install file types</property>
<signal name="activate" handler="InstallFileTypes_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_uninstallFileTypes">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Uninstall file types</property>
<signal name="activate" handler="UninstallFileTypes_Pressed" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
@@ -437,36 +467,36 @@
<child>
<object class="GtkMenuItem" id="HelpMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Help</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="UpdateMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Check for updates to Ryujinx</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Check for updates to Ryujinx</property>
<property name="label" translatable="yes">Check for Updates</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Update_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="About">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open about window</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open about window</property>
<property name="label" translatable="yes">About</property>
<property name="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="About_Pressed" swapped="no"/>
</object>
</child>
@@ -484,24 +514,24 @@
<child>
<object class="GtkBox" id="_mainBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="_viewBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="_gameTableWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="_gameTable">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can-focus">True</property>
<property name="reorderable">True</property>
<property name="hover_selection">True</property>
<property name="hover-selection">True</property>
<signal name="row-activated" handler="Row_Activated" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="_gameTableSelection"/>
@@ -524,24 +554,24 @@
</child>
<child>
<object class="GtkBox" id="_footerBox">
<property name="height_request">19</property>
<property name="height-request">19</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkBox" id="_listStatusBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<signal name="button-release-event" handler="RefreshList_Pressed" swapped="no"/>
<child>
<object class="GtkImage">
<property name="name">RefreshList</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="stock">gtk-refresh</property>
</object>
</child>
@@ -555,11 +585,11 @@
<child>
<object class="GtkLabel" id="_progressLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">5</property>
<property name="margin_top">2</property>
<property name="margin_bottom">2</property>
<property name="can-focus">False</property>
<property name="margin-left">10</property>
<property name="margin-right">5</property>
<property name="margin-top">2</property>
<property name="margin-bottom">2</property>
<property name="label" translatable="yes">0/0 Games Loaded</property>
</object>
<packing>
@@ -570,13 +600,13 @@
</child>
<child>
<object class="GtkProgressBar" id="_progressBar">
<property name="width_request">200</property>
<property name="width-request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">10</property>
<property name="margin_right">5</property>
<property name="margin_bottom">6</property>
<property name="margin-left">10</property>
<property name="margin-right">5</property>
<property name="margin-bottom">6</property>
</object>
<packing>
<property name="expand">True</property>
@@ -594,19 +624,19 @@
<child>
<object class="GtkBox" id="_statusBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<signal name="button-release-event" handler="VSyncStatus_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_vSyncStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
<property name="label" translatable="yes">VSync</property>
</object>
</child>
@@ -620,7 +650,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -631,15 +661,15 @@
<child>
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<signal name="button-release-event" handler="DockedMode_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_dockedMode">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
</child>
</object>
@@ -652,7 +682,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -663,15 +693,15 @@
<child>
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<signal name="button-release-event" handler="VolumeStatus_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_volumeStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
</child>
</object>
@@ -684,7 +714,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -695,15 +725,15 @@
<child>
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<signal name="button-release-event" handler="AspectRatio_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_aspectRatio">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
</child>
</object>
@@ -716,7 +746,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -727,10 +757,10 @@
<child>
<object class="GtkLabel" id="_gameStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">False</property>
@@ -741,7 +771,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -752,10 +782,10 @@
<child>
<object class="GtkLabel" id="_fifoStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">False</property>
@@ -766,7 +796,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -777,10 +807,10 @@
<child>
<object class="GtkLabel" id="_gpuBackend">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">False</property>
@@ -791,7 +821,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -802,10 +832,10 @@
<child>
<object class="GtkLabel" id="_gpuName">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">True</property>
@@ -823,12 +853,12 @@
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">System Version</property>
</object>
<packing>
@@ -839,16 +869,16 @@
</child>
<child>
<object class="GtkLabel" id="_firmwareVersionLabel">
<property name="width_request">50</property>
<property name="width-request">50</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
@@ -856,15 +886,15 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="pack-type">end</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="_loadingStatusLabel">
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
<property name="label" translatable="yes">0/0 </property>
</object>
<packing>
@@ -875,11 +905,11 @@
</child>
<child>
<object class="GtkProgressBar" id="_loadingStatusBar">
<property name="width_request">200</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_bottom">6</property>
<property name="width-request">200</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
<property name="margin-bottom">6</property>
</object>
<packing>
<property name="expand">False</property>
@@ -903,8 +933,5 @@
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
</interface>