From d562ba37a0bc603e9719bb36dc9e7e9bf4406687 Mon Sep 17 00:00:00 2001
From: jduncanator <1518948+jduncanator@users.noreply.github.com>
Date: Tue, 3 Dec 2019 15:00:56 +1100
Subject: [PATCH 1/8] Fully reverse swkbd configuration structure and follow-up
to swkbd implementation (#839)
* am: Fully reverse swkbd configuration structure
* Add documentation
* Remove explicit access modifiers
* Fix formatting
* Fix further formatting issues
---
.../SoftwareKeyboard/InitialCursorPosition.cs | 18 +++
.../Applets/SoftwareKeyboard/InputFormMode.cs | 18 +++
.../SoftwareKeyboard/InvalidCharFlags.cs | 56 ++++++++
.../Applets/SoftwareKeyboard/KeyboardMode.cs | 28 ++++
.../Applets/SoftwareKeyboard/PasswordMode.cs | 18 +++
.../SoftwareKeyboardApplet.cs | 56 +++++---
.../SoftwareKeyboardConfig.cs | 129 ++++++++++++++++--
.../SoftwareKeyboard/SoftwareKeyboardState.cs | 5 +-
.../SoftwareKeyboard/SoftwareKeyboardType.cs | 20 ---
9 files changed, 295 insertions(+), 53 deletions(-)
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs
create mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs
delete mode 100644 Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardType.cs
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs
new file mode 100644
index 0000000000..727b6d27b1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InitialCursorPosition.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies the initial position of the cursor displayed in the area.
+ ///
+ enum InitialCursorPosition : uint
+ {
+ ///
+ /// Position the cursor at the beginning of the text
+ ///
+ Start,
+
+ ///
+ /// Position the cursor at the end of the text
+ ///
+ End
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs
new file mode 100644
index 0000000000..c3ce2c1251
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InputFormMode.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies the text entry mode.
+ ///
+ enum InputFormMode : uint
+ {
+ ///
+ /// Displays the text entry area as a single-line field.
+ ///
+ SingleLine,
+
+ ///
+ /// Displays the text entry area as a multi-line field.
+ ///
+ MultiLine
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs
new file mode 100644
index 0000000000..f3fd8ac85d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/InvalidCharFlags.cs
@@ -0,0 +1,56 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies prohibited character sets.
+ ///
+ [Flags]
+ enum InvalidCharFlags : uint
+ {
+ ///
+ /// No characters are prohibited.
+ ///
+ None = 0 << 1,
+
+ ///
+ /// Prohibits spaces.
+ ///
+ Space = 1 << 1,
+
+ ///
+ /// Prohibits the at (@) symbol.
+ ///
+ AtSymbol = 1 << 2,
+
+ ///
+ /// Prohibits the percent (%) symbol.
+ ///
+ Percent = 1 << 3,
+
+ ///
+ /// Prohibits the forward slash (/) symbol.
+ ///
+ ForwardSlash = 1 << 4,
+
+ ///
+ /// Prohibits the backward slash (\) symbol.
+ ///
+ BackSlash = 1 << 5,
+
+ ///
+ /// Prohibits numbers.
+ ///
+ Numbers = 1 << 6,
+
+ ///
+ /// Prohibits characters outside of those allowed in download codes.
+ ///
+ DownloadCode = 1 << 7,
+
+ ///
+ /// Prohibits characters outside of those allowed in Mii Nicknames.
+ ///
+ Username = 1 << 8
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs
new file mode 100644
index 0000000000..e5418a6f34
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/KeyboardMode.cs
@@ -0,0 +1,28 @@
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies the variant of keyboard displayed on screen.
+ ///
+ enum KeyboardMode : uint
+ {
+ ///
+ /// A full alpha-numeric keyboard.
+ ///
+ Default,
+
+ ///
+ /// Number pad.
+ ///
+ NumbersOnly,
+
+ ///
+ /// QWERTY (and variants) keyboard only.
+ ///
+ LettersOnly,
+
+ ///
+ /// Unknown keyboard variant.
+ ///
+ Unknown
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs
new file mode 100644
index 0000000000..fc9e1ff8e2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/PasswordMode.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
+{
+ ///
+ /// Identifies the display mode of text in a password field.
+ ///
+ enum PasswordMode : uint
+ {
+ ///
+ /// Display input characters.
+ ///
+ Disabled,
+
+ ///
+ /// Hide input characters.
+ ///
+ Enabled
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
index 22fbe8d0a8..2780446a8b 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs
@@ -9,11 +9,11 @@ namespace Ryujinx.HLE.HOS.Applets
{
internal class SoftwareKeyboardApplet : IApplet
{
- private const string DEFAULT_NUMB = "1";
- private const string DEFAULT_TEXT = "Ryujinx";
+ private const string DefaultNumb = "1";
+ private const string DefaultText = "Ryujinx";
- private const int STANDARD_BUFFER_SIZE = 0x7D8;
- private const int INTERACTIVE_BUFFER_SIZE = 0x7D4;
+ private const int StandardBufferSize = 0x7D8;
+ private const int InteractiveBufferSize = 0x7D4;
private SoftwareKeyboardState _state = SoftwareKeyboardState.Uninitialized;
@@ -22,7 +22,8 @@ namespace Ryujinx.HLE.HOS.Applets
private SoftwareKeyboardConfig _keyboardConfig;
- private string _textValue = DEFAULT_TEXT;
+ private string _textValue = DefaultText;
+ private Encoding _encoding = Encoding.Unicode;
public event EventHandler AppletStateChanged;
@@ -42,6 +43,11 @@ namespace Ryujinx.HLE.HOS.Applets
_keyboardConfig = ReadStruct(keyboardConfig);
+ if (_keyboardConfig.UseUtf8)
+ {
+ _encoding = Encoding.UTF8;
+ }
+
_state = SoftwareKeyboardState.Ready;
Execute();
@@ -58,9 +64,9 @@ namespace Ryujinx.HLE.HOS.Applets
{
// If the keyboard type is numbers only, we swap to a default
// text that only contains numbers.
- if (_keyboardConfig.Type == SoftwareKeyboardType.NumbersOnly)
+ if (_keyboardConfig.Mode == KeyboardMode.NumbersOnly)
{
- _textValue = DEFAULT_NUMB;
+ _textValue = DefaultNumb;
}
// If the max string length is 0, we set it to a large default
@@ -70,6 +76,15 @@ namespace Ryujinx.HLE.HOS.Applets
_keyboardConfig.StringLengthMax = 100;
}
+ // If the game requests a string with a minimum length less
+ // than our default text, repeat our default text until we meet
+ // the minimum length requirement.
+ // This should always be done before the text truncation step.
+ while (_textValue.Length < _keyboardConfig.StringLengthMin)
+ {
+ _textValue = String.Join(" ", _textValue, _textValue);
+ }
+
// If our default text is longer than the allowed length,
// we truncate it.
if (_textValue.Length > _keyboardConfig.StringLengthMax)
@@ -77,7 +92,18 @@ namespace Ryujinx.HLE.HOS.Applets
_textValue = _textValue.Substring(0, (int)_keyboardConfig.StringLengthMax);
}
- if (!_keyboardConfig.CheckText)
+ // Does the application want to validate the text itself?
+ if (_keyboardConfig.CheckText)
+ {
+ // The application needs to validate the response, so we
+ // submit it to the interactive output buffer, and poll it
+ // for validation. Once validated, the application will submit
+ // back a validation status, which is handled in OnInteractiveDataPushIn.
+ _state = SoftwareKeyboardState.ValidationPending;
+
+ _interactiveSession.Push(BuildResponse(_textValue, true));
+ }
+ else
{
// If the application doesn't need to validate the response,
// we push the data to the non-interactive output buffer
@@ -88,16 +114,6 @@ namespace Ryujinx.HLE.HOS.Applets
AppletStateChanged?.Invoke(this, null);
}
- else
- {
- // The application needs to validate the response, so we
- // submit it to the interactive output buffer, and poll it
- // for validation. Once validated, the application will submit
- // back a validation status, which is handled in OnInteractiveDataPushIn.
- _state = SoftwareKeyboardState.ValidationPending;
-
- _interactiveSession.Push(BuildResponse(_textValue, true));
- }
}
private void OnInteractiveData(object sender, EventArgs e)
@@ -136,12 +152,12 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] BuildResponse(string text, bool interactive)
{
- int bufferSize = !interactive ? STANDARD_BUFFER_SIZE : INTERACTIVE_BUFFER_SIZE;
+ int bufferSize = interactive ? InteractiveBufferSize : StandardBufferSize;
using (MemoryStream stream = new MemoryStream(new byte[bufferSize]))
using (BinaryWriter writer = new BinaryWriter(stream))
{
- byte[] output = Encoding.Unicode.GetBytes(text);
+ byte[] output = _encoding.GetBytes(text);
if (!interactive)
{
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs
index 183da774ce..fd462382bc 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs
@@ -2,32 +2,137 @@
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
- // TODO(jduncanator): Define all fields
- [StructLayout(LayoutKind.Explicit)]
+ ///
+ /// A structure that defines the configuration options of the software keyboard.
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct SoftwareKeyboardConfig
{
+ private const int SubmitTextLength = 8;
+ private const int HeaderTextLength = 64;
+ private const int SubtitleTextLength = 128;
+ private const int GuideTextLength = 256;
+
///
/// Type of keyboard.
///
- [FieldOffset(0x0)]
- public SoftwareKeyboardType Type;
+ public KeyboardMode Mode;
///
- /// When non-zero, specifies the max string length. When the input is too long, swkbd will stop accepting more input until text is deleted via the B button (Backspace).
+ /// The string displayed in the Submit button.
///
- [FieldOffset(0x3AC)]
- public uint StringLengthMax;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = SubmitTextLength + 1)]
+ public string SubmitText;
///
- /// When non-zero, specifies the max string length. When the input is too long, swkbd will display an icon and disable the ok-button.
+ /// The character displayed in the left button of the numeric keyboard.
+ /// This is ignored when Mode is not set to NumbersOnly.
///
- [FieldOffset(0x3B0)]
- public uint StringLengthMaxExtended;
+ public char LeftOptionalSymbolKey;
///
- /// When set, the application will validate the entered text whilst the swkbd is still on screen.
+ /// The character displayed in the right button of the numeric keyboard.
+ /// This is ignored when Mode is not set to NumbersOnly.
///
- [FieldOffset(0x3D0), MarshalAs(UnmanagedType.I1)]
+ public char RightOptionalSymbolKey;
+
+ ///
+ /// When set, predictive typing is enabled making use of the system dictionary,
+ /// and any custom user dictionary.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
+ public bool PredictionEnabled;
+
+ ///
+ /// Specifies prohibited characters that cannot be input into the text entry area.
+ ///
+ public InvalidCharFlags InvalidCharFlag;
+
+ ///
+ /// The initial position of the text cursor displayed in the text entry area.
+ ///
+ public InitialCursorPosition InitialCursorPosition;
+
+ ///
+ /// The string displayed in the header area of the keyboard.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HeaderTextLength + 1)]
+ public string HeaderText;
+
+ ///
+ /// The string displayed in the subtitle area of the keyboard.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = SubtitleTextLength + 1)]
+ public string SubtitleText;
+
+ ///
+ /// The placeholder string displayed in the text entry area when no text is entered.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = GuideTextLength + 1)]
+ public string GuideText;
+
+ ///
+ /// When non-zero, specifies the maximum allowed length of the string entered into the text entry area.
+ ///
+ public int StringLengthMax;
+
+ ///
+ /// When non-zero, specifies the minimum allowed length of the string entered into the text entry area.
+ ///
+ public int StringLengthMin;
+
+ ///
+ /// When enabled, hides input characters as dots in the text entry area.
+ ///
+ public PasswordMode PasswordMode;
+
+ ///
+ /// Specifies whether the text entry area is displayed as a single-line entry, or a multi-line entry field.
+ ///
+ public InputFormMode InputFormMode;
+
+ ///
+ /// When set, enables or disables the return key. This value is ignored when single-line entry is specified as the InputFormMode.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
+ public bool UseNewLine;
+
+ ///
+ /// When set, the software keyboard will return a UTF-8 encoded string, rather than UTF-16.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
+ public bool UseUtf8;
+
+ ///
+ /// When set, the software keyboard will blur the game application rendered behind the keyboard.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
+ public bool UseBlurBackground;
+
+ ///
+ /// Offset into the work buffer of the initial text when the keyboard is first displayed.
+ ///
+ public int InitialStringOffset;
+
+ ///
+ /// Length of the initial text.
+ ///
+ public int InitialStringLength;
+
+ ///
+ /// Offset into the work buffer of the custom user dictionary.
+ ///
+ public int CustomDictionaryOffset;
+
+ ///
+ /// Number of entries in the custom user dictionary.
+ ///
+ public int CustomDictionaryCount;
+
+ ///
+ /// When set, the text entered will be validated on the application side after the keyboard has been submitted.
+ ///
+ [MarshalAs(UnmanagedType.I1)]
public bool CheckText;
}
}
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs
index 42a2831ec9..0f66fc9bac 100644
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs
+++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardState.cs
@@ -1,6 +1,9 @@
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
- internal enum SoftwareKeyboardState
+ ///
+ /// Identifies the software keyboard state.
+ ///
+ enum SoftwareKeyboardState
{
///
/// swkbd is uninitialized.
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardType.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardType.cs
deleted file mode 100644
index 4875da8098..0000000000
--- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardType.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
-{
- internal enum SoftwareKeyboardType : uint
- {
- ///
- /// Normal keyboard.
- ///
- Default = 0,
-
- ///
- /// Number pad. The buttons at the bottom left/right are only available when they're set in the config by leftButtonText / rightButtonText.
- ///
- NumbersOnly = 1,
-
- ///
- /// QWERTY (and variants) keyboard only.
- ///
- LettersOnly = 2
- }
-}
\ No newline at end of file
From 8c85bdf2edf5ebd7965fbbd08106f2e8d877d73e Mon Sep 17 00:00:00 2001
From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com>
Date: Sat, 7 Dec 2019 13:45:32 +0100
Subject: [PATCH 2/8] Implemented fast paths for: (#841)
* cpu-misc_opt
* B = ~b
* ;
---
ARMeilleure/CodeGen/X86/Assembler.cs | 6 +
ARMeilleure/CodeGen/X86/CodeGenerator.cs | 12 +-
ARMeilleure/CodeGen/X86/IntrinsicTable.cs | 4 +
ARMeilleure/CodeGen/X86/PreAllocator.cs | 7 +-
ARMeilleure/CodeGen/X86/X86Instruction.cs | 6 +
ARMeilleure/Common/BitUtils.cs | 62 +++--
ARMeilleure/Decoders/DecoderHelper.cs | 97 +++++--
ARMeilleure/Decoders/OpCodeSimdFmov.cs | 17 +-
ARMeilleure/Decoders/OpCodeSimdImm.cs | 17 +-
ARMeilleure/Instructions/InstEmitAluHelper.cs | 2 +-
.../Instructions/InstEmitSimdArithmetic.cs | 261 ++++++++++++++----
ARMeilleure/Instructions/InstEmitSimdCmp.cs | 22 +-
.../Instructions/InstEmitSimdHelper.cs | 5 +-
ARMeilleure/Instructions/InstEmitSimdMove.cs | 59 ++--
ARMeilleure/Instructions/SoftFloat.cs | 32 ++-
.../IntermediateRepresentation/Intrinsic.cs | 4 +
16 files changed, 450 insertions(+), 163 deletions(-)
diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs
index ee80d892b0..24a122c33b 100644
--- a/ARMeilleure/CodeGen/X86/Assembler.cs
+++ b/ARMeilleure/CodeGen/X86/Assembler.cs
@@ -75,6 +75,10 @@ namespace ARMeilleure.CodeGen.X86
Add(X86Instruction.And, new InstructionInfo(0x00000021, 0x04000083, 0x04000081, BadOp, 0x00000023, InstructionFlags.None));
Add(X86Instruction.Andnpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Andnps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f55, InstructionFlags.Vex));
+ Add(X86Instruction.Andpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex | InstructionFlags.Prefix66));
+ Add(X86Instruction.Andps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f54, InstructionFlags.Vex));
+ Add(X86Instruction.Blendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3815, InstructionFlags.Prefix66));
+ Add(X86Instruction.Blendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3814, InstructionFlags.Prefix66));
Add(X86Instruction.Bsr, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fbd, InstructionFlags.None));
Add(X86Instruction.Bswap, new InstructionInfo(0x00000fc8, BadOp, BadOp, BadOp, BadOp, InstructionFlags.RegOnly));
Add(X86Instruction.Call, new InstructionInfo(0x020000ff, BadOp, BadOp, BadOp, BadOp, InstructionFlags.None));
@@ -245,6 +249,8 @@ namespace ARMeilleure.CodeGen.X86
Add(X86Instruction.Unpckhps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f15, InstructionFlags.Vex));
Add(X86Instruction.Unpcklpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Unpcklps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f14, InstructionFlags.Vex));
+ Add(X86Instruction.Vblendvpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4b, InstructionFlags.Vex | InstructionFlags.Prefix66));
+ Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None));
Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66));
diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs
index 33fc2aee3e..d1224363c0 100644
--- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs
+++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs
@@ -336,7 +336,15 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(!dest.Type.IsInteger());
- if (info.Inst == X86Instruction.Pblendvb && HardwareCapabilities.SupportsVexEncoding)
+ if (info.Inst == X86Instruction.Blendvpd && HardwareCapabilities.SupportsVexEncoding)
+ {
+ context.Assembler.WriteInstruction(X86Instruction.Vblendvpd, dest, src1, src2, src3);
+ }
+ else if (info.Inst == X86Instruction.Blendvps && HardwareCapabilities.SupportsVexEncoding)
+ {
+ context.Assembler.WriteInstruction(X86Instruction.Vblendvps, dest, src1, src2, src3);
+ }
+ else if (info.Inst == X86Instruction.Pblendvb && HardwareCapabilities.SupportsVexEncoding)
{
context.Assembler.WriteInstruction(X86Instruction.Vpblendvb, dest, src1, src2, src3);
}
@@ -1646,7 +1654,7 @@ namespace ARMeilleure.CodeGen.X86
for (int offset = PageSize; offset < size; offset += PageSize)
{
- Operand memOp = new MemoryOperand(OperandType.I32, rsp, null, Multiplier.x1, -offset);;
+ Operand memOp = new MemoryOperand(OperandType.I32, rsp, null, Multiplier.x1, -offset);
context.Assembler.Mov(temp, memOp, OperandType.I32);
}
diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs
index 73fb5fd1b6..e87de03509 100644
--- a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs
+++ b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs
@@ -19,6 +19,10 @@ namespace ARMeilleure.CodeGen.X86
Add(Intrinsic.X86Addss, new IntrinsicInfo(X86Instruction.Addss, IntrinsicType.Binary));
Add(Intrinsic.X86Andnpd, new IntrinsicInfo(X86Instruction.Andnpd, IntrinsicType.Binary));
Add(Intrinsic.X86Andnps, new IntrinsicInfo(X86Instruction.Andnps, IntrinsicType.Binary));
+ Add(Intrinsic.X86Andpd, new IntrinsicInfo(X86Instruction.Andpd, IntrinsicType.Binary));
+ Add(Intrinsic.X86Andps, new IntrinsicInfo(X86Instruction.Andps, IntrinsicType.Binary));
+ Add(Intrinsic.X86Blendvpd, new IntrinsicInfo(X86Instruction.Blendvpd, IntrinsicType.Ternary));
+ Add(Intrinsic.X86Blendvps, new IntrinsicInfo(X86Instruction.Blendvps, IntrinsicType.Ternary));
Add(Intrinsic.X86Cmppd, new IntrinsicInfo(X86Instruction.Cmppd, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Cmpps, new IntrinsicInfo(X86Instruction.Cmpps, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Cmpsd, new IntrinsicInfo(X86Instruction.Cmpsd, IntrinsicType.TernaryImm));
diff --git a/ARMeilleure/CodeGen/X86/PreAllocator.cs b/ARMeilleure/CodeGen/X86/PreAllocator.cs
index a149013115..034a87ac25 100644
--- a/ARMeilleure/CodeGen/X86/PreAllocator.cs
+++ b/ARMeilleure/CodeGen/X86/PreAllocator.cs
@@ -298,8 +298,11 @@ namespace ARMeilleure.CodeGen.X86
{
IntrinsicOperation intrinOp = (IntrinsicOperation)operation;
- // PBLENDVB last operand is always implied to be XMM0 when VEX is not supported.
- if (intrinOp.Intrinsic == Intrinsic.X86Pblendvb && !HardwareCapabilities.SupportsVexEncoding)
+ // BLENDVPD, BLENDVPS, PBLENDVB last operand is always implied to be XMM0 when VEX is not supported.
+ if ((intrinOp.Intrinsic == Intrinsic.X86Blendvpd ||
+ intrinOp.Intrinsic == Intrinsic.X86Blendvps ||
+ intrinOp.Intrinsic == Intrinsic.X86Pblendvb) &&
+ !HardwareCapabilities.SupportsVexEncoding)
{
Operand xmm0 = Xmm(X86Register.Xmm0, OperandType.V128);
diff --git a/ARMeilleure/CodeGen/X86/X86Instruction.cs b/ARMeilleure/CodeGen/X86/X86Instruction.cs
index 10ba891aa5..a29e68fb6f 100644
--- a/ARMeilleure/CodeGen/X86/X86Instruction.cs
+++ b/ARMeilleure/CodeGen/X86/X86Instruction.cs
@@ -10,6 +10,10 @@ namespace ARMeilleure.CodeGen.X86
And,
Andnpd,
Andnps,
+ Andpd,
+ Andps,
+ Blendvpd,
+ Blendvps,
Bsr,
Bswap,
Call,
@@ -180,6 +184,8 @@ namespace ARMeilleure.CodeGen.X86
Unpckhps,
Unpcklpd,
Unpcklps,
+ Vblendvpd,
+ Vblendvps,
Vpblendvb,
Xor,
Xorpd,
diff --git a/ARMeilleure/Common/BitUtils.cs b/ARMeilleure/Common/BitUtils.cs
index 55344608cc..7a29dcff7f 100644
--- a/ARMeilleure/Common/BitUtils.cs
+++ b/ARMeilleure/Common/BitUtils.cs
@@ -1,12 +1,12 @@
-using System.Runtime.CompilerServices;
-
namespace ARMeilleure.Common
{
static class BitUtils
{
private const int DeBrujinSequence = 0x77cb531;
- private static int[] DeBrujinLbsLut;
+ private static readonly int[] DeBrujinLbsLut;
+
+ private static readonly sbyte[] HbsNibbleLut;
static BitUtils()
{
@@ -18,19 +18,27 @@ namespace ARMeilleure.Common
DeBrujinLbsLut[lutIndex] = index;
}
+
+ HbsNibbleLut = new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int LowestBitSet(int value)
+ public static int CountBits(int value)
{
- if (value == 0)
+ int count = 0;
+
+ while (value != 0)
{
- return -1;
+ value &= ~(value & -value);
+
+ count++;
}
- int lsb = value & -value;
+ return count;
+ }
- return DeBrujinLbsLut[(uint)(DeBrujinSequence * lsb) >> 27];
+ public static long FillWithOnes(int bits)
+ {
+ return bits == 64 ? -1L : (1L << bits) - 1;
}
public static int HighestBitSet(int value)
@@ -51,9 +59,22 @@ namespace ARMeilleure.Common
return -1;
}
- private static readonly sbyte[] HbsNibbleLut = { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
+ public static int HighestBitSetNibble(int value)
+ {
+ return HbsNibbleLut[value];
+ }
- public static int HighestBitSetNibble(int value) => HbsNibbleLut[value & 0b1111];
+ public static int LowestBitSet(int value)
+ {
+ if (value == 0)
+ {
+ return -1;
+ }
+
+ int lsb = value & -value;
+
+ return DeBrujinLbsLut[(uint)(DeBrujinSequence * lsb) >> 27];
+ }
public static long Replicate(long bits, int size)
{
@@ -67,25 +88,6 @@ namespace ARMeilleure.Common
return output;
}
- public static int CountBits(int value)
- {
- int count = 0;
-
- while (value != 0)
- {
- value &= ~(value & -value);
-
- count++;
- }
-
- return count;
- }
-
- public static long FillWithOnes(int bits)
- {
- return bits == 64 ? -1L : (1L << bits) - 1;
- }
-
public static int RotateRight(int bits, int shift, int size)
{
return (int)RotateRight((uint)bits, shift, size);
diff --git a/ARMeilleure/Decoders/DecoderHelper.cs b/ARMeilleure/Decoders/DecoderHelper.cs
index 3cbd49123d..bc41c61c6a 100644
--- a/ARMeilleure/Decoders/DecoderHelper.cs
+++ b/ARMeilleure/Decoders/DecoderHelper.cs
@@ -1,10 +1,77 @@
using ARMeilleure.Common;
-using System;
namespace ARMeilleure.Decoders
{
static class DecoderHelper
{
+ static DecoderHelper()
+ {
+ Imm8ToFP32Table = BuildImm8ToFP32Table();
+ Imm8ToFP64Table = BuildImm8ToFP64Table();
+ }
+
+ public static readonly uint[] Imm8ToFP32Table;
+ public static readonly ulong[] Imm8ToFP64Table;
+
+ private static uint[] BuildImm8ToFP32Table()
+ {
+ uint[] tbl = new uint[256];
+
+ for (int idx = 0; idx < 256; idx++)
+ {
+ tbl[idx] = ExpandImm8ToFP32((uint)idx);
+ }
+
+ return tbl;
+ }
+
+ private static ulong[] BuildImm8ToFP64Table()
+ {
+ ulong[] tbl = new ulong[256];
+
+ for (int idx = 0; idx < 256; idx++)
+ {
+ tbl[idx] = ExpandImm8ToFP64((ulong)idx);
+ }
+
+ return tbl;
+ }
+
+ // abcdefgh -> aBbbbbbc defgh000 00000000 00000000 (B = ~b)
+ private static uint ExpandImm8ToFP32(uint imm)
+ {
+ uint MoveBit(uint bits, int from, int to)
+ {
+ return ((bits >> from) & 1U) << to;
+ }
+
+ return MoveBit(imm, 7, 31) | MoveBit(~imm, 6, 30) |
+ MoveBit(imm, 6, 29) | MoveBit( imm, 6, 28) |
+ MoveBit(imm, 6, 27) | MoveBit( imm, 6, 26) |
+ MoveBit(imm, 6, 25) | MoveBit( imm, 5, 24) |
+ MoveBit(imm, 4, 23) | MoveBit( imm, 3, 22) |
+ MoveBit(imm, 2, 21) | MoveBit( imm, 1, 20) |
+ MoveBit(imm, 0, 19);
+ }
+
+ // abcdefgh -> aBbbbbbb bbcdefgh 00000000 00000000 00000000 00000000 00000000 00000000 (B = ~b)
+ private static ulong ExpandImm8ToFP64(ulong imm)
+ {
+ ulong MoveBit(ulong bits, int from, int to)
+ {
+ return ((bits >> from) & 1UL) << to;
+ }
+
+ return MoveBit(imm, 7, 63) | MoveBit(~imm, 6, 62) |
+ MoveBit(imm, 6, 61) | MoveBit( imm, 6, 60) |
+ MoveBit(imm, 6, 59) | MoveBit( imm, 6, 58) |
+ MoveBit(imm, 6, 57) | MoveBit( imm, 6, 56) |
+ MoveBit(imm, 6, 55) | MoveBit( imm, 6, 54) |
+ MoveBit(imm, 5, 53) | MoveBit( imm, 4, 52) |
+ MoveBit(imm, 3, 51) | MoveBit( imm, 2, 50) |
+ MoveBit(imm, 1, 49) | MoveBit( imm, 0, 48);
+ }
+
public struct BitMask
{
public long WMask;
@@ -62,34 +129,6 @@ namespace ARMeilleure.Decoders
};
}
- public static long DecodeImm8Float(long imm, int size)
- {
- int e = 0, f = 0;
-
- switch (size)
- {
- case 0: e = 8; f = 23; break;
- case 1: e = 11; f = 52; break;
-
- default: throw new ArgumentOutOfRangeException(nameof(size));
- }
-
- long value = (imm & 0x3f) << f - 4;
-
- long eBit = (imm >> 6) & 1;
- long sBit = (imm >> 7) & 1;
-
- if (eBit != 0)
- {
- value |= (1L << e - 3) - 1 << f + 2;
- }
-
- value |= (eBit ^ 1) << f + e - 1;
- value |= sBit << f + e;
-
- return value;
- }
-
public static long DecodeImm24_2(int opCode)
{
return ((long)opCode << 40) >> 38;
diff --git a/ARMeilleure/Decoders/OpCodeSimdFmov.cs b/ARMeilleure/Decoders/OpCodeSimdFmov.cs
index 61a3f077d9..f0da03968d 100644
--- a/ARMeilleure/Decoders/OpCodeSimdFmov.cs
+++ b/ARMeilleure/Decoders/OpCodeSimdFmov.cs
@@ -8,16 +8,8 @@ namespace ARMeilleure.Decoders
public OpCodeSimdFmov(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
- int imm5 = (opCode >> 5) & 0x1f;
int type = (opCode >> 22) & 0x3;
- if (imm5 != 0b00000 || type > 1)
- {
- Instruction = InstDescriptor.Undefined;
-
- return;
- }
-
Size = type;
long imm;
@@ -25,7 +17,14 @@ namespace ARMeilleure.Decoders
Rd = (opCode >> 0) & 0x1f;
imm = (opCode >> 13) & 0xff;
- Immediate = DecoderHelper.DecodeImm8Float(imm, type);
+ if (type == 0)
+ {
+ Immediate = (long)DecoderHelper.Imm8ToFP32Table[(int)imm];
+ }
+ else /* if (type == 1) */
+ {
+ Immediate = (long)DecoderHelper.Imm8ToFP64Table[(int)imm];
+ }
}
}
}
\ No newline at end of file
diff --git a/ARMeilleure/Decoders/OpCodeSimdImm.cs b/ARMeilleure/Decoders/OpCodeSimdImm.cs
index ecad906d91..a88e360ee4 100644
--- a/ARMeilleure/Decoders/OpCodeSimdImm.cs
+++ b/ARMeilleure/Decoders/OpCodeSimdImm.cs
@@ -23,19 +23,19 @@ namespace ARMeilleure.Decoders
if (modeHigh == 0b111)
{
- Size = modeLow != 0 ? op : 3;
-
switch (op | (modeLow << 1))
{
case 0:
// 64-bits Immediate.
// Transform abcd efgh into abcd efgh abcd efgh ...
+ Size = 3;
imm = (long)((ulong)imm * 0x0101010101010101);
break;
case 1:
// 64-bits Immediate.
// Transform abcd efgh into aaaa aaaa bbbb bbbb ...
+ Size = 3;
imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4;
imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2;
imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1;
@@ -49,9 +49,16 @@ namespace ARMeilleure.Decoders
break;
case 2:
+ // 2 x 32-bits floating point Immediate.
+ Size = 0;
+ imm = (long)DecoderHelper.Imm8ToFP32Table[(int)imm];
+ imm |= imm << 32;
+ break;
+
case 3:
- // Floating point Immediate.
- imm = DecoderHelper.DecodeImm8Float(imm, Size);
+ // 64-bits floating point Immediate.
+ Size = 1;
+ imm = (long)DecoderHelper.Imm8ToFP64Table[(int)imm];
break;
}
}
@@ -72,7 +79,7 @@ namespace ARMeilleure.Decoders
}
else
{
- // 8 bits without shift.
+ // 8-bits without shift.
Size = 0;
}
diff --git a/ARMeilleure/Instructions/InstEmitAluHelper.cs b/ARMeilleure/Instructions/InstEmitAluHelper.cs
index 81d5c9eb34..d032b32e87 100644
--- a/ARMeilleure/Instructions/InstEmitAluHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitAluHelper.cs
@@ -268,7 +268,7 @@ namespace ARMeilleure.Instructions
{
if (setCarry)
{
- SetFlag(context, PState.CFlag, Const(0));;
+ SetFlag(context, PState.CFlag, Const(0));
}
return Const(0);
diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
index c411a6d3fc..1a9e01c8da 100644
--- a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
@@ -384,8 +384,7 @@ namespace ARMeilleure.Instructions
}
else
{
- OperandType type = sizeF != 0 ? OperandType.FP64
- : OperandType.FP32;
+ OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
Operand ne0 = context.VectorExtract(type, GetVec(op.Rn), 0);
Operand ne1 = context.VectorExtract(type, GetVec(op.Rn), 1);
@@ -455,6 +454,7 @@ namespace ARMeilleure.Instructions
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+ Operand d = GetVec(op.Rd);
Operand a = GetVec(op.Ra);
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
@@ -462,18 +462,16 @@ namespace ARMeilleure.Instructions
if (op.Size == 0)
{
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
- res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
-
- context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
+ context.Copy(d, context.VectorZeroUpper96(res));
}
else /* if (op.Size == 1) */
{
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
- res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
-
- context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
+ context.Copy(d, context.VectorZeroUpper64(res));
}
}
else
@@ -517,18 +515,32 @@ namespace ARMeilleure.Instructions
public static void Fmaxnm_S(ArmEmitterContext context)
{
- EmitScalarBinaryOpF(context, (op1, op2) =>
+ if (Optimizations.FastFP && Optimizations.UseSse41)
{
- return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2);
- });
+ EmitSse41MaxMinNumOpF(context, isMaxNum: true, scalar: true);
+ }
+ else
+ {
+ EmitScalarBinaryOpF(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2);
+ });
+ }
}
public static void Fmaxnm_V(ArmEmitterContext context)
{
- EmitVectorBinaryOpF(context, (op1, op2) =>
+ if (Optimizations.FastFP && Optimizations.UseSse41)
{
- return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2);
- });
+ EmitSse41MaxMinNumOpF(context, isMaxNum: true, scalar: false);
+ }
+ else
+ {
+ EmitVectorBinaryOpF(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2);
+ });
+ }
}
public static void Fmaxp_V(ArmEmitterContext context)
@@ -578,18 +590,32 @@ namespace ARMeilleure.Instructions
public static void Fminnm_S(ArmEmitterContext context)
{
- EmitScalarBinaryOpF(context, (op1, op2) =>
+ if (Optimizations.FastFP && Optimizations.UseSse41)
{
- return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2);
- });
+ EmitSse41MaxMinNumOpF(context, isMaxNum: false, scalar: true);
+ }
+ else
+ {
+ EmitScalarBinaryOpF(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2);
+ });
+ }
}
public static void Fminnm_V(ArmEmitterContext context)
{
- EmitVectorBinaryOpF(context, (op1, op2) =>
+ if (Optimizations.FastFP && Optimizations.UseSse41)
{
- return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2);
- });
+ EmitSse41MaxMinNumOpF(context, isMaxNum: false, scalar: false);
+ }
+ else
+ {
+ EmitVectorBinaryOpF(context, (op1, op2) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2);
+ });
+ }
}
public static void Fminp_V(ArmEmitterContext context)
@@ -813,6 +839,7 @@ namespace ARMeilleure.Instructions
{
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+ Operand d = GetVec(op.Rd);
Operand a = GetVec(op.Ra);
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
@@ -820,18 +847,16 @@ namespace ARMeilleure.Instructions
if (op.Size == 0)
{
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
- res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
-
- context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
+ context.Copy(d, context.VectorZeroUpper96(res));
}
else /* if (op.Size == 1) */
{
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
- res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
-
- context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
+ context.Copy(d, context.VectorZeroUpper64(res));
}
}
else
@@ -1035,36 +1060,88 @@ namespace ARMeilleure.Instructions
public static void Fnmadd_S(ArmEmitterContext context) // Fused.
{
- OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+ if (Optimizations.FastFP && Optimizations.UseSse2)
+ {
+ OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
- int sizeF = op.Size & 1;
+ Operand d = GetVec(op.Rd);
+ Operand a = GetVec(op.Ra);
+ Operand n = GetVec(op.Rn);
+ Operand m = GetVec(op.Rm);
- OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+ if (op.Size == 0)
+ {
+ Operand mask = X86GetScalar(context, -0f);
- Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
- Operand me = context.VectorExtract(type, GetVec(op.Rm), 0);
- Operand ae = context.VectorExtract(type, GetVec(op.Ra), 0);
+ Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
- Operand res = context.Subtract(context.Multiply(context.Negate(ne), me), ae);
+ Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
- context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
+ context.Copy(d, context.VectorZeroUpper96(res));
+ }
+ else /* if (op.Size == 1) */
+ {
+ Operand mask = X86GetScalar(context, -0d);
+
+ Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
+
+ Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
+
+ context.Copy(d, context.VectorZeroUpper64(res));
+ }
+ }
+ else
+ {
+ EmitScalarTernaryRaOpF(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPNegMulAdd, SoftFloat64.FPNegMulAdd, op1, op2, op3);
+ });
+ }
}
public static void Fnmsub_S(ArmEmitterContext context) // Fused.
{
- OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+ if (Optimizations.FastFP && Optimizations.UseSse2)
+ {
+ OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
- int sizeF = op.Size & 1;
+ Operand d = GetVec(op.Rd);
+ Operand a = GetVec(op.Ra);
+ Operand n = GetVec(op.Rn);
+ Operand m = GetVec(op.Rm);
- OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
+ if (op.Size == 0)
+ {
+ Operand mask = X86GetScalar(context, -0f);
- Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
- Operand me = context.VectorExtract(type, GetVec(op.Rm), 0);
- Operand ae = context.VectorExtract(type, GetVec(op.Ra), 0);
+ Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
- Operand res = context.Subtract(context.Multiply(ne, me), ae);
+ Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
- context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
+ context.Copy(d, context.VectorZeroUpper96(res));
+ }
+ else /* if (op.Size == 1) */
+ {
+ Operand mask = X86GetScalar(context, -0d);
+
+ Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
+
+ Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
+ res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
+
+ context.Copy(d, context.VectorZeroUpper64(res));
+ }
+ }
+ else
+ {
+ EmitScalarTernaryRaOpF(context, (op1, op2, op3) =>
+ {
+ return EmitSoftFloatCall(context, SoftFloat32.FPNegMulSub, SoftFloat64.FPNegMulSub, op1, op2, op3);
+ });
+ }
}
public static void Fnmul_S(ArmEmitterContext context)
@@ -2067,9 +2144,7 @@ namespace ARMeilleure.Instructions
m = context.AddIntrinsic(Intrinsic.X86Psrldq, m, Const(8));
}
- Intrinsic movInst = op.Size == 0
- ? Intrinsic.X86Pmovsxbw
- : Intrinsic.X86Pmovsxwd;
+ Intrinsic movInst = op.Size == 0 ? Intrinsic.X86Pmovsxbw : Intrinsic.X86Pmovsxwd;
n = context.AddIntrinsic(movInst, n);
m = context.AddIntrinsic(movInst, m);
@@ -2694,9 +2769,7 @@ namespace ARMeilleure.Instructions
m = context.AddIntrinsic(Intrinsic.X86Psrldq, m, Const(8));
}
- Intrinsic movInst = op.Size == 0
- ? Intrinsic.X86Pmovzxbw
- : Intrinsic.X86Pmovzxwd;
+ Intrinsic movInst = op.Size == 0 ? Intrinsic.X86Pmovzxbw : Intrinsic.X86Pmovzxwd;
n = context.AddIntrinsic(movInst, n);
m = context.AddIntrinsic(movInst, m);
@@ -3011,6 +3084,98 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res);
}
+ private static Operand EmitSse2VectorIsQNaNOpF(ArmEmitterContext context, Operand opF)
+ {
+ IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
+
+ if ((op.Size & 1) == 0)
+ {
+ const int QBit = 22;
+
+ Operand qMask = X86GetAllElements(context, 1 << QBit);
+
+ Operand mask1 = context.AddIntrinsic(Intrinsic.X86Cmpps, opF, opF, Const((int)CmpCondition.UnorderedQ));
+
+ Operand mask2 = context.AddIntrinsic(Intrinsic.X86Pand, opF, qMask);
+ mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, mask2, qMask, Const((int)CmpCondition.Equal));
+
+ return context.AddIntrinsic(Intrinsic.X86Andps, mask1, mask2);
+ }
+ else /* if ((op.Size & 1) == 1) */
+ {
+ const int QBit = 51;
+
+ Operand qMask = X86GetAllElements(context, 1L << QBit);
+
+ Operand mask1 = context.AddIntrinsic(Intrinsic.X86Cmppd, opF, opF, Const((int)CmpCondition.UnorderedQ));
+
+ Operand mask2 = context.AddIntrinsic(Intrinsic.X86Pand, opF, qMask);
+ mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, mask2, qMask, Const((int)CmpCondition.Equal));
+
+ return context.AddIntrinsic(Intrinsic.X86Andpd, mask1, mask2);
+ }
+ }
+
+ private static void EmitSse41MaxMinNumOpF(ArmEmitterContext context, bool isMaxNum, bool scalar)
+ {
+ OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
+
+ Operand d = GetVec(op.Rd);
+ Operand n = GetVec(op.Rn);
+ Operand m = GetVec(op.Rm);
+
+ Operand nQNaNMask = EmitSse2VectorIsQNaNOpF(context, n);
+ Operand mQNaNMask = EmitSse2VectorIsQNaNOpF(context, m);
+
+ Operand nNum = context.Copy(n);
+ Operand mNum = context.Copy(m);
+
+ int sizeF = op.Size & 1;
+
+ if (sizeF == 0)
+ {
+ Operand negInfMask = X86GetAllElements(context, isMaxNum ? float.NegativeInfinity : float.PositiveInfinity);
+
+ Operand nMask = context.AddIntrinsic(Intrinsic.X86Andnps, mQNaNMask, nQNaNMask);
+ Operand mMask = context.AddIntrinsic(Intrinsic.X86Andnps, nQNaNMask, mQNaNMask);
+
+ nNum = context.AddIntrinsic(Intrinsic.X86Blendvps, nNum, negInfMask, nMask);
+ mNum = context.AddIntrinsic(Intrinsic.X86Blendvps, mNum, negInfMask, mMask);
+
+ Operand res = context.AddIntrinsic(isMaxNum ? Intrinsic.X86Maxps : Intrinsic.X86Minps, nNum, mNum);
+
+ if (scalar)
+ {
+ res = context.VectorZeroUpper96(res);
+ }
+ else if (op.RegisterSize == RegisterSize.Simd64)
+ {
+ res = context.VectorZeroUpper64(res);
+ }
+
+ context.Copy(d, res);
+ }
+ else /* if (sizeF == 1) */
+ {
+ Operand negInfMask = X86GetAllElements(context, isMaxNum ? double.NegativeInfinity : double.PositiveInfinity);
+
+ Operand nMask = context.AddIntrinsic(Intrinsic.X86Andnpd, mQNaNMask, nQNaNMask);
+ Operand mMask = context.AddIntrinsic(Intrinsic.X86Andnpd, nQNaNMask, mQNaNMask);
+
+ nNum = context.AddIntrinsic(Intrinsic.X86Blendvpd, nNum, negInfMask, nMask);
+ mNum = context.AddIntrinsic(Intrinsic.X86Blendvpd, mNum, negInfMask, mMask);
+
+ Operand res = context.AddIntrinsic(isMaxNum ? Intrinsic.X86Maxpd : Intrinsic.X86Minpd, nNum, mNum);
+
+ if (scalar)
+ {
+ res = context.VectorZeroUpper64(res);
+ }
+
+ context.Copy(d, res);
+ }
+ }
+
private enum AddSub
{
None,
diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp.cs b/ARMeilleure/Instructions/InstEmitSimdCmp.cs
index ac1bffcb2e..e70f56a0a3 100644
--- a/ARMeilleure/Instructions/InstEmitSimdCmp.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdCmp.cs
@@ -300,7 +300,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.Equal, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.Equal, scalar: true);
}
else
{
@@ -312,7 +312,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.Equal, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.Equal, scalar: false);
}
else
{
@@ -324,7 +324,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: true);
}
else
{
@@ -336,7 +336,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.GreaterThanOrEqual, scalar: false);
}
else
{
@@ -348,7 +348,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: true);
}
else
{
@@ -360,7 +360,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseAvx)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.GreaterThan, scalar: false);
}
else
{
@@ -372,7 +372,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.LessThanOrEqual, scalar: true);
}
else
{
@@ -384,7 +384,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.LessThanOrEqual, scalar: false);
}
else
{
@@ -396,7 +396,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: true);
+ EmitSse2CmpOpF(context, CmpCondition.LessThan, scalar: true);
}
else
{
@@ -408,7 +408,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.FastFP && Optimizations.UseSse2)
{
- EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: false);
+ EmitSse2CmpOpF(context, CmpCondition.LessThan, scalar: false);
}
else
{
@@ -673,7 +673,7 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res);
}
- private static void EmitCmpSseOrSse2OpF(ArmEmitterContext context, CmpCondition cond, bool scalar)
+ private static void EmitSse2CmpOpF(ArmEmitterContext context, CmpCondition cond, bool scalar)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs
index f0880079e4..28d075dd3b 100644
--- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs
@@ -907,7 +907,7 @@ namespace ARMeilleure.Instructions
Operand res = context.VectorZero();
- Operand me = EmitVectorExtract(context, op.Rm, op.Index, op.Size, signed);;
+ Operand me = EmitVectorExtract(context, op.Rm, op.Index, op.Size, signed);
int elems = 8 >> op.Size;
@@ -939,7 +939,7 @@ namespace ARMeilleure.Instructions
Operand res = context.VectorZero();
- Operand me = EmitVectorExtract(context, op.Rm, op.Index, op.Size, signed);;
+ Operand me = EmitVectorExtract(context, op.Rm, op.Index, op.Size, signed);
int elems = 8 >> op.Size;
@@ -1114,6 +1114,7 @@ namespace ARMeilleure.Instructions
Equal = 0, // Ordered, non-signaling.
LessThan = 1, // Ordered, signaling.
LessThanOrEqual = 2, // Ordered, signaling.
+ UnorderedQ = 3, // Non-signaling.
NotLessThan = 5, // Unordered, signaling.
NotLessThanOrEqual = 6, // Unordered, signaling.
OrderedQ = 7, // Non-signaling.
diff --git a/ARMeilleure/Instructions/InstEmitSimdMove.cs b/ARMeilleure/Instructions/InstEmitSimdMove.cs
index 9d2aeb3bde..789c8c8712 100644
--- a/ARMeilleure/Instructions/InstEmitSimdMove.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdMove.cs
@@ -177,7 +177,7 @@ namespace ARMeilleure.Instructions
if (op.RegisterSize == RegisterSize.Simd64)
{
- nShifted = context.AddIntrinsic(Intrinsic.X86Movlhps, nShifted, context.VectorZero());
+ nShifted = context.VectorZeroUpper64(nShifted);
}
nShifted = context.AddIntrinsic(Intrinsic.X86Psrldq, nShifted, Const(op.Imm4));
@@ -188,7 +188,7 @@ namespace ARMeilleure.Instructions
if (op.RegisterSize == RegisterSize.Simd64)
{
- mShifted = context.AddIntrinsic(Intrinsic.X86Movlhps, mShifted, context.VectorZero());
+ mShifted = context.VectorZeroUpper64(mShifted);
}
Operand res = context.AddIntrinsic(Intrinsic.X86Por, nShifted, mShifted);
@@ -277,9 +277,10 @@ namespace ARMeilleure.Instructions
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
+ Operand d = GetVec(op.Rd);
Operand n = GetIntOrZR(context, op.Rn);
- context.Copy(GetVec(op.Rd), EmitVectorInsert(context, GetVec(op.Rd), n, 1, 3));
+ context.Copy(d, EmitVectorInsert(context, d, n, 1, 3));
}
public static void Fmov_S(ArmEmitterContext context)
@@ -311,18 +312,32 @@ namespace ARMeilleure.Instructions
{
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
- Operand e = Const(op.Immediate);
-
- Operand res = context.VectorZero();
-
- int elems = op.RegisterSize == RegisterSize.Simd128 ? 4 : 2;
-
- for (int index = 0; index < (elems >> op.Size); index++)
+ if (Optimizations.UseSse2)
{
- res = EmitVectorInsert(context, res, e, index, op.Size + 2);
+ if (op.RegisterSize == RegisterSize.Simd128)
+ {
+ context.Copy(GetVec(op.Rd), X86GetAllElements(context, op.Immediate));
+ }
+ else
+ {
+ context.Copy(GetVec(op.Rd), X86GetScalar(context, op.Immediate));
+ }
}
+ else
+ {
+ Operand e = Const(op.Immediate);
- context.Copy(GetVec(op.Rd), res);
+ Operand res = context.VectorZero();
+
+ int elems = op.RegisterSize == RegisterSize.Simd128 ? 2 : 1;
+
+ for (int index = 0; index < elems; index++)
+ {
+ res = EmitVectorInsert(context, res, e, index, 3);
+ }
+
+ context.Copy(GetVec(op.Rd), res);
+ }
}
public static void Ins_Gp(ArmEmitterContext context)
@@ -349,7 +364,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse2)
{
- EmitMoviMvni(context, not: false);
+ EmitSse2MoviMvni(context, not: false);
}
else
{
@@ -361,7 +376,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse2)
{
- EmitMoviMvni(context, not: true);
+ EmitSse2MoviMvni(context, not: true);
}
else
{
@@ -430,13 +445,11 @@ namespace ARMeilleure.Instructions
{
Operand d = GetVec(op.Rd);
- Operand res = context.AddIntrinsic(Intrinsic.X86Movlhps, d, context.VectorZero());
-
- Operand n = GetVec(op.Rn);
+ Operand res = context.VectorZeroUpper64(d);
Operand mask = X86GetAllElements(context, _masksE0_TrnUzpXtn[op.Size]);
- Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mask);
+ Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, GetVec(op.Rn), mask);
Intrinsic movInst = op.RegisterSize == RegisterSize.Simd128
? Intrinsic.X86Movlhps
@@ -444,7 +457,7 @@ namespace ARMeilleure.Instructions
res = context.AddIntrinsic(movInst, res, res2);
- context.Copy(GetVec(op.Rd), res);
+ context.Copy(d, res);
}
else
{
@@ -452,7 +465,9 @@ namespace ARMeilleure.Instructions
int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0;
- Operand res = part == 0 ? context.VectorZero() : context.Copy(GetVec(op.Rd));
+ Operand d = GetVec(op.Rd);
+
+ Operand res = part == 0 ? context.VectorZero() : context.Copy(d);
for (int index = 0; index < elems; index++)
{
@@ -461,7 +476,7 @@ namespace ARMeilleure.Instructions
res = EmitVectorInsert(context, res, ne, part + index, op.Size);
}
- context.Copy(GetVec(op.Rd), res);
+ context.Copy(d, res);
}
}
@@ -475,7 +490,7 @@ namespace ARMeilleure.Instructions
EmitVectorZip(context, part: 1);
}
- private static void EmitMoviMvni(ArmEmitterContext context, bool not)
+ private static void EmitSse2MoviMvni(ArmEmitterContext context, bool not)
{
OpCodeSimdImm op = (OpCodeSimdImm)context.CurrOp;
diff --git a/ARMeilleure/Instructions/SoftFloat.cs b/ARMeilleure/Instructions/SoftFloat.cs
index af22c85d21..256bc5b975 100644
--- a/ARMeilleure/Instructions/SoftFloat.cs
+++ b/ARMeilleure/Instructions/SoftFloat.cs
@@ -1089,8 +1089,6 @@ namespace ARMeilleure.Instructions
public static float FPMulSub(float valueA, float value1, float value2)
{
- ExecutionContext context = NativeInterface.GetContext();
-
value1 = value1.FPNeg();
return FPMulAdd(valueA, value1, value2);
@@ -1138,6 +1136,21 @@ namespace ARMeilleure.Instructions
return result;
}
+ public static float FPNegMulAdd(float valueA, float value1, float value2)
+ {
+ valueA = valueA.FPNeg();
+ value1 = value1.FPNeg();
+
+ return FPMulAdd(valueA, value1, value2);
+ }
+
+ public static float FPNegMulSub(float valueA, float value1, float value2)
+ {
+ valueA = valueA.FPNeg();
+
+ return FPMulAdd(valueA, value1, value2);
+ }
+
public static float FPRecipEstimate(float value)
{
ExecutionContext context = NativeInterface.GetContext();
@@ -2196,6 +2209,21 @@ namespace ARMeilleure.Instructions
return result;
}
+ public static double FPNegMulAdd(double valueA, double value1, double value2)
+ {
+ valueA = valueA.FPNeg();
+ value1 = value1.FPNeg();
+
+ return FPMulAdd(valueA, value1, value2);
+ }
+
+ public static double FPNegMulSub(double valueA, double value1, double value2)
+ {
+ valueA = valueA.FPNeg();
+
+ return FPMulAdd(valueA, value1, value2);
+ }
+
public static double FPRecipEstimate(double value)
{
ExecutionContext context = NativeInterface.GetContext();
diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
index e2d3c6dbaf..57c8914d9c 100644
--- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
+++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
@@ -8,6 +8,10 @@ namespace ARMeilleure.IntermediateRepresentation
X86Addss,
X86Andnpd,
X86Andnps,
+ X86Andpd,
+ X86Andps,
+ X86Blendvpd,
+ X86Blendvps,
X86Cmppd,
X86Cmpps,
X86Cmpsd,
From d925de2d0e7f3a065020c63dc1274644284e4006 Mon Sep 17 00:00:00 2001
From: Thog
Date: Sun, 8 Dec 2019 14:35:25 +0100
Subject: [PATCH 3/8] Fix ILogger type and size decoding (#842)
* Fix ILogger type and size decoding
The type and size are custom encoded integer not byte.
This fix issues on games that send messages longer than 127 characters.
* Address gdk's comments
---
.../HOS/Services/Lm/LogService/ILogger.cs | 24 +++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
index 357a13324a..a269312ebe 100644
--- a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
+++ b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs
@@ -8,6 +8,26 @@ namespace Ryujinx.HLE.HOS.Services.Lm.LogService
{
public ILogger() { }
+ private static int ReadEncodedInt(BinaryReader reader)
+ {
+ int result = 0;
+ int position = 0;
+
+ byte encoded;
+
+ do
+ {
+ encoded = reader.ReadByte();
+
+ result += (encoded & 0x7F) << (7 * position);
+
+ position++;
+
+ } while ((encoded & 0x80) != 0);
+
+ return result;
+ }
+
[Command(0)]
// Log(buffer)
public ResultCode Log(ServiceCtx context)
@@ -34,8 +54,8 @@ namespace Ryujinx.HLE.HOS.Services.Lm.LogService
while (ms.Position < ms.Length)
{
- byte type = reader.ReadByte();
- byte size = reader.ReadByte();
+ int type = ReadEncodedInt(reader);
+ int size = ReadEncodedInt(reader);
LmLogField field = (LmLogField)type;
From e5858e2c7d0a46b771e874e37efb77f69120c141 Mon Sep 17 00:00:00 2001
From: gdkchan
Date: Sat, 14 Dec 2019 18:18:51 -0300
Subject: [PATCH 4/8] Add a limit for the number of instructions in a function
(#843)
---
ARMeilleure/Decoders/Decoder.cs | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/ARMeilleure/Decoders/Decoder.cs b/ARMeilleure/Decoders/Decoder.cs
index 2311e9e96e..8eb2a99d6d 100644
--- a/ARMeilleure/Decoders/Decoder.cs
+++ b/ARMeilleure/Decoders/Decoder.cs
@@ -10,6 +10,11 @@ namespace ARMeilleure.Decoders
{
static class Decoder
{
+ // We define a limit on the number of instructions that a function may have,
+ // this prevents functions being potentially too large, which would
+ // take too long to compile and use too much memory.
+ private const int MaxInstsPerFunction = 5000;
+
private delegate object MakeOp(InstDescriptor inst, ulong address, int opCode);
private static ConcurrentDictionary _opActivators;
@@ -36,10 +41,17 @@ namespace ARMeilleure.Decoders
Dictionary visited = new Dictionary();
+ int opsCount = 0;
+
Block GetBlock(ulong blkAddress)
{
if (!visited.TryGetValue(blkAddress, out Block block))
{
+ if (opsCount > MaxInstsPerFunction)
+ {
+ return null;
+ }
+
block = new Block(blkAddress);
workQueue.Enqueue(block);
@@ -92,6 +104,8 @@ namespace ARMeilleure.Decoders
FillBlock(memory, mode, currBlock, limitAddress);
+ opsCount += currBlock.OpCodes.Count;
+
if (currBlock.OpCodes.Count != 0)
{
// Set child blocks. "Branch" is the block the branch instruction
From 01a4c80ed5ef255a67c379fb847230c9b21efc47 Mon Sep 17 00:00:00 2001
From: Thog
Date: Sat, 21 Dec 2019 20:52:31 +0100
Subject: [PATCH 5/8] Rewrite the configuration system (#831)
The configuration system was quite fragile and too dependent on everything, this fix #812 .
The changes:
The file configuration is now entirely independent from the internal configuration state.
The file configuration is versioned (current version is 1).
Every configuration elements are now reactive properties that the emulator can register on to handle initialization and configuration changes.
The configuration system is now in Ryujinx.Common to be accessible on every projects.
Discord integration is now independent from the UI and can be reloaded.
The primary controller is now configurable at runtime (NOTE: the UI currently doesn't have any options to configure real controller).
The logger is entirely reloadable.
You can now hotplug your controller when the emulator is running.
The logger now takes name for every LogTarget to make them removable at runtime.
The logger now always add the default "console" target to avoid loosing early init logs.
The configuration system now generates a default file configuration if it's missing or too new.
General system stability improvements to enhance the user's experience
---
.../Configuration/ConfigurationFileFormat.cs | 146 +----
.../Configuration/ConfigurationState.cs | 502 ++++++++++++++++++
.../Configuration/Hid/ControllerInputId.cs | 45 ++
.../Configuration/Hid/ControllerType.cs | 11 +
Ryujinx.Common/Configuration/Hid/Key.cs | 153 ++++++
.../Configuration/Hid/KeyboardHotkeys.cs | 7 +
.../Configuration/Hid/NpadController.cs | 35 ++
.../Configuration/Hid/NpadControllerLeft.cs | 15 +
.../Configuration/Hid/NpadControllerRight.cs | 15 +
.../Configuration/Hid/NpadKeyboard.cs | 20 +
.../Configuration/Hid/NpadKeyboardLeft.cs | 18 +
.../Configuration/Hid/NpadKeyboardRight.cs | 18 +
Ryujinx.Common/Configuration/LoggerModule.cs | 109 ++++
.../Configuration/System/Language.cs | 23 +
.../Configuration}/Ui/GuiColumns.cs | 2 +-
Ryujinx.Common/Logging/Logger.cs | 33 ++
.../Logging/Targets/AsyncLogTargetWrapper.cs | 2 +
.../Logging/Targets/ConsoleLogTarget.cs | 7 +-
.../Logging/Targets/FileLogTarget.cs | 13 +-
Ryujinx.Common/Logging/Targets/ILogTarget.cs | 2 +
.../Logging/Targets/JsonLogTarget.cs | 9 +-
Ryujinx.Common/ReactiveObject.cs | 57 ++
Ryujinx.Common/Ryujinx.Common.csproj | 1 +
Ryujinx.HLE/Input/Hid.cs | 28 +-
Ryujinx.HLE/Switch.cs | 27 +
Ryujinx/Config.json | 9 +-
.../Configuration/DiscordIntegrationModule.cs | 92 ++++
Ryujinx/Program.cs | 47 +-
Ryujinx/Ryujinx.csproj | 1 -
Ryujinx/Ui/GLScreen.cs | 26 +-
.../{NpadKeyboard.cs => KeyboardControls.cs} | 119 ++---
Ryujinx/Ui/MainWindow.cs | 290 ++++------
Ryujinx/Ui/NpadController.cs | 175 ++----
Ryujinx/Ui/SwitchSettings.cs | 275 ++++++----
34 files changed, 1625 insertions(+), 707 deletions(-)
rename Ryujinx/Configuration.cs => Ryujinx.Common/Configuration/ConfigurationFileFormat.cs (53%)
create mode 100644 Ryujinx.Common/Configuration/ConfigurationState.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/ControllerType.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/Key.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/NpadController.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
create mode 100644 Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
create mode 100644 Ryujinx.Common/Configuration/LoggerModule.cs
create mode 100644 Ryujinx.Common/Configuration/System/Language.cs
rename {Ryujinx => Ryujinx.Common/Configuration}/Ui/GuiColumns.cs (90%)
create mode 100644 Ryujinx.Common/ReactiveObject.cs
create mode 100644 Ryujinx/Configuration/DiscordIntegrationModule.cs
rename Ryujinx/Ui/{NpadKeyboard.cs => KeyboardControls.cs} (74%)
diff --git a/Ryujinx/Configuration.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
similarity index 53%
rename from Ryujinx/Configuration.cs
rename to Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
index c259f9e9d4..1a9407cb2f 100644
--- a/Ryujinx/Configuration.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -1,30 +1,22 @@
using JsonPrettyPrinterPlus;
-using LibHac.FsSystem;
-using OpenTK.Input;
-using Ryujinx.Common;
using Ryujinx.Common.Logging;
-using Ryujinx.HLE;
-using Ryujinx.HLE.HOS.SystemState;
-using Ryujinx.HLE.HOS.Services;
-using Ryujinx.HLE.Input;
-using Ryujinx.Ui;
-using Ryujinx.Ui.Input;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
-using System.Threading.Tasks;
using Utf8Json;
using Utf8Json.Resolvers;
+using Ryujinx.Configuration.System;
+using Ryujinx.Configuration.Hid;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.UI.Input;
+using Ryujinx.Configuration.Ui;
-namespace Ryujinx
+namespace Ryujinx.Configuration
{
- public class Configuration
+ public class ConfigurationFileFormat
{
- ///
- /// The default configuration instance
- ///
- public static Configuration Instance { get; private set; }
+ public int Version { get; set; }
///
/// Dumps shaders in this local directory
@@ -79,7 +71,7 @@ namespace Ryujinx
///
/// Change System Language
///
- public SystemLanguage SystemLanguage { get; set; }
+ public Language SystemLanguage { get; set; }
///
/// Enables or disables Docked Mode
@@ -119,7 +111,7 @@ namespace Ryujinx
///
/// The primary controller's type
///
- public ControllerStatus ControllerType { get; set; }
+ public ControllerType ControllerType { get; set; }
///
/// Used to toggle columns in the GUI
@@ -154,13 +146,13 @@ namespace Ryujinx
///
/// Controller control bindings
///
- public Ui.Input.NpadController JoystickControls { get; private set; }
+ public NpadController JoystickControls { get; set; }
///
/// Loads a configuration file from disk
///
/// The path to the JSON configuration file
- public static void Load(string path)
+ public static ConfigurationFileFormat Load(string path)
{
var resolver = CompositeResolver.Create(
new[] { new ConfigurationEnumFormatter() },
@@ -169,24 +161,7 @@ namespace Ryujinx
using (Stream stream = File.OpenRead(path))
{
- Instance = JsonSerializer.Deserialize(stream, resolver);
- }
- }
-
- ///
- /// Loads a configuration file asynchronously from disk
- ///
- /// The path to the JSON configuration file
- public static async Task LoadAsync(string path)
- {
- IJsonFormatterResolver resolver = CompositeResolver.Create(
- new[] { new ConfigurationEnumFormatter() },
- new[] { StandardResolver.AllowPrivateSnakeCase }
- );
-
- using (Stream stream = File.OpenRead(path))
- {
- Instance = await JsonSerializer.DeserializeAsync(stream, resolver);
+ return JsonSerializer.Deserialize(stream, resolver);
}
}
@@ -194,108 +169,17 @@ namespace Ryujinx
/// Save a configuration file to disk
///
/// The path to the JSON configuration file
- public static void SaveConfig(Configuration config, string path)
+ public void SaveConfig(string path)
{
IJsonFormatterResolver resolver = CompositeResolver.Create(
new[] { new ConfigurationEnumFormatter() },
new[] { StandardResolver.AllowPrivateSnakeCase }
);
- byte[] data = JsonSerializer.Serialize(config, resolver);
+ byte[] data = JsonSerializer.Serialize(this, resolver);
File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson());
}
- ///
- /// Configures a instance
- ///
- /// The instance to configure
- public static void InitialConfigure(Switch device)
- {
- if (Instance == null)
- {
- throw new InvalidOperationException("Configuration has not been loaded yet.");
- }
-
- SwitchSettings.ConfigureSettings(Instance);
-
- Logger.AddTarget(new AsyncLogTargetWrapper(
- new ConsoleLogTarget(),
- 1000,
- AsyncLogTargetOverflowAction.Block
- ));
-
- if (Instance.EnableFileLog)
- {
- Logger.AddTarget(new AsyncLogTargetWrapper(
- new FileLogTarget(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.log")),
- 1000,
- AsyncLogTargetOverflowAction.Block
- ));
- }
-
- Configure(device, Instance);
- }
-
- public static void Configure(Switch device, Configuration SwitchConfig)
- {
- GraphicsConfig.ShadersDumpPath = SwitchConfig.GraphicsShadersDumpPath;
-
- Logger.SetEnable(LogLevel.Debug, SwitchConfig.LoggingEnableDebug );
- Logger.SetEnable(LogLevel.Stub, SwitchConfig.LoggingEnableStub );
- Logger.SetEnable(LogLevel.Info, SwitchConfig.LoggingEnableInfo );
- Logger.SetEnable(LogLevel.Warning, SwitchConfig.LoggingEnableWarn );
- Logger.SetEnable(LogLevel.Error, SwitchConfig.LoggingEnableError );
- Logger.SetEnable(LogLevel.Guest, SwitchConfig.LoggingEnableGuest );
- Logger.SetEnable(LogLevel.AccessLog, SwitchConfig.LoggingEnableFsAccessLog);
-
- if (SwitchConfig.LoggingFilteredClasses.Length > 0)
- {
- foreach (var logClass in EnumExtensions.GetValues())
- {
- Logger.SetEnable(logClass, false);
- }
-
- foreach (var logClass in SwitchConfig.LoggingFilteredClasses)
- {
- Logger.SetEnable(logClass, true);
- }
- }
-
- MainWindow.DiscordIntegrationEnabled = SwitchConfig.EnableDiscordIntegration;
-
- device.EnableDeviceVsync = SwitchConfig.EnableVsync;
-
- device.System.State.DockedMode = SwitchConfig.DockedMode;
-
- device.System.State.SetLanguage(SwitchConfig.SystemLanguage);
-
- if (SwitchConfig.EnableMulticoreScheduling)
- {
- device.System.EnableMultiCoreScheduling();
- }
-
- device.System.FsIntegrityCheckLevel = SwitchConfig.EnableFsIntegrityChecks
- ? IntegrityCheckLevel.ErrorOnInvalid
- : IntegrityCheckLevel.None;
-
- device.System.GlobalAccessLogMode = SwitchConfig.FsGlobalAccessLogMode;
-
- ServiceConfiguration.IgnoreMissingServices = SwitchConfig.IgnoreMissingServices;
- }
-
- public static void ConfigureHid(Switch device, Configuration SwitchConfig)
- {
- if (SwitchConfig.JoystickControls.Enabled)
- {
- if (!Joystick.GetState(SwitchConfig.JoystickControls.Index).IsConnected)
- {
- SwitchConfig.JoystickControls.SetEnabled(false);
- }
- }
- device.Hid.InitializePrimaryController(SwitchConfig.ControllerType);
- device.Hid.InitializeKeyboard();
- }
-
private class ConfigurationEnumFormatter : IJsonFormatter
where T : struct
{
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
new file mode 100644
index 0000000000..050b497385
--- /dev/null
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -0,0 +1,502 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Logging;
+using Ryujinx.Configuration.Hid;
+using Ryujinx.Configuration.System;
+using Ryujinx.Configuration.Ui;
+using Ryujinx.UI.Input;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Configuration
+{
+ public class ConfigurationState
+ {
+ ///
+ /// UI configuration section
+ ///
+ public class UiSection
+ {
+ public class Columns
+ {
+ public ReactiveObject FavColumn { get; private set; }
+ public ReactiveObject IconColumn { get; private set; }
+ public ReactiveObject AppColumn { get; private set; }
+ public ReactiveObject DevColumn { get; private set; }
+ public ReactiveObject VersionColumn { get; private set; }
+ public ReactiveObject TimePlayedColumn { get; private set; }
+ public ReactiveObject LastPlayedColumn { get; private set; }
+ public ReactiveObject FileExtColumn { get; private set; }
+ public ReactiveObject FileSizeColumn { get; private set; }
+ public ReactiveObject PathColumn { get; private set; }
+
+ public Columns()
+ {
+ FavColumn = new ReactiveObject();
+ IconColumn = new ReactiveObject();
+ AppColumn = new ReactiveObject();
+ DevColumn = new ReactiveObject();
+ VersionColumn = new ReactiveObject();
+ TimePlayedColumn = new ReactiveObject();
+ LastPlayedColumn = new ReactiveObject();
+ FileExtColumn = new ReactiveObject();
+ FileSizeColumn = new ReactiveObject();
+ PathColumn = new ReactiveObject();
+ }
+ }
+
+ ///
+ /// Used to toggle columns in the GUI
+ ///
+ public Columns GuiColumns { get; private set; }
+
+ ///
+ /// A list of directories containing games to be used to load games into the games list
+ ///
+ public ReactiveObject> GameDirs { get; private set; }
+
+ ///
+ /// Enable or disable custom themes in the GUI
+ ///
+ public ReactiveObject EnableCustomTheme { get; private set; }
+
+ ///
+ /// Path to custom GUI theme
+ ///
+ public ReactiveObject CustomThemePath { get; private set; }
+
+ public UiSection()
+ {
+ GuiColumns = new Columns();
+ GameDirs = new ReactiveObject>();
+ EnableCustomTheme = new ReactiveObject();
+ CustomThemePath = new ReactiveObject();
+ }
+ }
+
+ ///
+ /// Logger configuration section
+ ///
+ public class LoggerSection
+ {
+ ///
+ /// Enables printing debug log messages
+ ///
+ public ReactiveObject EnableDebug { get; private set; }
+
+ ///
+ /// Enables printing stub log messages
+ ///
+ public ReactiveObject EnableStub { get; private set; }
+
+ ///
+ /// Enables printing info log messages
+ ///
+ public ReactiveObject EnableInfo { get; private set; }
+
+ ///
+ /// Enables printing warning log messages
+ ///
+ public ReactiveObject EnableWarn { get; private set; }
+
+ ///
+ /// Enables printing error log messages
+ ///
+ public ReactiveObject EnableError { get; private set; }
+
+ ///
+ /// Enables printing guest log messages
+ ///
+ public ReactiveObject EnableGuest { get; private set; }
+
+ ///
+ /// Enables printing FS access log messages
+ ///
+ public ReactiveObject EnableFsAccessLog { get; private set; }
+
+ ///
+ /// Controls which log messages are written to the log targets
+ ///
+ public ReactiveObject FilteredClasses { get; private set; }
+
+ ///
+ /// Enables or disables logging to a file on disk
+ ///
+ public ReactiveObject EnableFileLog { get; private set; }
+
+ public LoggerSection()
+ {
+ EnableDebug = new ReactiveObject();
+ EnableStub = new ReactiveObject();
+ EnableInfo = new ReactiveObject();
+ EnableWarn = new ReactiveObject();
+ EnableError = new ReactiveObject();
+ EnableGuest = new ReactiveObject();
+ EnableFsAccessLog = new ReactiveObject();
+ FilteredClasses = new ReactiveObject();
+ EnableFileLog = new ReactiveObject();
+ }
+ }
+
+ ///
+ /// System configuration section
+ ///
+ public class SystemSection
+ {
+ ///
+ /// Change System Language
+ ///
+ public ReactiveObject Language { get; private set; }
+
+ ///
+ /// Enables or disables Docked Mode
+ ///
+ public ReactiveObject EnableDockedMode { get; private set; }
+
+ ///
+ /// Enables or disables multi-core scheduling of threads
+ ///
+ public ReactiveObject EnableMulticoreScheduling { get; private set; }
+
+ ///
+ /// Enables integrity checks on Game content files
+ ///
+ public ReactiveObject EnableFsIntegrityChecks { get; private set; }
+
+ ///
+ /// Enables FS access log output to the console. Possible modes are 0-3
+ ///
+ public ReactiveObject FsGlobalAccessLogMode { get; private set; }
+
+ ///
+ /// Enable or disable ignoring missing services
+ ///
+ public ReactiveObject IgnoreMissingServices { get; private set; }
+
+ public SystemSection()
+ {
+ Language = new ReactiveObject();
+ EnableDockedMode = new ReactiveObject();
+ EnableMulticoreScheduling = new ReactiveObject();
+ EnableFsIntegrityChecks = new ReactiveObject();
+ FsGlobalAccessLogMode = new ReactiveObject();
+ IgnoreMissingServices = new ReactiveObject();
+ }
+ }
+
+ ///
+ /// Hid configuration section
+ ///
+ public class HidSection
+ {
+ ///
+ /// The primary controller's type
+ ///
+ public ReactiveObject ControllerType { get; private set; }
+
+ ///
+ /// Enable or disable keyboard support (Independent from controllers binding)
+ ///
+ public ReactiveObject EnableKeyboard { get; private set; }
+
+ ///
+ /// Keyboard control bindings
+ ///
+ public ReactiveObject KeyboardControls { get; private set; }
+
+ ///
+ /// Controller control bindings
+ ///
+ public ReactiveObject JoystickControls { get; private set; }
+
+ public HidSection()
+ {
+ ControllerType = new ReactiveObject();
+ EnableKeyboard = new ReactiveObject();
+ KeyboardControls = new ReactiveObject();
+ JoystickControls = new ReactiveObject();
+ }
+ }
+
+ ///
+ /// Graphics configuration section
+ ///
+ public class GraphicsSection
+ {
+ ///
+ /// Dumps shaders in this local directory
+ ///
+ public ReactiveObject ShadersDumpPath { get; private set; }
+
+ ///
+ /// Enables or disables Vertical Sync
+ ///
+ public ReactiveObject EnableVsync { get; private set; }
+
+ public GraphicsSection()
+ {
+ ShadersDumpPath = new ReactiveObject();
+ EnableVsync = new ReactiveObject();
+ }
+ }
+
+ ///
+ /// The default configuration instance
+ ///
+ public static ConfigurationState Instance { get; private set; }
+
+ ///
+ /// The Ui section
+ ///
+ public UiSection Ui { get; private set; }
+
+ ///
+ /// The Logger section
+ ///
+ public LoggerSection Logger { get; private set; }
+
+ ///
+ /// The System section
+ ///
+ public SystemSection System { get; private set; }
+
+ ///
+ /// The Graphics section
+ ///
+ public GraphicsSection Graphics { get; private set; }
+
+ ///
+ /// The Hid section
+ ///
+ public HidSection Hid { get; private set; }
+
+ ///
+ /// Enables or disables Discord Rich Presence
+ ///
+ public ReactiveObject EnableDiscordIntegration { get; private set; }
+
+ private ConfigurationState()
+ {
+ Ui = new UiSection();
+ Logger = new LoggerSection();
+ System = new SystemSection();
+ Graphics = new GraphicsSection();
+ Hid = new HidSection();
+ EnableDiscordIntegration = new ReactiveObject();
+ }
+
+ public ConfigurationFileFormat ToFileFormat()
+ {
+ ConfigurationFileFormat configurationFile = new ConfigurationFileFormat
+ {
+ Version = 1,
+ GraphicsShadersDumpPath = Graphics.ShadersDumpPath,
+ LoggingEnableDebug = Logger.EnableDebug,
+ LoggingEnableStub = Logger.EnableStub,
+ LoggingEnableInfo = Logger.EnableInfo,
+ LoggingEnableWarn = Logger.EnableWarn,
+ LoggingEnableError = Logger.EnableError,
+ LoggingEnableGuest = Logger.EnableGuest,
+ LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
+ LoggingFilteredClasses = Logger.FilteredClasses,
+ EnableFileLog = Logger.EnableFileLog,
+ SystemLanguage = System.Language,
+ DockedMode = System.EnableDockedMode,
+ EnableDiscordIntegration = EnableDiscordIntegration,
+ EnableVsync = Graphics.EnableVsync,
+ EnableMulticoreScheduling = System.EnableMulticoreScheduling,
+ EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
+ FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
+ IgnoreMissingServices = System.IgnoreMissingServices,
+ ControllerType = Hid.ControllerType,
+ GuiColumns = new GuiColumns()
+ {
+ FavColumn = Ui.GuiColumns.FavColumn,
+ IconColumn = Ui.GuiColumns.IconColumn,
+ AppColumn = Ui.GuiColumns.AppColumn,
+ DevColumn = Ui.GuiColumns.DevColumn,
+ VersionColumn = Ui.GuiColumns.VersionColumn,
+ TimePlayedColumn = Ui.GuiColumns.TimePlayedColumn,
+ LastPlayedColumn = Ui.GuiColumns.LastPlayedColumn,
+ FileExtColumn = Ui.GuiColumns.FileExtColumn,
+ FileSizeColumn = Ui.GuiColumns.FileSizeColumn,
+ PathColumn = Ui.GuiColumns.PathColumn,
+ },
+ GameDirs = Ui.GameDirs,
+ EnableCustomTheme = Ui.EnableCustomTheme,
+ CustomThemePath = Ui.CustomThemePath,
+ EnableKeyboard = Hid.EnableKeyboard,
+ KeyboardControls = Hid.KeyboardControls,
+ JoystickControls = Hid.JoystickControls
+ };
+
+ return configurationFile;
+ }
+
+ public void LoadDefault()
+ {
+ Graphics.ShadersDumpPath.Value = "";
+ Logger.EnableDebug.Value = false;
+ Logger.EnableStub.Value = true;
+ Logger.EnableInfo.Value = true;
+ Logger.EnableWarn.Value = true;
+ Logger.EnableError.Value = true;
+ Logger.EnableGuest.Value = true;
+ Logger.EnableFsAccessLog.Value = false;
+ Logger.FilteredClasses.Value = new LogClass[] { };
+ Logger.EnableFileLog.Value = true;
+ System.Language.Value = Language.AmericanEnglish;
+ System.EnableDockedMode.Value = false;
+ EnableDiscordIntegration.Value = true;
+ Graphics.EnableVsync.Value = true;
+ System.EnableMulticoreScheduling.Value = true;
+ System.EnableFsIntegrityChecks.Value = true;
+ System.FsGlobalAccessLogMode.Value = 0;
+ System.IgnoreMissingServices.Value = false;
+ Hid.ControllerType.Value = ControllerType.Handheld;
+ Ui.GuiColumns.FavColumn.Value = true;
+ Ui.GuiColumns.IconColumn.Value = true;
+ Ui.GuiColumns.AppColumn.Value = true;
+ Ui.GuiColumns.DevColumn.Value = true;
+ Ui.GuiColumns.VersionColumn.Value = true;
+ Ui.GuiColumns.TimePlayedColumn.Value = true;
+ Ui.GuiColumns.LastPlayedColumn.Value = true;
+ Ui.GuiColumns.FileExtColumn.Value = true;
+ Ui.GuiColumns.FileSizeColumn.Value = true;
+ Ui.GuiColumns.PathColumn.Value = true;
+ Ui.GameDirs.Value = new List();
+ Ui.EnableCustomTheme.Value = false;
+ Ui.CustomThemePath.Value = "";
+ Hid.EnableKeyboard.Value = false;
+
+ Hid.KeyboardControls.Value = new NpadKeyboard
+ {
+ LeftJoycon = new NpadKeyboardLeft
+ {
+ StickUp = Key.W,
+ StickDown = Key.S,
+ StickLeft = Key.A,
+ StickRight = Key.D,
+ StickButton = Key.F,
+ DPadUp = Key.Up,
+ DPadDown = Key.Down,
+ DPadLeft = Key.Left,
+ DPadRight = Key.Right,
+ ButtonMinus = Key.Minus,
+ ButtonL = Key.E,
+ ButtonZl = Key.Q,
+ },
+ RightJoycon = new NpadKeyboardRight
+ {
+ StickUp = Key.I,
+ StickDown = Key.K,
+ StickLeft = Key.J,
+ StickRight = Key.L,
+ StickButton = Key.H,
+ ButtonA = Key.Z,
+ ButtonB = Key.X,
+ ButtonX = Key.C,
+ ButtonY = Key.V,
+ ButtonPlus = Key.Plus,
+ ButtonR = Key.U,
+ ButtonZr = Key.O,
+ },
+ Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = Key.Tab
+ }
+ };
+
+ Hid.JoystickControls.Value = new NpadController
+ {
+ Enabled = true,
+ Index = 0,
+ Deadzone = 0.05f,
+ TriggerThreshold = 0.5f,
+ LeftJoycon = new NpadControllerLeft
+ {
+ Stick = ControllerInputId.Axis0,
+ StickButton = ControllerInputId.Button8,
+ DPadUp = ControllerInputId.Hat0Up,
+ DPadDown = ControllerInputId.Hat0Down,
+ DPadLeft = ControllerInputId.Hat0Left,
+ DPadRight = ControllerInputId.Hat0Right,
+ ButtonMinus = ControllerInputId.Button6,
+ ButtonL = ControllerInputId.Button4,
+ ButtonZl = ControllerInputId.Axis2,
+ },
+ RightJoycon = new NpadControllerRight
+ {
+ Stick = ControllerInputId.Axis3,
+ StickButton = ControllerInputId.Button9,
+ ButtonA = ControllerInputId.Button1,
+ ButtonB = ControllerInputId.Button0,
+ ButtonX = ControllerInputId.Button3,
+ ButtonY = ControllerInputId.Button2,
+ ButtonPlus = ControllerInputId.Button7,
+ ButtonR = ControllerInputId.Button5,
+ ButtonZr = ControllerInputId.Axis5,
+ }
+ };
+ }
+
+ public void Load(ConfigurationFileFormat configurationFileFormat)
+ {
+ if (configurationFileFormat.Version != 1 && configurationFileFormat.Version != 0)
+ {
+ Common.Logging.Logger.PrintWarning(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
+
+ LoadDefault();
+
+ return;
+ }
+
+ Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath;
+ Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug;
+ Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub;
+ Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo;
+ Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn;
+ Logger.EnableError.Value = configurationFileFormat.LoggingEnableError;
+ Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest;
+ Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog;
+ Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses;
+ Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
+ System.Language.Value = configurationFileFormat.SystemLanguage;
+ System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
+ System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
+ EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
+ Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
+ System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
+ System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
+ System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
+ System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;
+ Hid.ControllerType.Value = configurationFileFormat.ControllerType;
+ Ui.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn;
+ Ui.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn;
+ Ui.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn;
+ Ui.GuiColumns.DevColumn.Value = configurationFileFormat.GuiColumns.DevColumn;
+ Ui.GuiColumns.VersionColumn.Value = configurationFileFormat.GuiColumns.VersionColumn;
+ Ui.GuiColumns.TimePlayedColumn.Value = configurationFileFormat.GuiColumns.TimePlayedColumn;
+ Ui.GuiColumns.LastPlayedColumn.Value = configurationFileFormat.GuiColumns.LastPlayedColumn;
+ Ui.GuiColumns.FileExtColumn.Value = configurationFileFormat.GuiColumns.FileExtColumn;
+ Ui.GuiColumns.FileSizeColumn.Value = configurationFileFormat.GuiColumns.FileSizeColumn;
+ Ui.GuiColumns.PathColumn.Value = configurationFileFormat.GuiColumns.PathColumn;
+ Ui.GameDirs.Value = configurationFileFormat.GameDirs;
+ Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
+ Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
+ Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
+ Hid.KeyboardControls.Value = configurationFileFormat.KeyboardControls;
+ Hid.JoystickControls.Value = configurationFileFormat.JoystickControls;
+ }
+
+ public static void Initialize()
+ {
+ if (Instance != null)
+ {
+ throw new InvalidOperationException("Configuration is already initialized");
+ }
+
+ Instance = new ConfigurationState();
+ }
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs b/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
new file mode 100644
index 0000000000..8969b6a4b8
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
@@ -0,0 +1,45 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+ public enum ControllerInputId
+ {
+ Button0,
+ Button1,
+ Button2,
+ Button3,
+ Button4,
+ Button5,
+ Button6,
+ Button7,
+ Button8,
+ Button9,
+ Button10,
+ Button11,
+ Button12,
+ Button13,
+ Button14,
+ Button15,
+ Button16,
+ Button17,
+ Button18,
+ Button19,
+ Button20,
+ Axis0,
+ Axis1,
+ Axis2,
+ Axis3,
+ Axis4,
+ Axis5,
+ Hat0Up,
+ Hat0Down,
+ Hat0Left,
+ Hat0Right,
+ Hat1Up,
+ Hat1Down,
+ Hat1Left,
+ Hat1Right,
+ Hat2Up,
+ Hat2Down,
+ Hat2Left,
+ Hat2Right
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/Ryujinx.Common/Configuration/Hid/ControllerType.cs
new file mode 100644
index 0000000000..b0613b2d66
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/ControllerType.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Configuration.Hid
+{
+ public enum ControllerType
+ {
+ ProController,
+ Handheld,
+ NpadPair,
+ NpadLeft,
+ NpadRight
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/Key.cs b/Ryujinx.Common/Configuration/Hid/Key.cs
new file mode 100644
index 0000000000..b658396b9b
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/Key.cs
@@ -0,0 +1,153 @@
+namespace Ryujinx.Configuration.Hid
+{
+ public enum Key
+ {
+ Unknown = 0,
+ ShiftLeft = 1,
+ LShift = 1,
+ ShiftRight = 2,
+ RShift = 2,
+ ControlLeft = 3,
+ LControl = 3,
+ ControlRight = 4,
+ RControl = 4,
+ AltLeft = 5,
+ LAlt = 5,
+ AltRight = 6,
+ RAlt = 6,
+ WinLeft = 7,
+ LWin = 7,
+ WinRight = 8,
+ RWin = 8,
+ Menu = 9,
+ F1 = 10,
+ F2 = 11,
+ F3 = 12,
+ F4 = 13,
+ F5 = 14,
+ F6 = 15,
+ F7 = 16,
+ F8 = 17,
+ F9 = 18,
+ F10 = 19,
+ F11 = 20,
+ F12 = 21,
+ F13 = 22,
+ F14 = 23,
+ F15 = 24,
+ F16 = 25,
+ F17 = 26,
+ F18 = 27,
+ F19 = 28,
+ F20 = 29,
+ F21 = 30,
+ F22 = 31,
+ F23 = 32,
+ F24 = 33,
+ F25 = 34,
+ F26 = 35,
+ F27 = 36,
+ F28 = 37,
+ F29 = 38,
+ F30 = 39,
+ F31 = 40,
+ F32 = 41,
+ F33 = 42,
+ F34 = 43,
+ F35 = 44,
+ Up = 45,
+ Down = 46,
+ Left = 47,
+ Right = 48,
+ Enter = 49,
+ Escape = 50,
+ Space = 51,
+ Tab = 52,
+ BackSpace = 53,
+ Back = 53,
+ Insert = 54,
+ Delete = 55,
+ PageUp = 56,
+ PageDown = 57,
+ Home = 58,
+ End = 59,
+ CapsLock = 60,
+ ScrollLock = 61,
+ PrintScreen = 62,
+ Pause = 63,
+ NumLock = 64,
+ Clear = 65,
+ Sleep = 66,
+ Keypad0 = 67,
+ Keypad1 = 68,
+ Keypad2 = 69,
+ Keypad3 = 70,
+ Keypad4 = 71,
+ Keypad5 = 72,
+ Keypad6 = 73,
+ Keypad7 = 74,
+ Keypad8 = 75,
+ Keypad9 = 76,
+ KeypadDivide = 77,
+ KeypadMultiply = 78,
+ KeypadSubtract = 79,
+ KeypadMinus = 79,
+ KeypadAdd = 80,
+ KeypadPlus = 80,
+ KeypadDecimal = 81,
+ KeypadPeriod = 81,
+ KeypadEnter = 82,
+ A = 83,
+ B = 84,
+ C = 85,
+ D = 86,
+ E = 87,
+ F = 88,
+ G = 89,
+ H = 90,
+ I = 91,
+ J = 92,
+ K = 93,
+ L = 94,
+ M = 95,
+ N = 96,
+ O = 97,
+ P = 98,
+ Q = 99,
+ R = 100,
+ S = 101,
+ T = 102,
+ U = 103,
+ V = 104,
+ W = 105,
+ X = 106,
+ Y = 107,
+ Z = 108,
+ Number0 = 109,
+ Number1 = 110,
+ Number2 = 111,
+ Number3 = 112,
+ Number4 = 113,
+ Number5 = 114,
+ Number6 = 115,
+ Number7 = 116,
+ Number8 = 117,
+ Number9 = 118,
+ Tilde = 119,
+ Grave = 119,
+ Minus = 120,
+ Plus = 121,
+ BracketLeft = 122,
+ LBracket = 122,
+ BracketRight = 123,
+ RBracket = 123,
+ Semicolon = 124,
+ Quote = 125,
+ Comma = 126,
+ Period = 127,
+ Slash = 128,
+ BackSlash = 129,
+ NonUSBackSlash = 130,
+ LastKey = 131
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
new file mode 100644
index 0000000000..1d0b050492
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Configuration.Hid
+{
+ public struct KeyboardHotkeys
+ {
+ public Key ToggleVsync;
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadController.cs b/Ryujinx.Common/Configuration/Hid/NpadController.cs
new file mode 100644
index 0000000000..f00865d556
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadController.cs
@@ -0,0 +1,35 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+ public class NpadController
+ {
+ ///
+ /// Enables or disables controller support
+ ///
+ public bool Enabled;
+
+ ///
+ /// Controller Device Index
+ ///
+ public int Index;
+
+ ///
+ /// Controller Analog Stick Deadzone
+ ///
+ public float Deadzone;
+
+ ///
+ /// Controller Trigger Threshold
+ ///
+ public float TriggerThreshold;
+
+ ///
+ /// Left JoyCon Controller Bindings
+ ///
+ public NpadControllerLeft LeftJoycon;
+
+ ///
+ /// Right JoyCon Controller Bindings
+ ///
+ public NpadControllerRight RightJoycon;
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
new file mode 100644
index 0000000000..54ac0f03ae
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+ public struct NpadControllerLeft
+ {
+ public ControllerInputId Stick;
+ public ControllerInputId StickButton;
+ public ControllerInputId ButtonMinus;
+ public ControllerInputId ButtonL;
+ public ControllerInputId ButtonZl;
+ public ControllerInputId DPadUp;
+ public ControllerInputId DPadDown;
+ public ControllerInputId DPadLeft;
+ public ControllerInputId DPadRight;
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
new file mode 100644
index 0000000000..315136d9f6
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+ public struct NpadControllerRight
+ {
+ public ControllerInputId Stick;
+ public ControllerInputId StickButton;
+ public ControllerInputId ButtonA;
+ public ControllerInputId ButtonB;
+ public ControllerInputId ButtonX;
+ public ControllerInputId ButtonY;
+ public ControllerInputId ButtonPlus;
+ public ControllerInputId ButtonR;
+ public ControllerInputId ButtonZr;
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
new file mode 100644
index 0000000000..911f5119ea
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
@@ -0,0 +1,20 @@
+namespace Ryujinx.UI.Input
+{
+ public class NpadKeyboard
+ {
+ ///
+ /// Left JoyCon Keyboard Bindings
+ ///
+ public Configuration.Hid.NpadKeyboardLeft LeftJoycon;
+
+ ///
+ /// Right JoyCon Keyboard Bindings
+ ///
+ public Configuration.Hid.NpadKeyboardRight RightJoycon;
+
+ ///
+ /// Hotkey Keyboard Bindings
+ ///
+ public Configuration.Hid.KeyboardHotkeys Hotkeys;
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
new file mode 100644
index 0000000000..799cdfdb8a
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Configuration.Hid
+{
+ public struct NpadKeyboardLeft
+ {
+ public Key StickUp;
+ public Key StickDown;
+ public Key StickLeft;
+ public Key StickRight;
+ public Key StickButton;
+ public Key DPadUp;
+ public Key DPadDown;
+ public Key DPadLeft;
+ public Key DPadRight;
+ public Key ButtonMinus;
+ public Key ButtonL;
+ public Key ButtonZl;
+ }
+}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
new file mode 100644
index 0000000000..311504bb7e
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Configuration.Hid
+{
+ public struct NpadKeyboardRight
+ {
+ public Key StickUp;
+ public Key StickDown;
+ public Key StickLeft;
+ public Key StickRight;
+ public Key StickButton;
+ public Key ButtonA;
+ public Key ButtonB;
+ public Key ButtonX;
+ public Key ButtonY;
+ public Key ButtonPlus;
+ public Key ButtonR;
+ public Key ButtonZr;
+ }
+}
diff --git a/Ryujinx.Common/Configuration/LoggerModule.cs b/Ryujinx.Common/Configuration/LoggerModule.cs
new file mode 100644
index 0000000000..504a81418f
--- /dev/null
+++ b/Ryujinx.Common/Configuration/LoggerModule.cs
@@ -0,0 +1,109 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using System;
+using System.IO;
+
+namespace Ryujinx.Configuration
+{
+ public static class LoggerModule
+ {
+ public static void Initialize()
+ {
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+ AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
+
+ ConfigurationState.Instance.Logger.EnableDebug.Event += ReloadEnableDebug;
+ ConfigurationState.Instance.Logger.EnableStub.Event += ReloadEnableStub;
+ ConfigurationState.Instance.Logger.EnableInfo.Event += ReloadEnableInfo;
+ ConfigurationState.Instance.Logger.EnableWarn.Event += ReloadEnableWarning;
+ ConfigurationState.Instance.Logger.EnableError.Event += ReloadEnableError;
+ ConfigurationState.Instance.Logger.EnableGuest.Event += ReloadEnableGuest;
+ ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += ReloadEnableFsAccessLog;
+ ConfigurationState.Instance.Logger.FilteredClasses.Event += ReloadFilteredClasses;
+ ConfigurationState.Instance.Logger.EnableFileLog.Event += ReloadFileLogger;
+ }
+
+ private static void ReloadEnableDebug(object sender, ReactiveEventArgs e)
+ {
+ Logger.SetEnable(LogLevel.Debug, e.NewValue);
+ }
+
+ private static void ReloadEnableStub(object sender, ReactiveEventArgs e)
+ {
+ Logger.SetEnable(LogLevel.Stub, e.NewValue);
+ }
+
+ private static void ReloadEnableInfo(object sender, ReactiveEventArgs e)
+ {
+ Logger.SetEnable(LogLevel.Info, e.NewValue);
+ }
+
+ private static void ReloadEnableWarning(object sender, ReactiveEventArgs e)
+ {
+ Logger.SetEnable(LogLevel.Warning, e.NewValue);
+ }
+
+ private static void ReloadEnableError(object sender, ReactiveEventArgs e)
+ {
+ Logger.SetEnable(LogLevel.Error, e.NewValue);
+ }
+
+ private static void ReloadEnableGuest(object sender, ReactiveEventArgs e)
+ {
+ Logger.SetEnable(LogLevel.Guest, e.NewValue);
+ }
+
+ private static void ReloadEnableFsAccessLog(object sender, ReactiveEventArgs e)
+ {
+ Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
+ }
+
+ private static void ReloadFilteredClasses(object sender, ReactiveEventArgs e)
+ {
+ bool noFilter = e.NewValue.Length == 0;
+
+ foreach (var logClass in EnumExtensions.GetValues())
+ {
+ Logger.SetEnable(logClass, noFilter);
+ }
+
+ foreach (var logClass in e.NewValue)
+ {
+ Logger.SetEnable(logClass, true);
+ }
+ }
+
+ private static void ReloadFileLogger(object sender, ReactiveEventArgs e)
+ {
+ if (e.NewValue)
+ {
+ Logger.AddTarget(new AsyncLogTargetWrapper(
+ new FileLogTarget(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.log"), "file"),
+ 1000,
+ AsyncLogTargetOverflowAction.Block
+ ));
+ }
+ else
+ {
+ Logger.RemoveTarget("file");
+ }
+ }
+
+ private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
+ {
+ Logger.Shutdown();
+ }
+
+ private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ var exception = e.ExceptionObject as Exception;
+
+ Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
+
+ if (e.IsTerminating)
+ {
+ Logger.Shutdown();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Common/Configuration/System/Language.cs b/Ryujinx.Common/Configuration/System/Language.cs
new file mode 100644
index 0000000000..d3af296ba9
--- /dev/null
+++ b/Ryujinx.Common/Configuration/System/Language.cs
@@ -0,0 +1,23 @@
+namespace Ryujinx.Configuration.System
+{
+ public enum Language
+ {
+ Japanese,
+ AmericanEnglish,
+ French,
+ German,
+ Italian,
+ Spanish,
+ Chinese,
+ Korean,
+ Dutch,
+ Portuguese,
+ Russian,
+ Taiwanese,
+ BritishEnglish,
+ CanadianFrench,
+ LatinAmericanSpanish,
+ SimplifiedChinese,
+ TraditionalChinese
+ }
+}
diff --git a/Ryujinx/Ui/GuiColumns.cs b/Ryujinx.Common/Configuration/Ui/GuiColumns.cs
similarity index 90%
rename from Ryujinx/Ui/GuiColumns.cs
rename to Ryujinx.Common/Configuration/Ui/GuiColumns.cs
index b86a273ea8..2b3524aa82 100644
--- a/Ryujinx/Ui/GuiColumns.cs
+++ b/Ryujinx.Common/Configuration/Ui/GuiColumns.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Ui
+namespace Ryujinx.Configuration.Ui
{
public struct GuiColumns
{
diff --git a/Ryujinx.Common/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs
index 10b1d97037..83af97b122 100644
--- a/Ryujinx.Common/Logging/Logger.cs
+++ b/Ryujinx.Common/Logging/Logger.cs
@@ -37,6 +37,12 @@ namespace Ryujinx.Common.Logging
m_LogTargets = new List();
m_Time = Stopwatch.StartNew();
+
+ // Logger should log to console by default
+ AddTarget(new AsyncLogTargetWrapper(
+ new ConsoleLogTarget("console"),
+ 1000,
+ AsyncLogTargetOverflowAction.Block));
}
public static void RestartTime()
@@ -44,6 +50,19 @@ namespace Ryujinx.Common.Logging
m_Time.Restart();
}
+ private static ILogTarget GetTarget(string targetName)
+ {
+ foreach (var target in m_LogTargets)
+ {
+ if (target.Name.Equals(targetName))
+ {
+ return target;
+ }
+ }
+
+ return null;
+ }
+
public static void AddTarget(ILogTarget target)
{
m_LogTargets.Add(target);
@@ -51,6 +70,20 @@ namespace Ryujinx.Common.Logging
Updated += target.Log;
}
+ public static void RemoveTarget(string target)
+ {
+ ILogTarget logTarget = GetTarget(target);
+
+ if (logTarget != null)
+ {
+ Updated -= logTarget.Log;
+
+ m_LogTargets.Remove(logTarget);
+
+ logTarget.Dispose();
+ }
+ }
+
public static void Shutdown()
{
Updated = null;
diff --git a/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
index a805a83b6e..c946b67880 100644
--- a/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
+++ b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
@@ -27,6 +27,8 @@ namespace Ryujinx.Common.Logging
private readonly int _overflowTimeout;
+ string ILogTarget.Name { get => _target.Name; }
+
public AsyncLogTargetWrapper(ILogTarget target)
: this(target, -1, AsyncLogTargetOverflowAction.Block)
{ }
diff --git a/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
index 871076a41a..ff5c6f5acb 100644
--- a/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
@@ -9,6 +9,10 @@ namespace Ryujinx.Common.Logging
private readonly ILogFormatter _formatter;
+ private readonly string _name;
+
+ string ILogTarget.Name { get => _name; }
+
static ConsoleLogTarget()
{
_logColors = new ConcurrentDictionary {
@@ -19,9 +23,10 @@ namespace Ryujinx.Common.Logging
};
}
- public ConsoleLogTarget()
+ public ConsoleLogTarget(string name)
{
_formatter = new DefaultLogFormatter();
+ _name = name;
}
public void Log(object sender, LogEventArgs args)
diff --git a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
index 85dc82497a..4db5f7bce5 100644
--- a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
@@ -1,4 +1,5 @@
-using System.IO;
+using System;
+using System.IO;
using System.Text;
namespace Ryujinx.Common.Logging
@@ -9,13 +10,17 @@ namespace Ryujinx.Common.Logging
private readonly StreamWriter _logWriter;
private readonly ILogFormatter _formatter;
+ private readonly string _name;
- public FileLogTarget(string path)
- : this(path, FileShare.Read, FileMode.Append)
+ string ILogTarget.Name { get => _name; }
+
+ public FileLogTarget(string path, string name)
+ : this(path, name, FileShare.Read, FileMode.Append)
{ }
- public FileLogTarget(string path, FileShare fileShare, FileMode fileMode)
+ public FileLogTarget(string path, string name, FileShare fileShare, FileMode fileMode)
{
+ _name = name;
_logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare));
_formatter = new DefaultLogFormatter();
}
diff --git a/Ryujinx.Common/Logging/Targets/ILogTarget.cs b/Ryujinx.Common/Logging/Targets/ILogTarget.cs
index 261c5e64b2..d4d26a936d 100644
--- a/Ryujinx.Common/Logging/Targets/ILogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/ILogTarget.cs
@@ -5,5 +5,7 @@ namespace Ryujinx.Common.Logging
public interface ILogTarget : IDisposable
{
void Log(object sender, LogEventArgs args);
+
+ string Name { get; }
}
}
diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
index 410394aa2d..3729b18d13 100644
--- a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
@@ -1,4 +1,5 @@
-using System.IO;
+using System;
+using System.IO;
using Utf8Json;
namespace Ryujinx.Common.Logging
@@ -7,10 +8,14 @@ namespace Ryujinx.Common.Logging
{
private Stream _stream;
private bool _leaveOpen;
+ private string _name;
- public JsonLogTarget(Stream stream)
+ string ILogTarget.Name { get => _name; }
+
+ public JsonLogTarget(Stream stream, string name)
{
_stream = stream;
+ _name = name;
}
public JsonLogTarget(Stream stream, bool leaveOpen)
diff --git a/Ryujinx.Common/ReactiveObject.cs b/Ryujinx.Common/ReactiveObject.cs
new file mode 100644
index 0000000000..be30e9b2c5
--- /dev/null
+++ b/Ryujinx.Common/ReactiveObject.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Threading;
+
+namespace Ryujinx.Common
+{
+ public class ReactiveObject
+ {
+ private ReaderWriterLock _readerWriterLock = new ReaderWriterLock();
+ private T _value;
+
+ public event EventHandler> Event;
+
+ public T Value
+ {
+ get
+ {
+ _readerWriterLock.AcquireReaderLock(Timeout.Infinite);
+ T value = _value;
+ _readerWriterLock.ReleaseReaderLock();
+
+ return value;
+ }
+ set
+ {
+ _readerWriterLock.AcquireWriterLock(Timeout.Infinite);
+
+ T oldValue = _value;
+
+ _value = value;
+
+ _readerWriterLock.ReleaseWriterLock();
+
+ if (oldValue == null || !oldValue.Equals(_value))
+ {
+ Event?.Invoke(this, new ReactiveEventArgs(oldValue, value));
+ }
+ }
+ }
+
+ public static implicit operator T(ReactiveObject obj)
+ {
+ return obj.Value;
+ }
+ }
+
+ public class ReactiveEventArgs
+ {
+ public T OldValue { get; }
+ public T NewValue { get; }
+
+ public ReactiveEventArgs(T oldValue, T newValue)
+ {
+ OldValue = oldValue;
+ NewValue = newValue;
+ }
+ }
+}
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index c777b402cc..7f6fa32323 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -27,6 +27,7 @@
+
diff --git a/Ryujinx.HLE/Input/Hid.cs b/Ryujinx.HLE/Input/Hid.cs
index 27e6a30873..5cb7f09de6 100644
--- a/Ryujinx.HLE/Input/Hid.cs
+++ b/Ryujinx.HLE/Input/Hid.cs
@@ -1,5 +1,7 @@
using Ryujinx.Common;
+using Ryujinx.Configuration.Hid;
using Ryujinx.HLE.HOS;
+using System;
namespace Ryujinx.HLE.Input
{
@@ -47,18 +49,31 @@ namespace Ryujinx.HLE.Input
_keyboardOffset = HidPosition + HidKeyboardOffset;
}
- public void InitializePrimaryController(ControllerStatus controllerType)
+ private static ControllerStatus ConvertControllerTypeToState(ControllerType controllerType)
{
- ControllerId controllerId = controllerType == ControllerStatus.Handheld ?
+ switch (controllerType)
+ {
+ case ControllerType.Handheld: return ControllerStatus.Handheld;
+ case ControllerType.NpadLeft: return ControllerStatus.NpadLeft;
+ case ControllerType.NpadRight: return ControllerStatus.NpadRight;
+ case ControllerType.NpadPair: return ControllerStatus.NpadPair;
+ case ControllerType.ProController: return ControllerStatus.ProController;
+ default: throw new NotImplementedException();
+ }
+ }
+
+ public void InitializePrimaryController(ControllerType controllerType)
+ {
+ ControllerId controllerId = controllerType == ControllerType.Handheld ?
ControllerId.ControllerHandheld : ControllerId.ControllerPlayer1;
- if (controllerType == ControllerStatus.ProController)
+ if (controllerType == ControllerType.ProController)
{
PrimaryController = new ProController(_device, NpadColor.Black, NpadColor.Black);
}
else
{
- PrimaryController = new NpadController(controllerType,
+ PrimaryController = new NpadController(ConvertControllerTypeToState(controllerType),
_device,
(NpadColor.BodyNeonRed, NpadColor.BodyNeonRed),
(NpadColor.ButtonsNeonBlue, NpadColor.ButtonsNeonBlue));
@@ -67,11 +82,6 @@ namespace Ryujinx.HLE.Input
PrimaryController.Connect(controllerId);
}
- public void InitializeKeyboard()
- {
- _device.Memory.FillWithZeros(HidPosition + HidKeyboardOffset, HidKeyboardSize);
- }
-
public ControllerButtons UpdateStickButtons(
JoystickPosition leftStick,
JoystickPosition rightStick)
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 4a15f616e7..a4d07f6ac1 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -1,8 +1,12 @@
+using LibHac.FsSystem;
using Ryujinx.Audio;
+using Ryujinx.Configuration;
using Ryujinx.Graphics;
using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
+using Ryujinx.HLE.HOS.Services;
+using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Input;
using System;
using System.Threading;
@@ -60,6 +64,29 @@ namespace Ryujinx.HLE
VsyncEvent = new AutoResetEvent(true);
}
+ public void Initialize()
+ {
+ System.State.SetLanguage((SystemLanguage)ConfigurationState.Instance.System.Language.Value);
+
+ EnableDeviceVsync = ConfigurationState.Instance.Graphics.EnableVsync;
+
+ // TODO: Make this reloadable and implement Docking/Undocking logic.
+ System.State.DockedMode = ConfigurationState.Instance.System.EnableDockedMode;
+
+ if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
+ {
+ System.EnableMultiCoreScheduling();
+ }
+
+ System.FsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
+ ? IntegrityCheckLevel.ErrorOnInvalid
+ : IntegrityCheckLevel.None;
+
+ System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
+
+ ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices;
+ }
+
public void LoadCart(string exeFsDir, string romFsFile = null)
{
System.LoadCart(exeFsDir, romFsFile);
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index 8463081fab..e35a67acd7 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -1,4 +1,5 @@
{
+ "version": 1,
"graphics_shaders_dump_path": "",
"logging_enable_debug": false,
"logging_enable_stub": true,
@@ -7,9 +8,7 @@
"logging_enable_error": true,
"logging_enable_guest": true,
"logging_enable_fs_access_log": false,
- "logging_filtered_classes": [
-
- ],
+ "logging_filtered_classes": [],
"enable_file_log": true,
"system_language": "AmericanEnglish",
"docked_mode": false,
@@ -32,9 +31,7 @@
"file_size_column": true,
"path_column": true
},
- "game_dirs": [
-
- ],
+ "game_dirs": [],
"enable_custom_theme": false,
"custom_theme_path": "",
"enable_keyboard": false,
diff --git a/Ryujinx/Configuration/DiscordIntegrationModule.cs b/Ryujinx/Configuration/DiscordIntegrationModule.cs
new file mode 100644
index 0000000000..15540a1c82
--- /dev/null
+++ b/Ryujinx/Configuration/DiscordIntegrationModule.cs
@@ -0,0 +1,92 @@
+using DiscordRPC;
+using Ryujinx.Common;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Ryujinx.Configuration
+{
+ static class DiscordIntegrationModule
+ {
+ private static DiscordRpcClient DiscordClient;
+
+ private static string LargeDescription = "Ryujinx is a Nintendo Switch emulator.";
+
+ public static RichPresence DiscordPresence { get; private set; }
+
+ public static void Initialize()
+ {
+ DiscordPresence = new RichPresence
+ {
+ Assets = new Assets
+ {
+ LargeImageKey = "ryujinx",
+ LargeImageText = LargeDescription
+ },
+ Details = "Main Menu",
+ State = "Idling",
+ Timestamps = new Timestamps(DateTime.UtcNow)
+ };
+
+ ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
+ }
+
+ private static void Update(object sender, ReactiveEventArgs e)
+ {
+ if (e.OldValue != e.NewValue)
+ {
+ // If the integration was active, disable it and unload everything
+ if (e.OldValue)
+ {
+ DiscordClient?.Dispose();
+
+ DiscordClient = null;
+ }
+
+ // If we need to activate it and the client isn't active, initialize it
+ if (e.NewValue && DiscordClient == null)
+ {
+ DiscordClient = new DiscordRpcClient("568815339807309834");
+
+ DiscordClient.Initialize();
+ DiscordClient.SetPresence(DiscordPresence);
+ }
+ }
+ }
+
+ public static void SwitchToPlayingState(string titleId, string titleName)
+ {
+ if (File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(titleId))
+ {
+ DiscordPresence.Assets.LargeImageKey = titleId;
+ }
+
+ string state = titleId;
+
+ if (state == null)
+ {
+ state = "Ryujinx";
+ }
+ else
+ {
+ state = state.ToUpper();
+ }
+
+ string details = "Idling";
+
+ if (titleName != null)
+ {
+ details = $"Playing {titleName}";
+ }
+
+ DiscordPresence.Details = details;
+ DiscordPresence.State = state;
+ DiscordPresence.Assets.LargeImageText = titleName;
+ DiscordPresence.Assets.SmallImageKey = "ryujinx";
+ DiscordPresence.Assets.SmallImageText = LargeDescription;
+ DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
+
+ DiscordClient?.SetPresence(DiscordPresence);
+ }
+ }
+}
diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs
index 96e9e8dec2..98b8d692d6 100644
--- a/Ryujinx/Program.cs
+++ b/Ryujinx/Program.cs
@@ -1,5 +1,6 @@
using Gtk;
using Ryujinx.Common.Logging;
+using Ryujinx.Configuration;
using Ryujinx.Profiler;
using Ryujinx.Ui;
using System;
@@ -16,9 +17,32 @@ namespace Ryujinx
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
- AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
- GLib.ExceptionManager.UnhandledException += Glib_UnhandledException;
+ GLib.ExceptionManager.UnhandledException += Glib_UnhandledException;
+
+ // Initialize the configuration
+ ConfigurationState.Initialize();
+
+ // Initialize the logger system
+ LoggerModule.Initialize();
+
+ // Initialize Discord integration
+ DiscordIntegrationModule.Initialize();
+
+ string configurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
+
+ // Now load the configuration as the other subsystems are now registered
+ if (File.Exists(configurationPath))
+ {
+ ConfigurationFileFormat configurationFileFormat = ConfigurationFileFormat.Load(configurationPath);
+ ConfigurationState.Instance.Load(configurationFileFormat);
+ }
+ else
+ {
+ // No configuration, we load the default values and save it on disk
+ ConfigurationState.Instance.LoadDefault();
+ ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath);
+ }
+
Profile.Initialize();
@@ -42,23 +66,6 @@ namespace Ryujinx
Application.Run();
}
- private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
- {
- Logger.Shutdown();
- }
-
- private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
- {
- Exception exception = e.ExceptionObject as Exception;
-
- Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
-
- if (e.IsTerminating)
- {
- Logger.Shutdown();
- }
- }
-
private static void Glib_UnhandledException(GLib.UnhandledExceptionArgs e)
{
Exception exception = e.ExceptionObject as Exception;
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index c54beffe47..b231ddb8d2 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -71,7 +71,6 @@
-
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index c23a369294..e1994803a6 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -1,10 +1,12 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
+using Ryujinx.Configuration;
using Ryujinx.Graphics.Gal;
using Ryujinx.HLE;
using Ryujinx.HLE.Input;
using Ryujinx.Profiler.UI;
+using Ryujinx.Ui;
using System;
using System.Threading;
@@ -29,6 +31,8 @@ namespace Ryujinx.Ui
private MouseState? _mouse = null;
+ private Input.NpadController _primaryController;
+
private Thread _renderThread;
private bool _resizeEvent;
@@ -50,6 +54,8 @@ namespace Ryujinx.Ui
_device = device;
_renderer = renderer;
+ _primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
+
Location = new Point(
(DisplayDevice.Default.Width / 2) - (Width / 2),
(DisplayDevice.Default.Height / 2) - (Height / 2));
@@ -162,16 +168,16 @@ namespace Ryujinx.Ui
#endif
// Normal Input
- currentHotkeyButtons = Configuration.Instance.KeyboardControls.GetHotkeyButtons(keyboard);
- currentButton = Configuration.Instance.KeyboardControls.GetButtons(keyboard);
+ currentHotkeyButtons = KeyboardControls.GetHotkeyButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
+ currentButton = KeyboardControls.GetButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
- if (Configuration.Instance.EnableKeyboard)
+ if (ConfigurationState.Instance.Hid.EnableKeyboard)
{
- hidKeyboard = Configuration.Instance.KeyboardControls.GetKeysDown(keyboard);
+ hidKeyboard = KeyboardControls.GetKeysDown(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
}
- (leftJoystickDx, leftJoystickDy) = Configuration.Instance.KeyboardControls.GetLeftStick(keyboard);
- (rightJoystickDx, rightJoystickDy) = Configuration.Instance.KeyboardControls.GetRightStick(keyboard);
+ (leftJoystickDx, leftJoystickDy) = KeyboardControls.GetLeftStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
+ (rightJoystickDx, rightJoystickDy) = KeyboardControls.GetRightStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
}
if (!hidKeyboard.HasValue)
@@ -183,17 +189,17 @@ namespace Ryujinx.Ui
};
}
- currentButton |= Configuration.Instance.JoystickControls.GetButtons();
+ currentButton |= _primaryController.GetButtons();
// Keyboard has priority stick-wise
if (leftJoystickDx == 0 && leftJoystickDy == 0)
{
- (leftJoystickDx, leftJoystickDy) = Configuration.Instance.JoystickControls.GetLeftStick();
+ (leftJoystickDx, leftJoystickDy) = _primaryController.GetLeftStick();
}
if (rightJoystickDx == 0 && rightJoystickDy == 0)
{
- (rightJoystickDx, rightJoystickDy) = Configuration.Instance.JoystickControls.GetRightStick();
+ (rightJoystickDx, rightJoystickDy) = _primaryController.GetRightStick();
}
leftJoystick = new JoystickPosition
@@ -269,7 +275,7 @@ namespace Ryujinx.Ui
_device.Hid.SetTouchPoints();
}
- if (Configuration.Instance.EnableKeyboard && hidKeyboard.HasValue)
+ if (ConfigurationState.Instance.Hid.EnableKeyboard && hidKeyboard.HasValue)
{
_device.Hid.WriteKeyboard(hidKeyboard.Value);
}
diff --git a/Ryujinx/Ui/NpadKeyboard.cs b/Ryujinx/Ui/KeyboardControls.cs
similarity index 74%
rename from Ryujinx/Ui/NpadKeyboard.cs
rename to Ryujinx/Ui/KeyboardControls.cs
index 95fb222183..db9c0cda8c 100644
--- a/Ryujinx/Ui/NpadKeyboard.cs
+++ b/Ryujinx/Ui/KeyboardControls.cs
@@ -1,118 +1,67 @@
-using OpenTK.Input;
+using OpenTK.Input;
using Ryujinx.HLE.Input;
+using Ryujinx.UI.Input;
-namespace Ryujinx.Ui.Input
+namespace Ryujinx.Ui
{
- public struct NpadKeyboardLeft
+ public static class KeyboardControls
{
- public Key StickUp;
- public Key StickDown;
- public Key StickLeft;
- public Key StickRight;
- public Key StickButton;
- public Key DPadUp;
- public Key DPadDown;
- public Key DPadLeft;
- public Key DPadRight;
- public Key ButtonMinus;
- public Key ButtonL;
- public Key ButtonZl;
- }
-
- public struct NpadKeyboardRight
- {
- public Key StickUp;
- public Key StickDown;
- public Key StickLeft;
- public Key StickRight;
- public Key StickButton;
- public Key ButtonA;
- public Key ButtonB;
- public Key ButtonX;
- public Key ButtonY;
- public Key ButtonPlus;
- public Key ButtonR;
- public Key ButtonZr;
- }
-
- public struct KeyboardHotkeys
- {
- public Key ToggleVsync;
- }
-
- public class NpadKeyboard
- {
- ///
- /// Left JoyCon Keyboard Bindings
- ///
- public NpadKeyboardLeft LeftJoycon { get; set; }
-
- ///
- /// Right JoyCon Keyboard Bindings
- ///
- public NpadKeyboardRight RightJoycon { get; set; }
-
- ///
- /// Hotkey Keyboard Bindings
- ///
- public KeyboardHotkeys Hotkeys { get; private set; }
-
- public ControllerButtons GetButtons(KeyboardState keyboard)
+ public static ControllerButtons GetButtons(NpadKeyboard npad, KeyboardState keyboard)
{
ControllerButtons buttons = 0;
- if (keyboard[(Key)LeftJoycon.StickButton]) buttons |= ControllerButtons.StickLeft;
- if (keyboard[(Key)LeftJoycon.DPadUp]) buttons |= ControllerButtons.DpadUp;
- if (keyboard[(Key)LeftJoycon.DPadDown]) buttons |= ControllerButtons.DpadDown;
- if (keyboard[(Key)LeftJoycon.DPadLeft]) buttons |= ControllerButtons.DpadLeft;
- if (keyboard[(Key)LeftJoycon.DPadRight]) buttons |= ControllerButtons.DPadRight;
- if (keyboard[(Key)LeftJoycon.ButtonMinus]) buttons |= ControllerButtons.Minus;
- if (keyboard[(Key)LeftJoycon.ButtonL]) buttons |= ControllerButtons.L;
- if (keyboard[(Key)LeftJoycon.ButtonZl]) buttons |= ControllerButtons.Zl;
+ if (keyboard[(Key)npad.LeftJoycon.StickButton]) buttons |= ControllerButtons.StickLeft;
+ if (keyboard[(Key)npad.LeftJoycon.DPadUp]) buttons |= ControllerButtons.DpadUp;
+ if (keyboard[(Key)npad.LeftJoycon.DPadDown]) buttons |= ControllerButtons.DpadDown;
+ if (keyboard[(Key)npad.LeftJoycon.DPadLeft]) buttons |= ControllerButtons.DpadLeft;
+ if (keyboard[(Key)npad.LeftJoycon.DPadRight]) buttons |= ControllerButtons.DPadRight;
+ if (keyboard[(Key)npad.LeftJoycon.ButtonMinus]) buttons |= ControllerButtons.Minus;
+ if (keyboard[(Key)npad.LeftJoycon.ButtonL]) buttons |= ControllerButtons.L;
+ if (keyboard[(Key)npad.LeftJoycon.ButtonZl]) buttons |= ControllerButtons.Zl;
- if (keyboard[(Key)RightJoycon.StickButton]) buttons |= ControllerButtons.StickRight;
- if (keyboard[(Key)RightJoycon.ButtonA]) buttons |= ControllerButtons.A;
- if (keyboard[(Key)RightJoycon.ButtonB]) buttons |= ControllerButtons.B;
- if (keyboard[(Key)RightJoycon.ButtonX]) buttons |= ControllerButtons.X;
- if (keyboard[(Key)RightJoycon.ButtonY]) buttons |= ControllerButtons.Y;
- if (keyboard[(Key)RightJoycon.ButtonPlus]) buttons |= ControllerButtons.Plus;
- if (keyboard[(Key)RightJoycon.ButtonR]) buttons |= ControllerButtons.R;
- if (keyboard[(Key)RightJoycon.ButtonZr]) buttons |= ControllerButtons.Zr;
+ if (keyboard[(Key)npad.RightJoycon.StickButton]) buttons |= ControllerButtons.StickRight;
+ if (keyboard[(Key)npad.RightJoycon.ButtonA]) buttons |= ControllerButtons.A;
+ if (keyboard[(Key)npad.RightJoycon.ButtonB]) buttons |= ControllerButtons.B;
+ if (keyboard[(Key)npad.RightJoycon.ButtonX]) buttons |= ControllerButtons.X;
+ if (keyboard[(Key)npad.RightJoycon.ButtonY]) buttons |= ControllerButtons.Y;
+ if (keyboard[(Key)npad.RightJoycon.ButtonPlus]) buttons |= ControllerButtons.Plus;
+ if (keyboard[(Key)npad.RightJoycon.ButtonR]) buttons |= ControllerButtons.R;
+ if (keyboard[(Key)npad.RightJoycon.ButtonZr]) buttons |= ControllerButtons.Zr;
return buttons;
}
- public (short, short) GetLeftStick(KeyboardState keyboard)
+ public static (short, short) GetLeftStick(NpadKeyboard npad, KeyboardState keyboard)
{
short dx = 0;
short dy = 0;
- if (keyboard[(Key)LeftJoycon.StickUp]) dy = short.MaxValue;
- if (keyboard[(Key)LeftJoycon.StickDown]) dy = -short.MaxValue;
- if (keyboard[(Key)LeftJoycon.StickLeft]) dx = -short.MaxValue;
- if (keyboard[(Key)LeftJoycon.StickRight]) dx = short.MaxValue;
+ if (keyboard[(Key)npad.LeftJoycon.StickUp]) dy = short.MaxValue;
+ if (keyboard[(Key)npad.LeftJoycon.StickDown]) dy = -short.MaxValue;
+ if (keyboard[(Key)npad.LeftJoycon.StickLeft]) dx = -short.MaxValue;
+ if (keyboard[(Key)npad.LeftJoycon.StickRight]) dx = short.MaxValue;
return (dx, dy);
}
- public (short, short) GetRightStick(KeyboardState keyboard)
+ public static (short, short) GetRightStick(NpadKeyboard npad, KeyboardState keyboard)
{
short dx = 0;
short dy = 0;
- if (keyboard[(Key)RightJoycon.StickUp]) dy = short.MaxValue;
- if (keyboard[(Key)RightJoycon.StickDown]) dy = -short.MaxValue;
- if (keyboard[(Key)RightJoycon.StickLeft]) dx = -short.MaxValue;
- if (keyboard[(Key)RightJoycon.StickRight]) dx = short.MaxValue;
+ if (keyboard[(Key)npad.RightJoycon.StickUp]) dy = short.MaxValue;
+ if (keyboard[(Key)npad.RightJoycon.StickDown]) dy = -short.MaxValue;
+ if (keyboard[(Key)npad.RightJoycon.StickLeft]) dx = -short.MaxValue;
+ if (keyboard[(Key)npad.RightJoycon.StickRight]) dx = short.MaxValue;
return (dx, dy);
}
- public HotkeyButtons GetHotkeyButtons(KeyboardState keyboard)
+ public static HotkeyButtons GetHotkeyButtons(NpadKeyboard npad, KeyboardState keyboard)
{
HotkeyButtons buttons = 0;
- if (keyboard[(Key)Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
+ if (keyboard[(Key)npad.Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
return buttons;
}
@@ -267,7 +216,7 @@ namespace Ryujinx.Ui.Input
new KeyMappingEntry { TargetKey = Key.NumLock, Target = 10 },
};
- public HLE.Input.Keyboard GetKeysDown(KeyboardState keyboard)
+ public static HLE.Input.Keyboard GetKeysDown(NpadKeyboard npad, KeyboardState keyboard)
{
HLE.Input.Keyboard hidKeyboard = new HLE.Input.Keyboard
{
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index a24f3ed050..e0bd849479 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -1,22 +1,22 @@
-using DiscordRPC;
using Gtk;
-using JsonPrettyPrinterPlus;
using Ryujinx.Audio;
using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Gal.OpenGL;
using Ryujinx.Graphics.Gal;
-using Ryujinx.HLE.FileSystem;
+using Ryujinx.Graphics.Gal.OpenGL;
using Ryujinx.Profiler;
using System;
-using System.Diagnostics;
using System.IO;
-using System.Linq;
using System.Reflection;
using System.Text;
-using System.Threading.Tasks;
using System.Threading;
+using Ryujinx.Configuration;
+using System.Diagnostics;
+using System.Threading.Tasks;
using Utf8Json;
+using JsonPrettyPrinterPlus;
using Utf8Json.Resolvers;
+using Ryujinx.HLE.FileSystem;
+
using GUI = Gtk.Builder.ObjectAttribute;
@@ -50,12 +50,6 @@ namespace Ryujinx.Ui
private static TreeView _treeView;
- public static bool DiscordIntegrationEnabled { get; set; }
-
- public static DiscordRpcClient DiscordClient;
-
- public static RichPresence DiscordPresence;
-
#pragma warning disable CS0649
#pragma warning disable IDE0044
[GUI] Window _mainWin;
@@ -91,60 +85,39 @@ namespace Ryujinx.Ui
_audioOut = InitializeAudioEngine();
- _device = new HLE.Switch(_renderer, _audioOut);
+ // TODO: Initialization and dispose of HLE.Switch when starting/stoping emulation.
+ _device = InitializeSwitchInstance();
_treeView = _gameTable;
- Configuration.Load(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
- Configuration.InitialConfigure(_device);
-
ApplyTheme();
- if (DiscordIntegrationEnabled)
- {
- DiscordClient = new DiscordRpcClient("568815339807309834");
- DiscordPresence = new RichPresence
- {
- Assets = new Assets
- {
- LargeImageKey = "ryujinx",
- LargeImageText = "Ryujinx is an emulator for the Nintendo Switch"
- },
- Details = "Main Menu",
- State = "Idling",
- Timestamps = new Timestamps(DateTime.UtcNow)
- };
-
- DiscordClient.Initialize();
- DiscordClient.SetPresence(DiscordPresence);
- }
-
_mainWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
_stopEmulation.Sensitive = false;
- if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _favToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _iconToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _appToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _developerToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.VersionColumn) { _versionToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _timePlayedToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _lastPlayedToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _fileExtToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _fileSizeToggle.Active = true; }
- if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _pathToggle.Active = true; }
+ 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;
+ 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;
_gameTable.Model = _tableStore = new ListStore(
- typeof(bool),
- typeof(Gdk.Pixbuf),
- typeof(string),
- typeof(string),
- typeof(string),
- typeof(string),
- typeof(string),
- typeof(string),
- typeof(string),
+ typeof(bool),
+ typeof(Gdk.Pixbuf),
+ typeof(string),
+ typeof(string),
+ typeof(string),
+ typeof(string),
+ typeof(string),
+ typeof(string),
+ typeof(string),
typeof(string));
-
+
_tableStore.SetSortFunc(5, TimePlayedSort);
_tableStore.SetSortFunc(6, LastPlayedSort);
_tableStore.SetSortFunc(8, FileSizeSort);
@@ -158,22 +131,22 @@ namespace Ryujinx.Ui
internal static void ApplyTheme()
{
- if (!SwitchSettings.SwitchConfig.EnableCustomTheme)
+ if (!ConfigurationState.Instance.Ui.EnableCustomTheme)
{
return;
}
- if (File.Exists(SwitchSettings.SwitchConfig.CustomThemePath) && (System.IO.Path.GetExtension(SwitchSettings.SwitchConfig.CustomThemePath) == ".css"))
+ if (File.Exists(ConfigurationState.Instance.Ui.CustomThemePath) && (System.IO.Path.GetExtension(ConfigurationState.Instance.Ui.CustomThemePath) == ".css"))
{
CssProvider cssProvider = new CssProvider();
- cssProvider.LoadFromPath(SwitchSettings.SwitchConfig.CustomThemePath);
+ cssProvider.LoadFromPath(ConfigurationState.Instance.Ui.CustomThemePath);
StyleContext.AddProviderForScreen(Gdk.Screen.Default, cssProvider, 800);
}
else
{
- Logger.PrintWarning(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{SwitchSettings.SwitchConfig.CustomThemePath}\".");
+ Logger.PrintWarning(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\".");
}
}
@@ -187,39 +160,48 @@ namespace Ryujinx.Ui
CellRendererToggle favToggle = new CellRendererToggle();
favToggle.Toggled += FavToggle_Toggled;
- if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _gameTable.AppendColumn("Fav", favToggle, "active", 0); }
- if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1); }
- if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2); }
- if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3); }
- if (SwitchSettings.SwitchConfig.GuiColumns.VersionColumn) { _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4); }
- if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5); }
- if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6); }
- if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7); }
- if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8); }
- if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9); }
+ if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _gameTable.AppendColumn("Fav", favToggle, "active", 0);
+ 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);
+ 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)
{
- if (column.Title == "Fav") { _favColumn = column; }
- else if (column.Title == "Application") { _appColumn = column; }
- else if (column.Title == "Developer") { _devColumn = column; }
- else if (column.Title == "Version") { _versionColumn = column; }
- else if (column.Title == "Time Played") { _timePlayedColumn = column; }
- else if (column.Title == "Last Played") { _lastPlayedColumn = column; }
- else if (column.Title == "File Ext") { _fileExtColumn = column; }
- else if (column.Title == "File Size") { _fileSizeColumn = column; }
- else if (column.Title == "Path") { _pathColumn = column; }
+ if (column.Title == "Fav") _favColumn = column;
+ else if (column.Title == "Application") _appColumn = column;
+ else if (column.Title == "Developer") _devColumn = column;
+ else if (column.Title == "Version") _versionColumn = column;
+ else if (column.Title == "Time Played") _timePlayedColumn = column;
+ else if (column.Title == "Last Played") _lastPlayedColumn = column;
+ else if (column.Title == "File Ext") _fileExtColumn = column;
+ else if (column.Title == "File Size") _fileSizeColumn = column;
+ else if (column.Title == "Path") _pathColumn = column;
}
- if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _favColumn.SortColumnId = 0; }
- if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _appColumn.SortColumnId = 2; }
- if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _devColumn.SortColumnId = 3; }
- if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _versionColumn.SortColumnId = 4; }
- if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _timePlayedColumn.SortColumnId = 5; }
- if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _lastPlayedColumn.SortColumnId = 6; }
- if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _fileExtColumn.SortColumnId = 7; }
- if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _fileSizeColumn.SortColumnId = 8; }
- if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _pathColumn.SortColumnId = 9; }
+ if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favColumn.SortColumnId = 0;
+ if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appColumn.SortColumnId = 2;
+ if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _devColumn.SortColumnId = 3;
+ if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _versionColumn.SortColumnId = 4;
+ if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedColumn.SortColumnId = 5;
+ if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedColumn.SortColumnId = 6;
+ if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _fileExtColumn.SortColumnId = 7;
+ if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeColumn.SortColumnId = 8;
+ if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathColumn.SortColumnId = 9;
+ }
+
+ private HLE.Switch InitializeSwitchInstance()
+ {
+ HLE.Switch instance = new HLE.Switch(_renderer, _audioOut);
+
+ instance.Initialize();
+
+ return instance;
}
internal static async Task UpdateGameTable()
@@ -233,7 +215,7 @@ namespace Ryujinx.Ui
_tableStore.Clear();
- await Task.Run(() => ApplicationLibrary.LoadApplications(SwitchSettings.SwitchConfig.GameDirs, _device.System.KeySet, _device.System.State.DesiredTitleLanguage));
+ await Task.Run(() => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, _device.System.KeySet, _device.System.State.DesiredTitleLanguage));
_updatingGameTable = false;
}
@@ -248,6 +230,9 @@ namespace Ryujinx.Ui
{
Logger.RestartTime();
+ // TODO: Move this somewhere else + reloadable?
+ GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
+
if (Directory.Exists(path))
{
string[] romFsFiles = Directory.GetFiles(path, "*.istorage");
@@ -313,40 +298,7 @@ namespace Ryujinx.Ui
_gameLoaded = true;
_stopEmulation.Sensitive = true;
- if (DiscordIntegrationEnabled)
- {
- if (File.ReadAllLines(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(_device.System.TitleId))
- {
- DiscordPresence.Assets.LargeImageKey = _device.System.TitleId;
- }
-
- string state = _device.System.TitleId;
-
- if (state == null)
- {
- state = "Ryujinx";
- }
- else
- {
- state = state.ToUpper();
- }
-
- string details = "Idling";
-
- if (_device.System.TitleName != null)
- {
- details = $"Playing {_device.System.TitleName}";
- }
-
- DiscordPresence.Details = details;
- DiscordPresence.State = state;
- DiscordPresence.Assets.LargeImageText = _device.System.TitleName;
- DiscordPresence.Assets.SmallImageKey = "ryujinx";
- DiscordPresence.Assets.SmallImageText = "Ryujinx is an emulator for the Nintendo Switch";
- DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
-
- DiscordClient.SetPresence(DiscordPresence);
- }
+ DiscordIntegrationModule.SwitchToPlayingState(_device.System.TitleId, _device.System.TitleName);
string metadataFolder = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "games", _device.System.TitleId, "gui");
string metadataFile = System.IO.Path.Combine(metadataFolder, "metadata.json");
@@ -384,8 +336,8 @@ namespace Ryujinx.Ui
private static void CreateGameWindow()
{
- Configuration.ConfigureHid(_device, SwitchSettings.SwitchConfig);
-
+ _device.Hid.InitializePrimaryController(ConfigurationState.Instance.Hid.ControllerType);
+
using (_screen = new GlScreen(_device, _renderer))
{
_screen.MainLoop();
@@ -444,7 +396,6 @@ namespace Ryujinx.Ui
Profile.FinishProfiling();
_device.Dispose();
_audioOut.Dispose();
- DiscordClient?.Dispose();
Logger.Shutdown();
Environment.Exit(0);
}
@@ -607,7 +558,7 @@ namespace Ryujinx.Ui
private void Settings_Pressed(object sender, EventArgs args)
{
- SwitchSettings settingsWin = new SwitchSettings(_device);
+ SwitchSettings settingsWin = new SwitchSettings();
settingsWin.Show();
}
@@ -633,121 +584,81 @@ namespace Ryujinx.Ui
private void Fav_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.FavColumn = _favToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.FavColumn.Value = _favToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void Icon_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.IconColumn = _iconToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.IconColumn.Value = _iconToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void Title_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.AppColumn = _appToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.AppColumn.Value = _appToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void Developer_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.DevColumn = _developerToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.DevColumn.Value = _developerToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void Version_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.VersionColumn = _versionToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.VersionColumn.Value = _versionToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void TimePlayed_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.TimePlayedColumn = _timePlayedToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn.Value = _timePlayedToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void LastPlayed_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.LastPlayedColumn = _lastPlayedToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn.Value = _lastPlayedToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void FileExt_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.FileExtColumn = _fileExtToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn.Value = _fileExtToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void FileSize_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.FileSizeColumn = _fileSizeToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn.Value = _fileSizeToggle.Active;
+ SaveConfig();
UpdateColumns();
}
private void Path_Toggled(object sender, EventArgs args)
{
- GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
-
- updatedColumns.PathColumn = _pathToggle.Active;
- SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
-
- Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ ConfigurationState.Instance.Ui.GuiColumns.PathColumn.Value = _pathToggle.Active;
+ SaveConfig();
UpdateColumns();
}
@@ -865,5 +776,10 @@ namespace Ryujinx.Ui
return 0;
}
}
+
+ public static void SaveConfig()
+ {
+ ConfigurationState.Instance.ToFileFormat().SaveConfig(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ }
}
}
diff --git a/Ryujinx/Ui/NpadController.cs b/Ryujinx/Ui/NpadController.cs
index f72c407551..67961b4914 100644
--- a/Ryujinx/Ui/NpadController.cs
+++ b/Ryujinx/Ui/NpadController.cs
@@ -1,160 +1,57 @@
using OpenTK;
using OpenTK.Input;
+using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE.Input;
using System;
+using InnerNpadController = Ryujinx.Common.Configuration.Hid.NpadController;
+
namespace Ryujinx.Ui.Input
{
- public enum ControllerInputId
- {
- Button0,
- Button1,
- Button2,
- Button3,
- Button4,
- Button5,
- Button6,
- Button7,
- Button8,
- Button9,
- Button10,
- Button11,
- Button12,
- Button13,
- Button14,
- Button15,
- Button16,
- Button17,
- Button18,
- Button19,
- Button20,
- Axis0,
- Axis1,
- Axis2,
- Axis3,
- Axis4,
- Axis5,
- Hat0Up,
- Hat0Down,
- Hat0Left,
- Hat0Right,
- Hat1Up,
- Hat1Down,
- Hat1Left,
- Hat1Right,
- Hat2Up,
- Hat2Down,
- Hat2Left,
- Hat2Right,
- }
-
- public struct NpadControllerLeft
- {
- public ControllerInputId Stick;
- public ControllerInputId StickButton;
- public ControllerInputId ButtonMinus;
- public ControllerInputId ButtonL;
- public ControllerInputId ButtonZl;
- public ControllerInputId DPadUp;
- public ControllerInputId DPadDown;
- public ControllerInputId DPadLeft;
- public ControllerInputId DPadRight;
- }
-
- public struct NpadControllerRight
- {
- public ControllerInputId Stick;
- public ControllerInputId StickButton;
- public ControllerInputId ButtonA;
- public ControllerInputId ButtonB;
- public ControllerInputId ButtonX;
- public ControllerInputId ButtonY;
- public ControllerInputId ButtonPlus;
- public ControllerInputId ButtonR;
- public ControllerInputId ButtonZr;
- }
-
public class NpadController
{
- ///
- /// Enables or disables controller support
- ///
- public bool Enabled { get; private set; }
+ private InnerNpadController _inner;
- ///
- /// Controller Device Index
- ///
- public int Index { get; private set; }
-
- ///
- /// Controller Analog Stick Deadzone
- ///
- public float Deadzone { get; private set; }
-
- ///
- /// Controller Trigger Threshold
- ///
- public float TriggerThreshold { get; private set; }
-
- ///
- /// Left JoyCon Controller Bindings
- ///
- public NpadControllerLeft LeftJoycon { get; private set; }
-
- ///
- /// Right JoyCon Controller Bindings
- ///
- public NpadControllerRight RightJoycon { get; private set; }
-
- public NpadController(
- bool enabled,
- int index,
- float deadzone,
- float triggerThreshold,
- NpadControllerLeft leftJoycon,
- NpadControllerRight rightJoycon)
+ // NOTE: This should be initialized AFTER GTK for compat reasons with OpenTK SDL2 backend and GTK on Linux.
+ // BODY: Usage of Joystick.GetState must be defer to after GTK full initialization. Otherwise, GTK will segfault because SDL2 was already init *sighs*
+ public NpadController(InnerNpadController inner)
{
- Enabled = enabled;
- Index = index;
- Deadzone = deadzone;
- TriggerThreshold = triggerThreshold;
- LeftJoycon = leftJoycon;
- RightJoycon = rightJoycon;
+ _inner = inner;
}
- public void SetEnabled(bool enabled)
+ private bool IsEnabled()
{
- Enabled = enabled;
+ return _inner.Enabled && Joystick.GetState(_inner.Index).IsConnected;
}
public ControllerButtons GetButtons()
{
- if (!Enabled)
+ if (!IsEnabled())
{
return 0;
}
- JoystickState joystickState = Joystick.GetState(Index);
+ JoystickState joystickState = Joystick.GetState(_inner.Index);
ControllerButtons buttons = 0;
- if (IsActivated(joystickState, LeftJoycon.DPadUp)) buttons |= ControllerButtons.DpadUp;
- if (IsActivated(joystickState, LeftJoycon.DPadDown)) buttons |= ControllerButtons.DpadDown;
- if (IsActivated(joystickState, LeftJoycon.DPadLeft)) buttons |= ControllerButtons.DpadLeft;
- if (IsActivated(joystickState, LeftJoycon.DPadRight)) buttons |= ControllerButtons.DPadRight;
- if (IsActivated(joystickState, LeftJoycon.StickButton)) buttons |= ControllerButtons.StickLeft;
- if (IsActivated(joystickState, LeftJoycon.ButtonMinus)) buttons |= ControllerButtons.Minus;
- if (IsActivated(joystickState, LeftJoycon.ButtonL)) buttons |= ControllerButtons.L;
- if (IsActivated(joystickState, LeftJoycon.ButtonZl)) buttons |= ControllerButtons.Zl;
+ if (IsActivated(joystickState, _inner.LeftJoycon.DPadUp)) buttons |= ControllerButtons.DpadUp;
+ if (IsActivated(joystickState, _inner.LeftJoycon.DPadDown)) buttons |= ControllerButtons.DpadDown;
+ if (IsActivated(joystickState, _inner.LeftJoycon.DPadLeft)) buttons |= ControllerButtons.DpadLeft;
+ if (IsActivated(joystickState, _inner.LeftJoycon.DPadRight)) buttons |= ControllerButtons.DPadRight;
+ if (IsActivated(joystickState, _inner.LeftJoycon.StickButton)) buttons |= ControllerButtons.StickLeft;
+ if (IsActivated(joystickState, _inner.LeftJoycon.ButtonMinus)) buttons |= ControllerButtons.Minus;
+ if (IsActivated(joystickState, _inner.LeftJoycon.ButtonL)) buttons |= ControllerButtons.L;
+ if (IsActivated(joystickState, _inner.LeftJoycon.ButtonZl)) buttons |= ControllerButtons.Zl;
- if (IsActivated(joystickState, RightJoycon.ButtonA)) buttons |= ControllerButtons.A;
- if (IsActivated(joystickState, RightJoycon.ButtonB)) buttons |= ControllerButtons.B;
- if (IsActivated(joystickState, RightJoycon.ButtonX)) buttons |= ControllerButtons.X;
- if (IsActivated(joystickState, RightJoycon.ButtonY)) buttons |= ControllerButtons.Y;
- if (IsActivated(joystickState, RightJoycon.StickButton)) buttons |= ControllerButtons.StickRight;
- if (IsActivated(joystickState, RightJoycon.ButtonPlus)) buttons |= ControllerButtons.Plus;
- if (IsActivated(joystickState, RightJoycon.ButtonR)) buttons |= ControllerButtons.R;
- if (IsActivated(joystickState, RightJoycon.ButtonZr)) buttons |= ControllerButtons.Zr;
+ if (IsActivated(joystickState, _inner.RightJoycon.ButtonA)) buttons |= ControllerButtons.A;
+ if (IsActivated(joystickState, _inner.RightJoycon.ButtonB)) buttons |= ControllerButtons.B;
+ if (IsActivated(joystickState, _inner.RightJoycon.ButtonX)) buttons |= ControllerButtons.X;
+ if (IsActivated(joystickState, _inner.RightJoycon.ButtonY)) buttons |= ControllerButtons.Y;
+ if (IsActivated(joystickState, _inner.RightJoycon.StickButton)) buttons |= ControllerButtons.StickRight;
+ if (IsActivated(joystickState, _inner.RightJoycon.ButtonPlus)) buttons |= ControllerButtons.Plus;
+ if (IsActivated(joystickState, _inner.RightJoycon.ButtonR)) buttons |= ControllerButtons.R;
+ if (IsActivated(joystickState, _inner.RightJoycon.ButtonZr)) buttons |= ControllerButtons.Zr;
return buttons;
}
@@ -169,7 +66,7 @@ namespace Ryujinx.Ui.Input
{
int axis = controllerInputId - ControllerInputId.Axis0;
- return joystickState.GetAxis(axis) > TriggerThreshold;
+ return joystickState.GetAxis(axis) > _inner.TriggerThreshold;
}
else if (controllerInputId <= ControllerInputId.Hat2Right)
{
@@ -190,22 +87,22 @@ namespace Ryujinx.Ui.Input
public (short, short) GetLeftStick()
{
- if (!Enabled)
+ if (!IsEnabled())
{
return (0, 0);
}
- return GetStick(LeftJoycon.Stick);
+ return GetStick(_inner.LeftJoycon.Stick);
}
public (short, short) GetRightStick()
{
- if (!Enabled)
+ if (!IsEnabled())
{
return (0, 0);
}
- return GetStick(RightJoycon.Stick);
+ return GetStick(_inner.RightJoycon.Stick);
}
private (short, short) GetStick(ControllerInputId stickInputId)
@@ -215,7 +112,7 @@ namespace Ryujinx.Ui.Input
return (0, 0);
}
- JoystickState jsState = Joystick.GetState(Index);
+ JoystickState jsState = Joystick.GetState(_inner.Index);
int xAxis = stickInputId - ControllerInputId.Axis0;
@@ -227,8 +124,8 @@ namespace Ryujinx.Ui.Input
private (short, short) ApplyDeadzone(Vector2 axis)
{
- return (ClampAxis(MathF.Abs(axis.X) > Deadzone ? axis.X : 0f),
- ClampAxis(MathF.Abs(axis.Y) > Deadzone ? axis.Y : 0f));
+ return (ClampAxis(MathF.Abs(axis.X) > _inner.Deadzone ? axis.X : 0f),
+ ClampAxis(MathF.Abs(axis.Y) > _inner.Deadzone ? axis.Y : 0f));
}
private static short ClampAxis(float value)
diff --git a/Ryujinx/Ui/SwitchSettings.cs b/Ryujinx/Ui/SwitchSettings.cs
index 955c6b0b64..5c56cf7ea1 100644
--- a/Ryujinx/Ui/SwitchSettings.cs
+++ b/Ryujinx/Ui/SwitchSettings.cs
@@ -1,12 +1,12 @@
using Gtk;
-using Ryujinx.HLE.HOS.SystemState;
-using Ryujinx.HLE.Input;
-using Ryujinx.Ui.Input;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
+using Ryujinx.Configuration;
+using Ryujinx.Configuration.System;
+using Ryujinx.Configuration.Hid;
using GUI = Gtk.Builder.ObjectAttribute;
@@ -14,10 +14,6 @@ namespace Ryujinx.Ui
{
public class SwitchSettings : Window
{
- internal static Configuration SwitchConfig { get; set; }
-
- private readonly HLE.Switch _device;
-
private static ListStore _gameDirsBoxStore;
private static bool _listeningForKeypress;
@@ -83,16 +79,12 @@ namespace Ryujinx.Ui
#pragma warning restore CS0649
#pragma warning restore IDE0044
- public static void ConfigureSettings(Configuration instance) { SwitchConfig = instance; }
+ public SwitchSettings() : this(new Builder("Ryujinx.Ui.SwitchSettings.glade")) { }
- public SwitchSettings(HLE.Switch device) : this(new Builder("Ryujinx.Ui.SwitchSettings.glade"), device) { }
-
- private SwitchSettings(Builder builder, HLE.Switch device) : base(builder.GetObject("_settingsWin").Handle)
+ private SwitchSettings(Builder builder) : base(builder.GetObject("_settingsWin").Handle)
{
builder.Autoconnect(this);
- _device = device;
-
_settingsWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
_controller1Image.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyCon.png", 500, 500);
@@ -124,60 +116,123 @@ namespace Ryujinx.Ui
_controller1Type.Changed += (sender, args) => Controller_Changed(sender, args, _controller1Type.ActiveId, _controller1Image);
//Setup Currents
- if (SwitchConfig.EnableFileLog) _fileLogToggle.Click();
- if (SwitchConfig.LoggingEnableError) _errorLogToggle.Click();
- if (SwitchConfig.LoggingEnableWarn) _warningLogToggle.Click();
- if (SwitchConfig.LoggingEnableInfo) _infoLogToggle.Click();
- if (SwitchConfig.LoggingEnableStub) _stubLogToggle.Click();
- if (SwitchConfig.LoggingEnableDebug) _debugLogToggle.Click();
- if (SwitchConfig.LoggingEnableGuest) _guestLogToggle.Click();
- if (SwitchConfig.LoggingEnableFsAccessLog) _fsAccessLogToggle.Click();
- if (SwitchConfig.DockedMode) _dockedModeToggle.Click();
- if (SwitchConfig.EnableDiscordIntegration) _discordToggle.Click();
- if (SwitchConfig.EnableVsync) _vSyncToggle.Click();
- if (SwitchConfig.EnableMulticoreScheduling) _multiSchedToggle.Click();
- if (SwitchConfig.EnableFsIntegrityChecks) _fsicToggle.Click();
- if (SwitchConfig.IgnoreMissingServices) _ignoreToggle.Click();
- if (SwitchConfig.EnableKeyboard) _directKeyboardAccess.Click();
- if (SwitchConfig.EnableCustomTheme) _custThemeToggle.Click();
+ if (ConfigurationState.Instance.Logger.EnableFileLog)
+ {
+ _fileLogToggle.Click();
+ }
- _systemLanguageSelect.SetActiveId(SwitchConfig.SystemLanguage.ToString());
- _controller1Type .SetActiveId(SwitchConfig.ControllerType.ToString());
+ if (ConfigurationState.Instance.Logger.EnableError)
+ {
+ _errorLogToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Logger.EnableWarn)
+ {
+ _warningLogToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Logger.EnableInfo)
+ {
+ _infoLogToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Logger.EnableStub)
+ {
+ _stubLogToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Logger.EnableDebug)
+ {
+ _debugLogToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Logger.EnableGuest)
+ {
+ _guestLogToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Logger.EnableFsAccessLog)
+ {
+ _fsAccessLogToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.System.EnableDockedMode)
+ {
+ _dockedModeToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.EnableDiscordIntegration)
+ {
+ _discordToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Graphics.EnableVsync)
+ {
+ _vSyncToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
+ {
+ _multiSchedToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.System.EnableFsIntegrityChecks)
+ {
+ _fsicToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.System.IgnoreMissingServices)
+ {
+ _ignoreToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Hid.EnableKeyboard)
+ {
+ _directKeyboardAccess.Click();
+ }
+
+ if (ConfigurationState.Instance.Ui.EnableCustomTheme)
+ {
+ _custThemeToggle.Click();
+ }
+
+ _systemLanguageSelect.SetActiveId(ConfigurationState.Instance.System.Language.Value.ToString());
+ _controller1Type .SetActiveId(ConfigurationState.Instance.Hid.ControllerType.Value.ToString());
Controller_Changed(null, null, _controller1Type.ActiveId, _controller1Image);
- _lStickUp1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickUp.ToString();
- _lStickDown1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickDown.ToString();
- _lStickLeft1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickLeft.ToString();
- _lStickRight1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickRight.ToString();
- _lStickButton1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickButton.ToString();
- _dpadUp1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadUp.ToString();
- _dpadDown1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadDown.ToString();
- _dpadLeft1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadLeft.ToString();
- _dpadRight1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadRight.ToString();
- _minus1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonMinus.ToString();
- _l1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonL.ToString();
- _zL1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonZl.ToString();
- _rStickUp1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickUp.ToString();
- _rStickDown1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickDown.ToString();
- _rStickLeft1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickLeft.ToString();
- _rStickRight1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickRight.ToString();
- _rStickButton1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickButton.ToString();
- _a1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonA.ToString();
- _b1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonB.ToString();
- _x1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonX.ToString();
- _y1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonY.ToString();
- _plus1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonPlus.ToString();
- _r1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonR.ToString();
- _zR1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonZr.ToString();
+ _lStickUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickUp.ToString();
+ _lStickDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickDown.ToString();
+ _lStickLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickLeft.ToString();
+ _lStickRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickRight.ToString();
+ _lStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickButton.ToString();
+ _dpadUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadUp.ToString();
+ _dpadDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadDown.ToString();
+ _dpadLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadLeft.ToString();
+ _dpadRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadRight.ToString();
+ _minus1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonMinus.ToString();
+ _l1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonL.ToString();
+ _zL1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonZl.ToString();
+ _rStickUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickUp.ToString();
+ _rStickDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickDown.ToString();
+ _rStickLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickLeft.ToString();
+ _rStickRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickRight.ToString();
+ _rStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickButton.ToString();
+ _a1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonA.ToString();
+ _b1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonB.ToString();
+ _x1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonX.ToString();
+ _y1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonY.ToString();
+ _plus1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonPlus.ToString();
+ _r1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonR.ToString();
+ _zR1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonZr.ToString();
- _custThemePath.Buffer.Text = SwitchConfig.CustomThemePath;
- _graphicsShadersDumpPath.Buffer.Text = SwitchConfig.GraphicsShadersDumpPath;
- _fsLogSpinAdjustment.Value = SwitchConfig.FsGlobalAccessLogMode;
+ _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
+ _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
+ _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
_gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0);
_gameDirsBoxStore = new ListStore(typeof(string));
_gameDirsBox.Model = _gameDirsBoxStore;
- foreach (string gameDir in SwitchConfig.GameDirs)
+ foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value)
{
_gameDirsBoxStore.AppendValues(gameDir);
}
@@ -208,7 +263,7 @@ namespace Ryujinx.Ui
string key = keyPressed.Event.Key.ToString();
string capKey = key.First().ToString().ToUpper() + key.Substring(1);
- if (Enum.IsDefined(typeof(OpenTK.Input.Key), capKey))
+ if (Enum.IsDefined(typeof(Configuration.Hid.Key), capKey))
{
button.Label = capKey;
}
@@ -325,65 +380,63 @@ namespace Ryujinx.Ui
_gameDirsBoxStore.IterNext(ref treeIter);
}
- SwitchConfig.LoggingEnableError = _errorLogToggle.Active;
- SwitchConfig.LoggingEnableWarn = _warningLogToggle.Active;
- SwitchConfig.LoggingEnableInfo = _infoLogToggle.Active;
- SwitchConfig.LoggingEnableStub = _stubLogToggle.Active;
- SwitchConfig.LoggingEnableDebug = _debugLogToggle.Active;
- SwitchConfig.LoggingEnableGuest = _guestLogToggle.Active;
- SwitchConfig.LoggingEnableFsAccessLog = _fsAccessLogToggle.Active;
- SwitchConfig.EnableFileLog = _fileLogToggle.Active;
- SwitchConfig.DockedMode = _dockedModeToggle.Active;
- SwitchConfig.EnableDiscordIntegration = _discordToggle.Active;
- SwitchConfig.EnableVsync = _vSyncToggle.Active;
- SwitchConfig.EnableMulticoreScheduling = _multiSchedToggle.Active;
- SwitchConfig.EnableFsIntegrityChecks = _fsicToggle.Active;
- SwitchConfig.IgnoreMissingServices = _ignoreToggle.Active;
- SwitchConfig.EnableKeyboard = _directKeyboardAccess.Active;
- SwitchConfig.EnableCustomTheme = _custThemeToggle.Active;
+ ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active;
+ ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active;
+ ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
+ ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
+ ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
+ ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
+ ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
+ ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active;
+ ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active;
- SwitchConfig.KeyboardControls.LeftJoycon = new NpadKeyboardLeft()
+ ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon = new NpadKeyboardLeft()
{
- StickUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickUp1.Label),
- StickDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickDown1.Label),
- StickLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickLeft1.Label),
- StickRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickRight1.Label),
- StickButton = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickButton1.Label),
- DPadUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadUp1.Label),
- DPadDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadDown1.Label),
- DPadLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadLeft1.Label),
- DPadRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadRight1.Label),
- ButtonMinus = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _minus1.Label),
- ButtonL = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _l1.Label),
- ButtonZl = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _zL1.Label),
+ StickUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickUp1.Label),
+ StickDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickDown1.Label),
+ StickLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickLeft1.Label),
+ StickRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickRight1.Label),
+ StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickButton1.Label),
+ DPadUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadUp1.Label),
+ DPadDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadDown1.Label),
+ DPadLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadLeft1.Label),
+ DPadRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadRight1.Label),
+ ButtonMinus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _minus1.Label),
+ ButtonL = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _l1.Label),
+ ButtonZl = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zL1.Label),
};
- SwitchConfig.KeyboardControls.RightJoycon = new NpadKeyboardRight()
+ ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon = new NpadKeyboardRight()
{
- StickUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickUp1.Label),
- StickDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickDown1.Label),
- StickLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickLeft1.Label),
- StickRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickRight1.Label),
- StickButton = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickButton1.Label),
- ButtonA = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _a1.Label),
- ButtonB = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _b1.Label),
- ButtonX = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _x1.Label),
- ButtonY = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _y1.Label),
- ButtonPlus = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _plus1.Label),
- ButtonR = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _r1.Label),
- ButtonZr = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _zR1.Label),
+ StickUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickUp1.Label),
+ StickDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickDown1.Label),
+ StickLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickLeft1.Label),
+ StickRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickRight1.Label),
+ StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickButton1.Label),
+ ButtonA = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _a1.Label),
+ ButtonB = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _b1.Label),
+ ButtonX = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _x1.Label),
+ ButtonY = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _y1.Label),
+ ButtonPlus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _plus1.Label),
+ ButtonR = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _r1.Label),
+ ButtonZr = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zR1.Label),
};
- SwitchConfig.SystemLanguage = (SystemLanguage)Enum.Parse(typeof(SystemLanguage), _systemLanguageSelect.ActiveId);
- SwitchConfig.ControllerType = (ControllerStatus)Enum.Parse(typeof(ControllerStatus), _controller1Type.ActiveId);
- SwitchConfig.CustomThemePath = _custThemePath.Buffer.Text;
- SwitchConfig.GraphicsShadersDumpPath = _graphicsShadersDumpPath.Buffer.Text;
- SwitchConfig.GameDirs = gameDirs;
- SwitchConfig.FsGlobalAccessLogMode = (int)_fsLogSpinAdjustment.Value;
-
- Configuration.SaveConfig(SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
- Configuration.Configure(_device, SwitchConfig);
+ ConfigurationState.Instance.System.Language.Value = (Language)Enum.Parse(typeof(Language), _systemLanguageSelect.ActiveId);
+ ConfigurationState.Instance.Hid.ControllerType.Value = (ControllerType)Enum.Parse(typeof(ControllerType), _controller1Type.ActiveId);
+ ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text;
+ ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text;
+ ConfigurationState.Instance.Ui.GameDirs.Value = gameDirs;
+ ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value;
+ MainWindow.SaveConfig();
MainWindow.ApplyTheme();
#pragma warning disable CS4014
MainWindow.UpdateGameTable();
From bd010869a5e6005331be2f6f3ed0e72dfc006d41 Mon Sep 17 00:00:00 2001
From: Xpl0itR
Date: Sun, 22 Dec 2019 02:49:51 +0000
Subject: [PATCH 6/8] GUI: Implement context menu for the game table (#840)
* Add context menu to the game table
* Minor bugfix and cleanup
* add ability to create directory if it doesn't exist
* nit
* dont show menu when right-clicking nothing
---
Ryujinx/Ryujinx.csproj | 2 +
Ryujinx/Ui/GameTableContextMenu.cs | 75 ++++++++++++++++++++++++
Ryujinx/Ui/GameTableContextMenu.glade | 18 ++++++
Ryujinx/Ui/MainWindow.cs | 82 +++++++++++++--------------
Ryujinx/Ui/MainWindow.glade | 2 +-
5 files changed, 135 insertions(+), 44 deletions(-)
create mode 100644 Ryujinx/Ui/GameTableContextMenu.cs
create mode 100644 Ryujinx/Ui/GameTableContextMenu.glade
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index b231ddb8d2..e610e8277e 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -43,6 +43,7 @@
+
@@ -63,6 +64,7 @@
+
diff --git a/Ryujinx/Ui/GameTableContextMenu.cs b/Ryujinx/Ui/GameTableContextMenu.cs
new file mode 100644
index 0000000000..f8d1d6815f
--- /dev/null
+++ b/Ryujinx/Ui/GameTableContextMenu.cs
@@ -0,0 +1,75 @@
+using Gtk;
+using Ryujinx.HLE.FileSystem;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+
+using GUI = Gtk.Builder.ObjectAttribute;
+
+namespace Ryujinx.Ui
+{
+ public class GameTableContextMenu : Menu
+ {
+ private static ListStore _gameTableStore;
+ private static TreeIter _rowIter;
+
+#pragma warning disable CS0649
+#pragma warning disable IDE0044
+ [GUI] MenuItem _openSaveDir;
+#pragma warning restore CS0649
+#pragma warning restore IDE0044
+
+ public GameTableContextMenu(ListStore gameTableStore, TreeIter rowIter) : this(new Builder("Ryujinx.Ui.GameTableContextMenu.glade"), gameTableStore, rowIter) { }
+
+ private GameTableContextMenu(Builder builder, ListStore gameTableStore, TreeIter rowIter) : base(builder.GetObject("_contextMenu").Handle)
+ {
+ builder.Autoconnect(this);
+
+ _openSaveDir.Activated += OpenSaveDir_Clicked;
+
+ _gameTableStore = gameTableStore;
+ _rowIter = rowIter;
+ }
+
+ //Events
+ private void OpenSaveDir_Clicked(object sender, EventArgs args)
+ {
+ string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
+ string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
+ string saveDir = System.IO.Path.Combine(new VirtualFileSystem().GetNandPath(), "user", "save", "0000000000000000", "00000000000000000000000000000001", titleId, "0");
+
+ if (!Directory.Exists(saveDir))
+ {
+ MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.YesNo, null)
+ {
+ Title = "Ryujinx",
+ Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
+ Text = $"Could not find save directory for {titleName} [{titleId}]",
+ SecondaryText = "Would you like to create the directory?",
+ WindowPosition = WindowPosition.Center
+ };
+
+ if (messageDialog.Run() == (int)ResponseType.Yes)
+ {
+ Directory.CreateDirectory(saveDir);
+ }
+ else
+ {
+ messageDialog.Dispose();
+
+ return;
+ }
+
+ messageDialog.Dispose();
+ }
+
+ Process.Start(new ProcessStartInfo()
+ {
+ FileName = saveDir,
+ UseShellExecute = true,
+ Verb = "open"
+ });
+ }
+ }
+}
diff --git a/Ryujinx/Ui/GameTableContextMenu.glade b/Ryujinx/Ui/GameTableContextMenu.glade
new file mode 100644
index 0000000000..2c9e097292
--- /dev/null
+++ b/Ryujinx/Ui/GameTableContextMenu.glade
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index e0bd849479..dc3315e979 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -38,16 +38,6 @@ namespace Ryujinx.Ui
private static bool _gameLoaded;
private static bool _ending;
- private static TreeViewColumn _favColumn;
- private static TreeViewColumn _appColumn;
- private static TreeViewColumn _devColumn;
- private static TreeViewColumn _versionColumn;
- private static TreeViewColumn _timePlayedColumn;
- private static TreeViewColumn _lastPlayedColumn;
- private static TreeViewColumn _fileExtColumn;
- private static TreeViewColumn _fileSizeColumn;
- private static TreeViewColumn _pathColumn;
-
private static TreeView _treeView;
#pragma warning disable CS0649
@@ -66,6 +56,7 @@ namespace Ryujinx.Ui
[GUI] CheckMenuItem _fileSizeToggle;
[GUI] CheckMenuItem _pathToggle;
[GUI] TreeView _gameTable;
+ [GUI] TreeSelection _gameTableSelection;
[GUI] Label _progressLabel;
[GUI] LevelBar _progressBar;
#pragma warning restore CS0649
@@ -81,6 +72,8 @@ namespace Ryujinx.Ui
ApplicationLibrary.ApplicationAdded += Application_Added;
+ _gameTable.ButtonReleaseEvent += Row_Clicked;
+
_renderer = new OglRenderer();
_audioOut = InitializeAudioEngine();
@@ -173,26 +166,16 @@ namespace Ryujinx.Ui
foreach (TreeViewColumn column in _gameTable.Columns)
{
- if (column.Title == "Fav") _favColumn = column;
- else if (column.Title == "Application") _appColumn = column;
- else if (column.Title == "Developer") _devColumn = column;
- else if (column.Title == "Version") _versionColumn = column;
- else if (column.Title == "Time Played") _timePlayedColumn = column;
- else if (column.Title == "Last Played") _lastPlayedColumn = column;
- else if (column.Title == "File Ext") _fileExtColumn = column;
- else if (column.Title == "File Size") _fileSizeColumn = column;
- else if (column.Title == "Path") _pathColumn = column;
+ if (column.Title == "Fav" && ConfigurationState.Instance.Ui.GuiColumns.FavColumn) column.SortColumnId = 0;
+ else if (column.Title == "Application" && ConfigurationState.Instance.Ui.GuiColumns.AppColumn) column.SortColumnId = 2;
+ else if (column.Title == "Developer" && ConfigurationState.Instance.Ui.GuiColumns.DevColumn) column.SortColumnId = 3;
+ else if (column.Title == "Version" && ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) column.SortColumnId = 4;
+ else if (column.Title == "Time Played" && ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) column.SortColumnId = 5;
+ else if (column.Title == "Last Played" && ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) column.SortColumnId = 6;
+ else if (column.Title == "File Ext" && ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) column.SortColumnId = 7;
+ else if (column.Title == "File Size" && ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) column.SortColumnId = 8;
+ else if (column.Title == "Path" && ConfigurationState.Instance.Ui.GuiColumns.PathColumn) column.SortColumnId = 9;
}
-
- if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favColumn.SortColumnId = 0;
- if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appColumn.SortColumnId = 2;
- if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _devColumn.SortColumnId = 3;
- if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _versionColumn.SortColumnId = 4;
- if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedColumn.SortColumnId = 5;
- if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedColumn.SortColumnId = 6;
- if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _fileExtColumn.SortColumnId = 7;
- if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeColumn.SortColumnId = 8;
- if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathColumn.SortColumnId = 9;
}
private HLE.Switch InitializeSwitchInstance()
@@ -421,24 +404,24 @@ namespace Ryujinx.Ui
}
//Events
- private void Application_Added(object sender, ApplicationAddedEventArgs e)
+ private void Application_Added(object sender, ApplicationAddedEventArgs args)
{
Application.Invoke(delegate
{
_tableStore.AppendValues(
- e.AppData.Favorite,
- new Gdk.Pixbuf(e.AppData.Icon, 75, 75),
- $"{e.AppData.TitleName}\n{e.AppData.TitleId.ToUpper()}",
- e.AppData.Developer,
- e.AppData.Version,
- e.AppData.TimePlayed,
- e.AppData.LastPlayed,
- e.AppData.FileExtension,
- e.AppData.FileSize,
- e.AppData.Path);
+ args.AppData.Favorite,
+ new Gdk.Pixbuf(args.AppData.Icon, 75, 75),
+ $"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}",
+ args.AppData.Developer,
+ args.AppData.Version,
+ args.AppData.TimePlayed,
+ args.AppData.LastPlayed,
+ args.AppData.FileExtension,
+ args.AppData.FileSize,
+ args.AppData.Path);
- _progressLabel.Text = $"{e.NumAppsLoaded}/{e.NumAppsFound} Games Loaded";
- _progressBar.Value = (float)e.NumAppsLoaded / e.NumAppsFound;
+ _progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded";
+ _progressBar.Value = (float)args.NumAppsLoaded / args.NumAppsFound;
});
}
@@ -477,12 +460,25 @@ namespace Ryujinx.Ui
private void Row_Activated(object sender, RowActivatedArgs args)
{
- _tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path.ToString()));
+ _gameTableSelection.GetSelected(out TreeIter treeIter);
string path = (string)_tableStore.GetValue(treeIter, 9);
LoadApplication(path);
}
+ private void Row_Clicked(object sender, ButtonReleaseEventArgs args)
+ {
+ if (args.Event.Button != 3) return;
+
+ _gameTableSelection.GetSelected(out TreeIter treeIter);
+
+ if (treeIter.UserData == IntPtr.Zero) return;
+
+ GameTableContextMenu contextMenu = new GameTableContextMenu(_tableStore, treeIter);
+ contextMenu.ShowAll();
+ contextMenu.PopupAtPointer(null);
+ }
+
private void Load_Application_File(object sender, EventArgs args)
{
FileChooserDialog fileChooser = new FileChooserDialog("Choose the file to open", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Open", ResponseType.Accept);
diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade
index 6d66dc2007..fcf91bc4c5 100644
--- a/Ryujinx/Ui/MainWindow.glade
+++ b/Ryujinx/Ui/MainWindow.glade
@@ -330,7 +330,7 @@
True
-
+
From bce8972e7a42687c2add1bd939d2c8b2441dc5f1 Mon Sep 17 00:00:00 2001
From: Thog
Date: Sun, 22 Dec 2019 20:33:59 +0100
Subject: [PATCH 7/8] Fix an endge case in bsd IClient::Poll implementation
(#848)
This add some code to handle usage of poll without any fds.
This is required by Dark Souls Remastered main loop logic as it's
calling it without any fds during initialization.
===
General system stability improvements to enhance the user's experience.
---
.../HOS/Services/Sockets/Bsd/IClient.cs | 26 ++++++++++++++-----
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
index af9b38815e..b2b3d05260 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs
@@ -1,11 +1,12 @@
-using Ryujinx.Common;
-using Ryujinx.Common.Logging;
+using Ryujinx.Common.Logging;
using Ryujinx.HLE.Utilities;
+using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
+using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
@@ -379,13 +380,26 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
}
}
- try
+ if (fdsCount != 0)
{
- System.Net.Sockets.Socket.Select(readEvents, writeEvents, errorEvents, timeout);
+ try
+ {
+ System.Net.Sockets.Socket.Select(readEvents, writeEvents, errorEvents, timeout);
+ }
+ catch (SocketException exception)
+ {
+ return WriteWinSock2Error(context, (WsaError)exception.ErrorCode);
+ }
}
- catch (SocketException exception)
+ else if (timeout == -1)
{
- return WriteWinSock2Error(context, (WsaError)exception.ErrorCode);
+ // FIXME: If we get a timeout of -1 and there is no fds to wait on, this should kill the KProces. (need to check that with re)
+ throw new InvalidOperationException();
+ }
+ else
+ {
+ // FIXME: We should make the KThread sleep but we can't do much about it yet.
+ Thread.Sleep(timeout);
}
for (int i = 0; i < fdsCount; i++)
From 17b2be7174b857a48c24592e2e6c067324821ade Mon Sep 17 00:00:00 2001
From: Thog
Date: Sun, 22 Dec 2019 20:36:24 +0100
Subject: [PATCH 8/8] Also use Github Actions as a CI" (#847)
---
.github/workflows/build.yml | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 .github/workflows/build.yml
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000000..cca6c60806
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,27 @@
+name: "Build job"
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - '*'
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macOS-latest, windows-latest]
+ dotnet: ['3.1.100']
+ environment: ['Debug', 'Release', 'Profile Debug', 'Profile Release']
+ name: ${{ matrix.environment }} build (Dotnet ${{ matrix.dotnet }}, OS ${{ matrix.os }})
+ steps:
+ - uses: actions/checkout@master
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: ${{ matrix.dotnet }}
+ - name: Build
+ run: dotnet build -c "${{ matrix.environment }}"
+ - name: Test
+ run: dotnet test -c "${{ matrix.environment }}"
\ No newline at end of file