diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 22fbe8d0a8..8eed33f9e3 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -1,4 +1,5 @@ -using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using Ryujinx.HLE.HOS.Services.Am.AppletAE; using System; using System.IO; @@ -22,7 +23,8 @@ namespace Ryujinx.HLE.HOS.Applets private SoftwareKeyboardConfig _keyboardConfig; - private string _textValue = DEFAULT_TEXT; + private string _textValue = DEFAULT_TEXT; + private Encoding _encoding = Encoding.Unicode; public event EventHandler AppletStateChanged; @@ -41,6 +43,7 @@ namespace Ryujinx.HLE.HOS.Applets var transferMemory = _normalSession.Pop(); _keyboardConfig = ReadStruct(keyboardConfig); + _encoding = _keyboardConfig.UseUtf8 ? Encoding.UTF8 : Encoding.Unicode; _state = SoftwareKeyboardState.Ready; @@ -58,7 +61,7 @@ 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; } @@ -70,6 +73,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,6 +89,7 @@ namespace Ryujinx.HLE.HOS.Applets _textValue = _textValue.Substring(0, (int)_keyboardConfig.StringLengthMax); } + // Does the application want to validate the text itself? if (!_keyboardConfig.CheckText) { // If the application doesn't need to validate the response, @@ -136,12 +149,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 ? INTERACTIVE_BUFFER_SIZE : STANDARD_BUFFER_SIZE; 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..92ab6060a8 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardConfig.cs @@ -2,32 +2,240 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { - // TODO(jduncanator): Define all fields - [StructLayout(LayoutKind.Explicit)] + /// + /// + /// + internal enum KeyboardMode : uint + { + /// + /// Normal keyboard. + /// + Default, + + /// + /// Number pad. The buttons at the bottom left/right are only available when they're set in the config by leftButtonText / rightButtonText. + /// + NumbersOnly, + + /// + /// QWERTY (and variants) keyboard only. + /// + LettersOnly + } + + /// + /// + /// + internal enum InvalidCharFlags : uint + { + None = 0 << 1, + + Space = 1 << 1, + + AtSymbol = 1 << 2, + + Percent = 1 << 3, + + ForwardSlash = 1 << 4, + + BackSlash = 1 << 5, + + Numbers = 1 << 6, + + DownloadCode = 1 << 7, + + Username = 1 << 8 + + } + + /// + /// + /// + internal enum PasswordMode : uint + { + /// + /// + /// + Disabled, + + /// + /// + /// + Enabled + } + + /// + /// + /// + internal enum InputFormMode : uint + { + /// + /// + /// + SingleLine, + + /// + /// + /// + MultiLine + } + + /// + /// + /// + internal enum InitialCursorPosition : uint + { + /// + /// + /// + Start, + + /// + /// + /// + End + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct SoftwareKeyboardConfig { + /// + /// + /// + const int SubmitTextLength = 8; + + /// + /// + /// + const int HeaderTextLength = 64; + + /// + /// + /// + const int SubtitleTextLength = 128; + + /// + /// + /// + const int GuideTextLength = 256; + /// /// Type of keyboard. /// - [FieldOffset(0x0)] - public SoftwareKeyboardType Type; + public KeyboardMode Mode; + + /// + /// + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = SubmitTextLength + 1)] + public string SubmitText; + + /// + /// + /// + public char LeftOptionalSymbolKey; + + /// + /// + /// + public char RightOptionalSymbolKey; + + /// + /// + /// + [MarshalAs(UnmanagedType.I1)] + public bool PredictionEnabled; + + /// + /// + /// + public InvalidCharFlags InvalidCharFlag; + + /// + /// + /// + public InitialCursorPosition InitialCursorPosition; + + /// + /// + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HeaderTextLength + 1)] + public string HeaderText; + + /// + /// + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = SubtitleTextLength + 1)] + public string SubtitleText; + + /// + /// + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = GuideTextLength + 1)] + public string GuideText; /// /// 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). /// - [FieldOffset(0x3AC)] - public uint StringLengthMax; + public int StringLengthMax; /// - /// When non-zero, specifies the max string length. When the input is too long, swkbd will display an icon and disable the ok-button. + /// When non-zero, specifies the minimum string length. /// - [FieldOffset(0x3B0)] - public uint StringLengthMaxExtended; + public int StringLengthMin; + + /// + /// + /// + public PasswordMode PasswordMode; + + /// + /// + /// + public InputFormMode InputFormMode; + + /// + /// + /// + [MarshalAs(UnmanagedType.I1)] + public bool UseNewLine; + + /// + /// When set, the software keyboard will return a string UTF-8 encoded, rather than UTF-16. + /// + [MarshalAs(UnmanagedType.I1)] + public bool UseUtf8; + + /// + /// + /// + [MarshalAs(UnmanagedType.I1)] + public bool UseBlurBackground; + + /// + /// + /// + public int InitialStringOffset; + + /// + /// + /// + public int InitialStringLength; + + /// + /// + /// + public int CustomDictionaryOffset; + + /// + /// + /// + public int CustomDictionaryCount; /// /// When set, the application will validate the entered text whilst the swkbd is still on screen. /// - [FieldOffset(0x3D0), MarshalAs(UnmanagedType.I1)] + [MarshalAs(UnmanagedType.I1)] public bool CheckText; } } 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