diff options
author | Logan Stromberg <loganstromberg@gmail.com> | 2022-11-16 14:53:17 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-16 23:53:17 +0100 |
commit | 2c9ab5e45fd45d45909b9b348580c44bdfc4d36f (patch) | |
tree | 204dec18131dcdf05837999600ad1a32435a566c | |
parent | d536cc8ae6d6725780365d858f2fd64b66d90b7f (diff) |
Prevent raw Unicode control codes from showing on software keyboard applet. (#3845)1.1.350
* Revert "Add support for releasing a semaphore to DmaClass (#2926)"
This reverts commit 521a07e6125d3a5d9781512639387a9be5f09107.
* Revert "Revert "Add support for releasing a semaphore to DmaClass (#2926)""
This reverts commit ec8a5fd05362f04cc77436ee3e45a9188777f75e.
* Strip non-visible control codes from strings before they are sent to the software keyboard to prevent ugly unicode blocks from being shown on the UI.
* remove debugging junk
* Initialize stringbuilder capacity at the start to prevent resizing (a tiny tiny microoptimization)
* Update remarks documentation. Remove unneeded imports.
* Removing a test that's actually just redundant
Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
-rw-r--r-- | Ryujinx.HLE/AssemblyInfo.cs | 3 | ||||
-rw-r--r-- | Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs | 42 | ||||
-rw-r--r-- | Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs | 71 |
3 files changed, 112 insertions, 4 deletions
diff --git a/Ryujinx.HLE/AssemblyInfo.cs b/Ryujinx.HLE/AssemblyInfo.cs new file mode 100644 index 00000000..9d7bad6b --- /dev/null +++ b/Ryujinx.HLE/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Ryujinx.Tests")]
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 3cfd192c..e287318a 100644 --- a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -204,12 +204,11 @@ namespace Ryujinx.HLE.HOS.Applets else { // Call the configured GUI handler to get user's input. - var args = new SoftwareKeyboardUiArgs { - HeaderText = _keyboardForegroundConfig.HeaderText, - SubtitleText = _keyboardForegroundConfig.SubtitleText, - GuideText = _keyboardForegroundConfig.GuideText, + HeaderText = StripUnicodeControlCodes(_keyboardForegroundConfig.HeaderText), + SubtitleText = StripUnicodeControlCodes(_keyboardForegroundConfig.SubtitleText), + GuideText = StripUnicodeControlCodes(_keyboardForegroundConfig.GuideText), SubmitText = (!string.IsNullOrWhiteSpace(_keyboardForegroundConfig.SubmitText) ? _keyboardForegroundConfig.SubmitText : "OK"), StringLengthMin = _keyboardForegroundConfig.StringLengthMin, @@ -764,6 +763,41 @@ namespace Ryujinx.HLE.HOS.Applets } } + /// <summary> + /// Removes all Unicode control code characters from the input string. + /// This includes CR/LF, tabs, null characters, escape characters, + /// and special control codes which are used for formatting by the real keyboard applet. + /// </summary> + /// <remarks> + /// Some games send special control codes (such as 0x13 "Device Control 3") as part of the string. + /// Future implementations of the emulated keyboard applet will need to handle these as well. + /// </remarks> + /// <param name="input">The input string to sanitize (may be null).</param> + /// <returns>The sanitized string.</returns> + internal static string StripUnicodeControlCodes(string input) + { + if (input is null) + { + return null; + } + + if (input.Length == 0) + { + return string.Empty; + } + + StringBuilder sb = new StringBuilder(capacity: input.Length); + foreach (char c in input) + { + if (!char.IsControl(c)) + { + sb.Append(c); + } + } + + return sb.ToString(); + } + private static T ReadStruct<T>(byte[] data) where T : struct { diff --git a/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs b/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs new file mode 100644 index 00000000..d16039ad --- /dev/null +++ b/Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs @@ -0,0 +1,71 @@ +using NUnit.Framework; +using Ryujinx.HLE.HOS.Applets; +using System.Text; + +namespace Ryujinx.Tests.HLE +{ + public class SoftwareKeyboardTests + { + [Test] + public void StripUnicodeControlCodes_NullInput() + { + Assert.IsNull(SoftwareKeyboardApplet.StripUnicodeControlCodes(null)); + } + + [Test] + public void StripUnicodeControlCodes_EmptyInput() + { + Assert.AreEqual(string.Empty, SoftwareKeyboardApplet.StripUnicodeControlCodes(string.Empty)); + } + + [Test] + public void StripUnicodeControlCodes_Passthrough() + { + string[] prompts = new string[] + { + "Please name him.", + "Name her, too.", + "Name your friend.", + "Name another friend.", + "Name your pet.", + "Favorite homemade food?", + "What’s your favorite thing?", + "Are you sure?", + }; + + foreach (string prompt in prompts) + { + Assert.AreEqual(prompt, SoftwareKeyboardApplet.StripUnicodeControlCodes(prompt)); + } + } + + [Test] + public void StripUnicodeControlCodes_StripsNewlines() + { + Assert.AreEqual("I am very tall", SoftwareKeyboardApplet.StripUnicodeControlCodes("I \r\nam \r\nvery \r\ntall")); + } + + [Test] + public void StripUnicodeControlCodes_StripsDeviceControls() + { + // 0x13 is control code DC3 used by some games + string specialInput = Encoding.UTF8.GetString(new byte[] { 0x13, 0x53, 0x68, 0x69, 0x6E, 0x65, 0x13 }); + Assert.AreEqual("Shine", SoftwareKeyboardApplet.StripUnicodeControlCodes(specialInput)); + } + + [Test] + public void StripUnicodeControlCodes_StripsToEmptyString() + { + string specialInput = Encoding.UTF8.GetString(new byte[] { 17, 18, 19, 20 }); // DC1 - DC4 special codes + Assert.AreEqual(string.Empty, SoftwareKeyboardApplet.StripUnicodeControlCodes(specialInput)); + } + + [Test] + public void StripUnicodeControlCodes_PreservesMultiCodePoints() + { + // Turtles are a good example of multi-codepoint Unicode chars + string specialInput = "♀ 🐢 🐢 ♂ "; + Assert.AreEqual(specialInput, SoftwareKeyboardApplet.StripUnicodeControlCodes(specialInput)); + } + } +} |