From aee754ab7d50f496661c98b77a5eb74bf5bff366 Mon Sep 17 00:00:00 2001 From: tontyGH <39193182+tontyGH@users.noreply.github.com> Date: Fri, 19 Jun 2026 15:31:37 -0400 Subject: [PATCH 1/2] yeah --- .../Procs/Native/DreamProcNativeHelpers.cs | 57 +++++++++ .../Procs/Native/DreamProcNativeRoot.cs | 108 ++---------------- 2 files changed, 67 insertions(+), 98 deletions(-) diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs index f599f2b3e4..52de862f94 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs @@ -316,6 +316,63 @@ public static DreamValue HandleOviewersOhearers(NativeProc.Bundle bundle, DreamO return new DreamValue(view); } + public static DreamValue HandleReplaceText(DreamValue haystackValue, DreamValue needleValue, DreamValue replacementValue, int start, int end, bool exact) { + // TODO: _char support if/when we support char + + if(needleValue.TryGetValueAsDreamObject(out var regexObject)) { + // Equivalent to regex.Replace according to spec + return DreamProcNativeRegex.RegexReplace(regexObject, haystackValue, replacementValue, start, end); + } + + if(!haystackValue.TryGetValueAsString(out var haystack)) + return DreamValue.Null; + + if(start == 0) + return new(haystack); + + if(start < 0)// Negative wrap-around + start = Math.Max(start + haystack.Length + 1, 1); + if(end <= 0)// Zero or negative wrap-around + end = Math.Max(end + haystack.Length + 1, start); + + var replacement = replacementValue.Stringify(); + + needleValue.TryGetValueAsString(out var needle); + if(needle is null || needle.Length == 0) { + if(replacement.Length == 0) { + return new DreamValue(haystack); + } + + if(start == 1) { + start = 2; + } + + end = Math.Min(end, haystack.Length); + + StringBuilder result = new(); + for(int i = 0; i < haystack.Length; i++) { + result.Append(haystack[i]); + if (i >= start - 2 && i < end - 1) + result.Append(replacement); + } + + return new DreamValue(result.ToString()); + } + + // TODO: replacetext automatically upper/lowercases matches, but how? To what extent? + string before = haystack[..(start - 1)]; + string after = haystack[(end - 1)..]; + string textSub = haystack.Substring(start - 1, end - start); + string replaced = textSub.Replace(needle, replacement, exact ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + + StringBuilder newTextBuilder = new(); + newTextBuilder.Append(before); + newTextBuilder.Append(replaced); + newTextBuilder.Append(after); + + return new DreamValue(newTextBuilder.ToString()); + } + /// /// Determines whether the first parameter is "visible" to the second parameter, according to BYOND's various rules on visibility. /// diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs index 5cbcf2c020..cb26d87a3e 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs @@ -2063,68 +2063,11 @@ public static DreamValue NativeProc_regex(NativeProc.Bundle bundle, DreamObject? public static DreamValue NativeProc_replacetext(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) { DreamValue haystack = bundle.GetArgument(0, "Haystack"); DreamValue needle = bundle.GetArgument(1, "Needle"); - DreamValue replacementArg = bundle.GetArgument(2, "Replacement"); - bundle.GetArgument(3, "Start").TryGetValueAsInteger(out var start); //1-indexed - int end = bundle.GetArgument(4, "End").GetValueAsInteger(); //1-indexed + DreamValue replacement = bundle.GetArgument(2, "Replacement"); + bundle.GetArgument(3, "Start").TryGetValueAsInteger(out var start); + bundle.GetArgument(4, "End").TryGetValueAsInteger(out var end); - if (needle.TryGetValueAsDreamObject(out var regexObject)) { - // According to the docs, this is the same as /regex.Replace() - return DreamProcNativeRegex.RegexReplace(regexObject, haystack, replacementArg, start, end); - } - - if (!haystack.TryGetValueAsString(out var text)) { - return DreamValue.Null; - } - - if (start == 0) { // Return unmodified if Start is 0 - return new(text); - } else if (start < 0) { // Negative wrap-around - start = Math.Max(start + text.Length + 1, 1); - } - - var arg3 = replacementArg.TryGetValueAsString(out var replacement); - - if (end <= 0) { // Zero or negative wrap-around - end = Math.Max(end + text.Length + 1, start); - } - - if (needle.IsNull) { // Insert the replacement after each char except the last - if (!arg3) { // No change if no Replacement was given - return new DreamValue(text); - } - - // A Start of 2 is the same as 1. This only happens when Needle is null. - if (start == 1) - start = 2; - - // End cannot reach the last char - end = Math.Min(end, text.Length); - - StringBuilder result = new StringBuilder(); - for (int i = 0; i < text.Length; i++) { - result.Append(text[i]); - if (i >= start - 2 && i < end - 1) - result.Append(replacement); - } - - return new DreamValue(result.ToString()); - } - - if (needle.TryGetValueAsString(out var needleStr)) { - string before = text.Substring(0, start - 1); - string after = text.Substring(end - 1); - string textSub = text.Substring(start - 1, end - start); - string replaced = textSub.Replace(needleStr, replacement, StringComparison.OrdinalIgnoreCase); - - StringBuilder newTextBuilder = new(); - newTextBuilder.Append(before); - newTextBuilder.Append(replaced); - newTextBuilder.Append(after); - - return new DreamValue(newTextBuilder.ToString()); - } - - throw new Exception($"Invalid needle {needle}"); + return DreamProcNativeHelpers.HandleReplaceText(haystack, needle, replacement, start, end, false); } [DreamProc("replacetext_char")] @@ -2145,44 +2088,13 @@ public static DreamValue NativeProc_replacetext_char(NativeProc.Bundle bundle, D [DreamProcParameter("Start", Type = DreamValueTypeFlag.Float, DefaultValue = 1)] [DreamProcParameter("End", Type = DreamValueTypeFlag.Float, DefaultValue = 0)] public static DreamValue NativeProc_replacetextEx(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) { - if (!bundle.GetArgument(0, "Haystack").TryGetValueAsString(out var text)) { - return DreamValue.Null; - } - - var arg3 = bundle.GetArgument(2, "Replacement").TryGetValueAsString(out var replacement); - - if (!bundle.GetArgument(1, "Needle").TryGetValueAsString(out var needle)) { - if (!arg3) { - return new DreamValue(text); - } - - //Insert the replacement after each char except the last char - //TODO: Properly support non-default start/end values - StringBuilder result = new StringBuilder(); - var pos = 0; - while (pos + 1 <= text.Length) { - result.Append(text[pos]).Append(arg3); - pos += 1; - } - - result.Append(text[pos]); - return new DreamValue(result.ToString()); - } - - int start = bundle.GetArgument(3, "Start").GetValueAsInteger(); //1-indexed - int end = bundle.GetArgument(4, "End").GetValueAsInteger(); //1-indexed - - if (start == 0) { // Return unmodified - return new(text); - } else if (start < 0) { // Negative wrap-around - start = Math.Max(start + text.Length + 1, 1); - } - - if (end <= 0) { // Zero and negative wrap-around - end = Math.Max(end + text.Length + 1, start); - } + DreamValue haystack = bundle.GetArgument(0, "Haystack"); + DreamValue needle = bundle.GetArgument(1, "Needle"); + DreamValue replacement = bundle.GetArgument(2, "Replacement"); + bundle.GetArgument(3, "Start").TryGetValueAsInteger(out var start); + bundle.GetArgument(4, "End").TryGetValueAsInteger(out var end); - return new DreamValue(text.Substring(start - 1, end - start).Replace(needle, replacement, StringComparison.Ordinal)); + return DreamProcNativeHelpers.HandleReplaceText(haystack, needle, replacement, start, end, true); } [DreamProc("replacetextEx_char")] From b9b919c7e5f231c23049696716c076bb80c26408 Mon Sep 17 00:00:00 2001 From: tontyGH <39193182+tontyGH@users.noreply.github.com> Date: Fri, 19 Jun 2026 18:07:09 -0400 Subject: [PATCH 2/2] Extremely useless helper but okay --- OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs index 52de862f94..36e8a33968 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeHelpers.cs @@ -338,7 +338,7 @@ public static DreamValue HandleReplaceText(DreamValue haystackValue, DreamValue var replacement = replacementValue.Stringify(); needleValue.TryGetValueAsString(out var needle); - if(needle is null || needle.Length == 0) { + if(string.IsNullOrEmpty(needle)) { if(replacement.Length == 0) { return new DreamValue(haystack); }