Kaynağa Gözat

Prevent raw Unicode control codes from showing on software keyboard applet. (#3845)

* 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>
Logan Stromberg 3 yıl önce
ebeveyn
işleme
2c9ab5e45f

+ 3 - 0
Ryujinx.HLE/AssemblyInfo.cs

@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Ryujinx.Tests")]

+ 38 - 4
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
         {

+ 71 - 0
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));
+        }
+    }
+}