aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLogan Stromberg <loganstromberg@gmail.com>2022-11-16 14:53:17 -0800
committerGitHub <noreply@github.com>2022-11-16 23:53:17 +0100
commit2c9ab5e45fd45d45909b9b348580c44bdfc4d36f (patch)
tree204dec18131dcdf05837999600ad1a32435a566c
parentd536cc8ae6d6725780365d858f2fd64b66d90b7f (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.cs3
-rw-r--r--Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs42
-rw-r--r--Ryujinx.Tests/HLE/SoftwareKeyboardTests.cs71
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));
+ }
+ }
+}