diff --git a/UndertaleModLib/Decompiler/Assembler.cs b/UndertaleModLib/Decompiler/Assembler.cs index 5179f5af6..d936ca1f6 100644 --- a/UndertaleModLib/Decompiler/Assembler.cs +++ b/UndertaleModLib/Decompiler/Assembler.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -41,6 +42,9 @@ public static class Assembler { "pushref", -11 } }; + private static readonly Regex callInstrRegex = new(@"^(.*)\(argc=(.*)\)$", RegexOptions.Compiled); + private static readonly Regex codeEntryRegex = new(@"^\(locals=(.*)\,\s*argc=(.*)\)$", RegexOptions.Compiled); + // TODO: Improve the error messages public static UndertaleInstruction AssembleOne(string source, IList funcs, IList vars, IList strg, Dictionary localvars = null, UndertaleData data = null) @@ -205,7 +209,7 @@ public static UndertaleInstruction AssembleOne(string source, IList Assemble(string source, IList funcs, IList vars, IList strg, UndertaleData data = null) { - var lines = source.Replace("\r", "", StringComparison.InvariantCulture).Split('\n'); + StringReader strReader = new(source); uint addr = 0; Dictionary labels = new Dictionary(); Dictionary labelTargets = new Dictionary(); List instructions = new List(); Dictionary localvars = new Dictionary(); - foreach (var fullline in lines) + string fullLine; + while ((fullLine = strReader.ReadLine()) is not null) { - string line = fullline; + string line = fullLine; if (line.Length == 0) continue; @@ -279,7 +284,7 @@ public static List Assemble(string source, IList(); GlobalDecompileContext globalDecompileContext = new GlobalDecompileContext(data, false); + // TODO: Is this necessary? + // Doesn't the latter `Parallel.ForEach()` already cover this? foreach (var func in data.Functions) { if (func.Name.Content.StartsWith("gml_Script_")) { - var funcName = func.Name.Content.Substring("gml_Script_".Length); + var funcName = func.Name.Content[("gml_Script_".Length)..]; data.KnownSubFunctions.TryAdd(funcName, func); } } @@ -4024,7 +4026,7 @@ public static void BuildSubFunctionCache(UndertaleData data) { lock (data.KnownSubFunctions) { - data.KnownSubFunctions.Add(assign.Destination.Var.Name.Content, funcDef.Function); + data.KnownSubFunctions.TryAdd(assign.Destination.Var.Name.Content, funcDef.Function); } } } diff --git a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs index 5091fe659..22bf5e8e0 100644 --- a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs @@ -557,6 +557,8 @@ private void UpdateGettext(UndertaleCode gettextCode) } public static Dictionary gettextJSON = null; + private static readonly Regex gettextRegex = new("scr_gettext\\(\\\"(.*?)\\\"\\)", RegexOptions.Compiled); + private static readonly Regex getlangRegex = new("scr_84_get_lang_string(?:.*?)\\(\\\"(.*?)\\\"\\)", RegexOptions.Compiled); private string UpdateGettextJSON(string json) { try @@ -677,42 +679,57 @@ private async Task DecompileCode(UndertaleCode code, bool first, LoaderDialog ex mainWindow.ShowError(exc.ToString()); } - if (decompiled != null) + // Add `// string` at the end of lines with `scr_gettext()` or `scr_84_get_lang_string()` + if (decompiled is not null) { - string[] decompiledLines; - if (gettext != null && decompiled.Contains("scr_gettext")) + StringReader decompLinesReader; + StringBuilder decompLinesBuilder; + Dictionary currDict = null; + Regex currRegex = null; + if (gettext is not null && decompiled.Contains("scr_gettext")) { - decompiledLines = decompiled.Split('\n'); - for (int i = 0; i < decompiledLines.Length; i++) - { - var matches = Regex.Matches(decompiledLines[i], "scr_gettext\\(\\\"(\\w*)\\\"\\)"); - foreach (Match match in matches) - { - if (match.Success) - { - if (gettext.TryGetValue(match.Groups[1].Value, out string text)) - decompiledLines[i] += $" // {text}"; - } - } - } - decompiled = string.Join('\n', decompiledLines); + currDict = gettext; + currRegex = gettextRegex; + } + else if (gettextJSON is not null && decompiled.Contains("scr_84_get_lang_string")) + { + currDict = gettextJSON; + currRegex = getlangRegex; } - else if (gettextJSON != null && decompiled.Contains("scr_84_get_lang_string")) + + if (currDict is not null && currRegex is not null) { - decompiledLines = decompiled.Split('\n'); - for (int i = 0; i < decompiledLines.Length; i++) + decompLinesReader = new(decompiled); + decompLinesBuilder = new(); + string line; + while ((line = decompLinesReader.ReadLine()) is not null) { - var matches = Regex.Matches(decompiledLines[i], "scr_84_get_lang_string(\\w*)\\(\\\"(\\w*)\\\"\\)"); - foreach (Match match in matches) + // Not `currRegex.Match()`, because one line could contain several calls + // if the "Profile mode" is enabled. + var matches = currRegex.Matches(line).Where(m => m.Success).ToArray(); + if (matches.Length > 0) { - if (match.Success) + decompLinesBuilder.Append($"{line} // "); + + for (int i = 0; i < matches.Length; i++) { - if (gettextJSON.TryGetValue(match.Groups[^1].Value, out string text)) - decompiledLines[i] += $" // {text}"; + Match match = matches[i]; + if (!currDict.TryGetValue(match.Groups[1].Value, out string text)) + continue; + + if (i != matches.Length - 1) // If not the last + decompLinesBuilder.Append($"{text}; "); + else + decompLinesBuilder.Append(text + '\n'); } } + else + { + decompLinesBuilder.Append(line + '\n'); + } } - decompiled = string.Join('\n', decompiledLines); + + decompiled = decompLinesBuilder.ToString(); } }