diff --git a/DMCompiler/Bytecode/StringFormatEncoder.cs b/DMCompiler/Bytecode/StringFormatEncoder.cs index 3b096e1c97..2e859617f9 100644 --- a/DMCompiler/Bytecode/StringFormatEncoder.cs +++ b/DMCompiler/Bytecode/StringFormatEncoder.cs @@ -90,26 +90,10 @@ public static bool Decode(char c, [NotNullWhen(true)] out FormatSuffix? suffix) return true; } - public static bool Decode(char c) { - ushort bytes = c; - return (bytes & FormatPrefix) == FormatPrefix; // Could also check that the lower byte is a valid enum but... ehhhhh - } - /// true if argument is a marker for an interpolated value, one of them [] things. false if not. public static bool IsInterpolation(FormatSuffix suffix) { //This logic requires that all the interpolated-value enums keep separated from the others. //I'd write some type-engine code to catch a discrepancy in that but alas, this language is just not OOPy enough. return suffix <= FormatSuffix.ReferenceOfValue; } - - /// A new version of the string, with all formatting characters removed. - public static string RemoveFormatting(string input) { - StringBuilder ret = new StringBuilder(input.Length); // Trying to keep it to one malloc here - foreach(char c in input) { - if(!Decode(c)) - ret.Append(c); - } - - return ret.ToString(); - } } diff --git a/OpenDreamClient/Input/ContextMenu/ContextMenuPopup.xaml.cs b/OpenDreamClient/Input/ContextMenu/ContextMenuPopup.xaml.cs index 3e90159083..2effcc7461 100644 --- a/OpenDreamClient/Input/ContextMenu/ContextMenuPopup.xaml.cs +++ b/OpenDreamClient/Input/ContextMenu/ContextMenuPopup.xaml.cs @@ -62,7 +62,7 @@ public void RepopulateEntities(ScalingViewport viewport, Vector2 relativePos, Sc continue; var reference = new ClientObjectReference(_entityManager.GetNetEntity(uid)); - var name = _appearanceSystem.GetName(reference); + var name = _appearanceSystem.GetNameUnformatted(reference); ContextMenu.AddChild(new ContextMenuItem(this, reference, name, sprite.Icon)); } @@ -74,7 +74,7 @@ public void RepopulateEntities(ScalingViewport viewport, Vector2 relativePos, Sc if (_spriteQuery.TryGetComponent(uid, out var sprite) && sprite.Icon.Appearance?.MouseOpacity != MouseOpacity.Transparent) { var reference = new ClientObjectReference(_entityManager.GetNetEntity(uid)); - var name = _appearanceSystem.GetName(reference); + var name = _appearanceSystem.GetNameUnformatted(reference); ContextMenu.AddChild(new ContextMenuItem(this, reference, name, sprite.Icon)); } } @@ -82,7 +82,7 @@ public void RepopulateEntities(ScalingViewport viewport, Vector2 relativePos, Sc // Append the turf to the end of the context menu var turfUnderMouse = _mouseInputSystem.GetTurfUnderMouse(mapCoords, out var turfId)?.Atom; if (turfUnderMouse is not null && turfId is not null) { - var name = _appearanceSystem.GetName(turfUnderMouse.Value); + var name = _appearanceSystem.GetNameUnformatted(turfUnderMouse.Value); var icon = _appearanceSystem.GetTurfIcon(turfId.Value); ContextMenu.AddChild(new ContextMenuItem(this, turfUnderMouse.Value, name, icon)); diff --git a/OpenDreamClient/Interface/Controls/ControlMap.cs b/OpenDreamClient/Interface/Controls/ControlMap.cs index 018a45a194..21b7ae429f 100644 --- a/OpenDreamClient/Interface/Controls/ControlMap.cs +++ b/OpenDreamClient/Interface/Controls/ControlMap.cs @@ -140,7 +140,7 @@ public override bool TryGetProperty(string property, [NotNullWhen(true)] out IDM private void UpdateAtomUnderMouse(ClientObjectReference? atom, Vector2 relativePos, Vector2i iconPos) { if (!_atomUnderMouse.Equals(atom)) { _entitySystemManager.Resolve(ref _appearanceSystem); - var name = (atom != null) ? _appearanceSystem.GetName(atom.Value) : string.Empty; + var name = (atom != null) ? _appearanceSystem.GetNameUnformatted(atom.Value) : string.Empty; Window?.SetStatus(name); if (_atomUnderMouse != null) diff --git a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs index d3913ad484..ee59b5b115 100644 --- a/OpenDreamClient/Rendering/ClientAppearanceSystem.cs +++ b/OpenDreamClient/Rendering/ClientAppearanceSystem.cs @@ -350,7 +350,7 @@ public bool TryGetAppearance(ClientObjectReference reference, [NotNullWhen(true) return false; } - public string GetName(ClientObjectReference reference) { + public string GetNameUnformatted(ClientObjectReference reference) { switch (reference.Type) { case ClientObjectReference.RefType.Client: return _playerManager.LocalSession?.Name ?? ""; @@ -359,7 +359,7 @@ public string GetName(ClientObjectReference reference) { if (!TryGetAppearance(reference, out var appearance)) break; - return appearance.Name; + return StringFormatDecoder.RemoveFormatting(appearance.Name); } return ""; diff --git a/OpenDreamClient/Rendering/MapTextRenderer.cs b/OpenDreamClient/Rendering/MapTextRenderer.cs index d1b4aa6fd0..59badd82bb 100644 --- a/OpenDreamClient/Rendering/MapTextRenderer.cs +++ b/OpenDreamClient/Rendering/MapTextRenderer.cs @@ -1,6 +1,7 @@ using System.Diagnostics.Contracts; using System.Text; using OpenDreamClient.Interface.Html; +using OpenDreamShared.Dream; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; using Robust.Client.UserInterface.RichText; @@ -26,7 +27,7 @@ public void RenderToTarget(DrawingHandleWorld handle, IRenderTexture texture, st handle.SetTransform(DreamViewOverlay.CreateRenderTargetFlipMatrix(texture.Size, Vector2.Zero)); var message = new FormattedMessage(); - HtmlParser.Parse(maptext, message); + HtmlParser.Parse(StringFormatDecoder.RemoveFormatting(maptext), message); var (height, lineBreaks) = ProcessWordWrap(message, texture.Size.X); var lineHeight = _defaultFont.GetLineHeight(Scale); diff --git a/OpenDreamRuntime/DreamConnection.cs b/OpenDreamRuntime/DreamConnection.cs index 94ea63c49a..6b1e7c7638 100644 --- a/OpenDreamRuntime/DreamConnection.cs +++ b/OpenDreamRuntime/DreamConnection.cs @@ -284,7 +284,7 @@ public void OutputDreamValue(DreamValue value) { // Prune any remaining formatting var message = value.Stringify(); - message = StringFormatEncoder.RemoveFormatting(message); + message = StringFormatDecoder.RemoveFormatting(message); OutputControl(message, null); } diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 8d339c9cc8..f76e6c6faa 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -7,6 +7,7 @@ using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Rendering; using OpenDreamRuntime.Resources; +using OpenDreamShared.Dream; using Robust.Server.GameObjects; using Robust.Server.GameStates; using Robust.Shared.Map; @@ -371,7 +372,7 @@ public virtual string GetDisplayName(StringFormatEncoder.FormatSuffix? suffix = var name = GetRawName(); bool isProper = StringIsProper(name); - name = StringFormatEncoder.RemoveFormatting(name); // TODO: Care about other formatting macros for obj names beyond \proper & \improper + name = StringFormatDecoder.RemoveFormatting(name); // TODO: Care about other formatting macros for obj names beyond \proper & \improper if(!isProper) { return name; } @@ -390,7 +391,7 @@ public virtual string GetDisplayName(StringFormatEncoder.FormatSuffix? suffix = /// Similar to except it just returns the name as plaintext, with formatting removed. No article or anything. /// public string GetNameUnformatted() { - return StringFormatEncoder.RemoveFormatting(GetRawName()); + return StringFormatDecoder.RemoveFormatting(GetRawName()); } /// diff --git a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs index 825115ed8d..5eaa479384 100644 --- a/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs +++ b/OpenDreamRuntime/Procs/DMOpcodeHandlers.cs @@ -444,7 +444,7 @@ public static ProcStatus FormatString(DMProcState state) { if (interps[nextInterpIndex].TryGetValueAsDreamObject(out var dreamObject)) { formattedString.Append(dreamObject.GetNameUnformatted()); } else if (interps[nextInterpIndex].TryGetValueAsString(out var interpStr)) { - formattedString.Append(StringFormatEncoder.RemoveFormatting(interpStr)); + formattedString.Append(StringFormatDecoder.RemoveFormatting(interpStr)); } // NOTE probably should put this above the TryGetAsDreamObject function and continue if formatting has occured diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs index 7e9e71c385..a1d823f5cb 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs @@ -1233,7 +1233,7 @@ private static void JsonEncode(Utf8JsonWriter writer, DreamValue value) { writer.WriteEndObject(); } } else if (value.TryGetValueAsString(out var text)) - writer.WriteStringValue(text); + writer.WriteStringValue(StringFormatDecoder.RemoveFormatting(text)); else if (value.TryGetValueAsType(out var type)) writer.WriteStringValue(type.Path); else if (value.TryGetValueAsProc(out var proc)) diff --git a/OpenDreamRuntime/Resources/ConsoleOutputResource.cs b/OpenDreamRuntime/Resources/ConsoleOutputResource.cs index d1b5a2dad6..d41f3eafbf 100644 --- a/OpenDreamRuntime/Resources/ConsoleOutputResource.cs +++ b/OpenDreamRuntime/Resources/ConsoleOutputResource.cs @@ -1,5 +1,5 @@ -using DMCompiler.Bytecode; using OpenDreamRuntime.Procs.DebugAdapter; +using OpenDreamShared.Dream; namespace OpenDreamRuntime.Resources; @@ -22,7 +22,7 @@ public void WriteConsole(LogLevel logLevel, string sawmill, string message) { public override void Output(DreamValue value) { // Prune any remaining formatting var message = value.Stringify(); - message = StringFormatEncoder.RemoveFormatting(message); + message = StringFormatDecoder.RemoveFormatting(message); WriteConsole(LogLevel.Info, "world.log", message); } diff --git a/OpenDreamRuntime/Resources/DreamResource.cs b/OpenDreamRuntime/Resources/DreamResource.cs index 72c3db81c0..654b434555 100644 --- a/OpenDreamRuntime/Resources/DreamResource.cs +++ b/OpenDreamRuntime/Resources/DreamResource.cs @@ -1,7 +1,8 @@ using System.IO; -using DMCompiler.Bytecode; +// ReSharper disable once RedundantUsingDirective using System.Runtime.CompilerServices; using System.Text; +using OpenDreamShared.Dream; namespace OpenDreamRuntime.Resources; @@ -80,7 +81,7 @@ public virtual void Output(DreamValue value) { } // Prune any remaining formatting - text = StringFormatEncoder.RemoveFormatting(text); + text = StringFormatDecoder.RemoveFormatting(text); CreateDirectory(); File.AppendAllText(ResourcePath, text + "\r\n"); diff --git a/OpenDreamShared/Dream/StringFormatDecoder.cs b/OpenDreamShared/Dream/StringFormatDecoder.cs new file mode 100644 index 0000000000..22e7fbbb09 --- /dev/null +++ b/OpenDreamShared/Dream/StringFormatDecoder.cs @@ -0,0 +1,25 @@ +using System.Text; + +namespace OpenDreamShared.Dream; + +public static class StringFormatDecoder { + /// + /// Refer to DMCompiler.Bytecode.StringFormatEncoder for documentation (can't cref cross-project) + /// + private static readonly ushort FormatPrefix = 0xFF00; + + private static readonly StringBuilder UnformattedStringBuilder = new(); + + /// A new version of the string, with all formatting characters removed. + public static string RemoveFormatting(string input) { + UnformattedStringBuilder.Clear(); + UnformattedStringBuilder.EnsureCapacity(input.Length); // Trying to keep it to one malloc here + foreach(char c in input) { + ushort bytes = c; + if((bytes & FormatPrefix) != FormatPrefix) + UnformattedStringBuilder.Append(c); + } + + return UnformattedStringBuilder.ToString(); + } +}