From 71d1ffa35fdafe893d47ca10ef24eabbb0b9f1ce Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 24 Dec 2023 01:18:50 +0300 Subject: [PATCH 01/14] Make code search results line numbers clickable. --- UndertaleModLib/Scripting/IScriptInterface.cs | 26 +----- .../Editors/UndertaleCodeEditor.xaml.cs | 58 +++++++++++-- UndertaleModTool/MainWindow.xaml.cs | 40 +++++---- .../Scripts/Builtin Scripts/Search.csx | 54 ++++++------ .../Scripts/Builtin Scripts/SearchASM.csx | 36 ++++---- .../Scripts/Builtin Scripts/SearchLimited.csx | 35 ++++---- UndertaleModTool/Tab.cs | 4 +- .../Windows/ClickableTextOutput.xaml.cs | 87 ++++++++++++++----- 8 files changed, 207 insertions(+), 133 deletions(-) diff --git a/UndertaleModLib/Scripting/IScriptInterface.cs b/UndertaleModLib/Scripting/IScriptInterface.cs index e896e4373..f21cd8325 100644 --- a/UndertaleModLib/Scripting/IScriptInterface.cs +++ b/UndertaleModLib/Scripting/IScriptInterface.cs @@ -293,20 +293,11 @@ bool AreFilesIdentical(string file1, string file2) /// The query that was searched for. /// How many results have been found. /// An of type , - /// with TKey being the name of the code entry an TValue being a list of matching code lines with their line number prepended. + /// with TKey being the name of the code entry an TValue being a list of tuples where the first item is the matching code line number and the second one is the code line itself. /// Whether to open the "Decompiled" view or the "Disassembly" view when clicking on an entry name. /// A list of code entries that encountered an error while searching. /// A task that represents the search output. - Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null); - - /// - /// Obsolete. - /// - [Obsolete("Use ClickableSearchOutput instead!")] - sealed Task ClickableTextOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null) - { - return ClickableSearchOutput(title, query, resultsCount, resultsDict, showInDecompiledView, failedList); - } + Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null); /// /// Shows search output with clickable text to the user. @@ -315,20 +306,11 @@ sealed Task ClickableTextOutput(string title, string query, int resultsCount, IO /// The query that was searched for. /// How many results have been found. /// A with TKey being the name of the code entry and - /// TValue being a list of matching code lines with their line number prepended. + /// TValue being a list of tuples where the first item is the matching code line number and the second one is the code line itself. /// Whether to open the "Decompiled" view or the "Disassembly" view when clicking on an entry name. /// A list of code entries that encountered an error while searching. /// A task that represents the search output. - Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null); - - /// - /// Obsolete. - /// - [Obsolete("Use ClickableSearchOutput instead!")] - sealed Task ClickableTextOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null) - { - return ClickableSearchOutput(title, query, resultsCount, resultsDict, showInDecompiledView, failedList); - } + Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null); /// /// Sets . diff --git a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs index 5091fe659..e821c225c 100644 --- a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs @@ -34,7 +34,6 @@ using UndertaleModLib.Compiler; using UndertaleModLib.Decompiler; using UndertaleModLib.Models; -using static UndertaleModTool.MainWindow.CodeEditorMode; using Input = System.Windows.Input; namespace UndertaleModTool @@ -59,14 +58,14 @@ public partial class UndertaleCodeEditor : DataUserControl public bool DecompiledYet = false; public bool DecompiledSkipped = false; public SearchPanel DecompiledSearchPanel; - public static (int Line, int Column, double ScrollPos) OverriddenDecompPos; + public static (int Line, int Column, double ScrollPos) OverriddenDecompPos { get; set; } public bool DisassemblyFocused = false; public bool DisassemblyChanged = false; public bool DisassembledYet = false; public bool DisassemblySkipped = false; public SearchPanel DisassemblySearchPanel; - public static (int Line, int Column, double ScrollPos) OverriddenDisasmPos; + public static (int Line, int Column, double ScrollPos) OverriddenDisasmPos { get; set; } public static RoutedUICommand Compile = new RoutedUICommand("Compile code", "Compile", typeof(UndertaleCodeEditor)); @@ -75,6 +74,14 @@ public partial class UndertaleCodeEditor : DataUserControl private static readonly Dictionary FunctionsDict = new(); private static readonly Dictionary CodeDict = new(); + public enum CodeEditorTab + { + Unstated, + Disassembly, + Decompiled + } + public static CodeEditorTab EditorTab { get; set; } = CodeEditorTab.Unstated; + public UndertaleCodeEditor() { InitializeComponent(); @@ -298,9 +305,9 @@ private async void UserControl_DataContextChanged(object sender, DependencyPrope CurrentDecompiled = null; CurrentDisassembled = null; - if (MainWindow.CodeEditorDecompile != Unstated) //if opened from the code search results "link" + if (EditorTab != CodeEditorTab.Unstated) //if opened from the code search results "link" { - if (MainWindow.CodeEditorDecompile == DontDecompile && code != CurrentDisassembled) + if (EditorTab == CodeEditorTab.Disassembly && code != CurrentDisassembled) { if (CodeModeTabs.SelectedItem != DisassemblyTab) CodeModeTabs.SelectedItem = DisassemblyTab; @@ -308,7 +315,7 @@ private async void UserControl_DataContextChanged(object sender, DependencyPrope DisassembleCode(code, true); } - if (MainWindow.CodeEditorDecompile == Decompile && code != CurrentDecompiled) + if (EditorTab == CodeEditorTab.Decompiled && code != CurrentDecompiled) { if (CodeModeTabs.SelectedItem != DecompiledTab) CodeModeTabs.SelectedItem = DecompiledTab; @@ -316,7 +323,7 @@ private async void UserControl_DataContextChanged(object sender, DependencyPrope _ = DecompileCode(code, true); } - MainWindow.CodeEditorDecompile = Unstated; + EditorTab = CodeEditorTab.Unstated; } else FillInCodeViewer(true); @@ -367,6 +374,9 @@ private static void RestoreCaretPosition(TextEditor textEditor, int linePos, int { if (linePos <= textEditor.LineCount) { + if (linePos == -1) + linePos = textEditor.Document.LineCount; + int lineLen = textEditor.Document.GetLineByNumber(linePos).Length; textEditor.TextArea.Caret.Line = linePos; if (columnPos != -1) @@ -375,7 +385,8 @@ private static void RestoreCaretPosition(TextEditor textEditor, int linePos, int textEditor.TextArea.Caret.Column = lineLen + 1; textEditor.ScrollToLine(linePos); - textEditor.ScrollToVerticalOffset(scrollPos); + if (scrollPos != -1) + textEditor.ScrollToVerticalOffset(scrollPos); } else { @@ -383,7 +394,36 @@ private static void RestoreCaretPosition(TextEditor textEditor, int linePos, int textEditor.ScrollToEnd(); } } - + public static void ChangeLineNumber(int lineNum, CodeEditorTab editorTab) + { + if (lineNum < 1) + return; + + if (editorTab == CodeEditorTab.Unstated) + { + Debug.WriteLine("The \"editorTab\" argument of \"GoToLine()\" is \"Unstated\"."); + return; + } + + if (editorTab == CodeEditorTab.Decompiled) + OverriddenDecompPos = (lineNum, -1, -1); + else + OverriddenDisasmPos = (lineNum, -1, -1); + } + public static void ChangeLineNumber(int lineNum, TextEditor textEditor) + { + if (lineNum < 1) + return; + + if (textEditor is null) + { + Debug.WriteLine("The \"textEditor\" argument of \"GoToLine()\" is null."); + return; + } + + RestoreCaretPosition(textEditor, lineNum, -1, -1); + } + private static void FillObjectDicts() { var data = mainWindow.Data; diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index 37d803042..65247c39f 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -116,12 +116,7 @@ public object Selected public string ExePath { get; private set; } = Program.GetExecutableDirectory(); public string ScriptErrorType { get; set; } = ""; - public enum CodeEditorMode - { - Unstated, - DontDecompile, - Decompile - } + public enum SaveResult { NotSaved, @@ -134,9 +129,6 @@ public enum ScrollDirection Right } - // TODO: move this to the code editor - public static CodeEditorMode CodeEditorDecompile { get; set; } = CodeEditorMode.Unstated; - private int progressValue; private Task updater; private CancellationTokenSource cts; @@ -2421,7 +2413,11 @@ public async Task StopProgressBarUpdater() //async because "Wait()" blocks UI th updater.Dispose(); } - public void OpenCodeFile(string name, CodeEditorMode editorDecompile, bool inNewTab = false) + public void OpenCodeEntry(string name, UndertaleCodeEditor.CodeEditorTab editorTab, bool inNewTab = false) + { + OpenCodeEntry(name, -1, editorTab, inNewTab); + } + public void OpenCodeEntry(string name, int lineNum, UndertaleCodeEditor.CodeEditorTab editorTab, bool inNewTab = false) { UndertaleCode code = Data.Code.ByName(name); @@ -2429,31 +2425,39 @@ public void OpenCodeFile(string name, CodeEditorMode editorDecompile, bool inNew { Focus(); + #pragma warning disable CA1416 if (Selected == code) { - #pragma warning disable CA1416 var codeEditor = FindVisualChild(DataEditor); if (codeEditor is null) { - Debug.WriteLine("Cannot select the code editor mode tab - its instance is not found."); + Debug.WriteLine("Cannot select the code editor mode tab - the instance is not found."); } else { - if (editorDecompile == CodeEditorMode.Decompile + if (editorTab == UndertaleCodeEditor.CodeEditorTab.Decompiled && !codeEditor.DecompiledTab.IsSelected) { codeEditor.CodeModeTabs.SelectedItem = codeEditor.DecompiledTab; } - else if (editorDecompile == CodeEditorMode.DontDecompile + else if (editorTab == UndertaleCodeEditor.CodeEditorTab.Disassembly && !codeEditor.DisassemblyTab.IsSelected) { codeEditor.CodeModeTabs.SelectedItem = codeEditor.DisassemblyTab; } + + var editor = editorTab == UndertaleCodeEditor.CodeEditorTab.Decompiled + ? codeEditor.DecompiledEditor : codeEditor.DisassemblyEditor; + UndertaleCodeEditor.ChangeLineNumber(lineNum, editor); } - #pragma warning restore CA1416 } else - CodeEditorDecompile = editorDecompile; + { + UndertaleCodeEditor.EditorTab = editorTab; + UndertaleCodeEditor.ChangeLineNumber(lineNum, editorTab); + } + + #pragma warning restore CA1416 HighlightObject(code); ChangeSelection(code, inNewTab); @@ -2718,7 +2722,7 @@ public void SimpleTextOutput(string titleText, string labelText, string message, TextInput textOutput = new TextInput(labelText, titleText, message, isMultiline, true); //read-only mode textOutput.Show(); } - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null) { await Task.Delay(150); //wait until progress bar status is displayed @@ -2731,7 +2735,7 @@ public async Task ClickableSearchOutput(string title, string query, int resultsC PlayInformationSound(); } - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null) { await Task.Delay(150); diff --git a/UndertaleModTool/Scripts/Builtin Scripts/Search.csx b/UndertaleModTool/Scripts/Builtin Scripts/Search.csx index 562f482a0..4af143523 100644 --- a/UndertaleModTool/Scripts/Builtin Scripts/Search.csx +++ b/UndertaleModTool/Scripts/Builtin Scripts/Search.csx @@ -17,16 +17,16 @@ if (Data.IsYYC()) } StringBuilder results = new(); -ConcurrentDictionary> resultsDict = new(); +ConcurrentDictionary> resultsDict = new(); ConcurrentBag failedList = new(); -IOrderedEnumerable failedSorted; //failedList.OrderBy() -IOrderedEnumerable>> resultsSorted; //resultsDict.OrderBy() -int result_count = 0; +IOrderedEnumerable failedSorted; //failedList.OrderBy() +IOrderedEnumerable>> resultsSorted; //resultsDict.OrderBy() +int resultCount = 0; ThreadLocal DECOMPILE_CONTEXT = new ThreadLocal(() => new GlobalDecompileContext(Data, false)); -bool case_sensitive = ScriptQuestion("Case sensitive?"); -bool regex_check = ScriptQuestion("Regex search?"); +bool caseSensitive = ScriptQuestion("Case sensitive?"); +bool regexCheck = ScriptQuestion("Regex search?"); string keyword = SimpleTextInput("Enter your search", "Search box below", "", false); if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) { @@ -35,9 +35,9 @@ if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) } Regex keywordRegex; -if (regex_check) +if (regexCheck) { - if (case_sensitive) + if (caseSensitive) keywordRegex = new(keyword, RegexOptions.Compiled); else keywordRegex = new(keyword, RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -56,7 +56,7 @@ await StopProgressBarUpdater(); await Task.Run(SortResults); UpdateProgressStatus("Generating result list..."); -await ClickableSearchOutput("Search results.", keyword, result_count, resultsSorted, true, failedSorted); +await ClickableSearchOutput("Search results.", keyword, resultCount, resultsSorted, true, failedSorted); HideProgressBar(); EnableUI(); @@ -108,26 +108,26 @@ void DumpCode(UndertaleCode code) { if (code is not null && code.ParentEntry is null) { - var line_number = 1; + var lineNumber = 1; StringReader decompiledText = new(code != null ? Decompiler.Decompile(code, DECOMPILE_CONTEXT.Value) : ""); - bool name_written = false; + bool nameWritten = false; string lineInt; while ((lineInt = decompiledText.ReadLine()) is not null) { if (lineInt == string.Empty) continue; - if (((regex_check && RegexContains(in lineInt)) || ((!regex_check && case_sensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) + if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { - if (name_written == false) + if (nameWritten == false) { - resultsDict[code.Name.Content] = new List(); - name_written = true; + resultsDict[code.Name.Content] = new List<(int, string)>(); + nameWritten = true; } - resultsDict[code.Name.Content].Add($"Line {line_number}: {lineInt}"); - Interlocked.Increment(ref result_count); + resultsDict[code.Name.Content].Add((lineNumber, lineInt)); + Interlocked.Increment(ref resultCount); } - line_number += 1; + lineNumber += 1; } } } @@ -142,26 +142,26 @@ void ScanCode(KeyValuePair code) { try { - var line_number = 1; + var lineNumber = 1; StringReader decompiledText = new(code.Value); - bool name_written = false; + bool nameWritten = false; string lineInt; while ((lineInt = decompiledText.ReadLine()) is not null) { if (lineInt == string.Empty) continue; - if (((regex_check && RegexContains(in lineInt)) || ((!regex_check && case_sensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) + if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { - if (name_written == false) + if (nameWritten == false) { - resultsDict[code.Key] = new List(); - name_written = true; + resultsDict[code.Key] = new List<(int, string)>(); + nameWritten = true; } - resultsDict[code.Key].Add($"Line {line_number}: {lineInt}"); - Interlocked.Increment(ref result_count); + resultsDict[code.Key].Add((lineNumber, lineInt)); + Interlocked.Increment(ref resultCount); } - line_number += 1; + lineNumber += 1; } } catch (Exception e) diff --git a/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx b/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx index 239050cc6..e0b290c5d 100644 --- a/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx +++ b/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx @@ -17,13 +17,13 @@ if (Data.IsYYC()) } StringBuilder results = new(); -ConcurrentDictionary> resultsDict = new(); +ConcurrentDictionary> resultsDict = new(); ConcurrentBag failedList = new(); -IOrderedEnumerable failedSorted; //failedList.OrderBy() -IOrderedEnumerable>> resultsSorted; //resultsDict.OrderBy() -int result_count = 0; -bool case_sensitive = ScriptQuestion("Case sensitive?"); -bool regex_check = ScriptQuestion("Regex search?"); +IOrderedEnumerable failedSorted; //failedList.OrderBy() +IOrderedEnumerable>> resultsSorted; //resultsDict.OrderBy() +int resultCount = 0; +bool caseSensitive = ScriptQuestion("Case sensitive?"); +bool regexCheck = ScriptQuestion("Regex search?"); string keyword = SimpleTextInput("Enter your search", "Search box below", "", false); if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) { @@ -32,9 +32,9 @@ if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) } Regex keywordRegex; -if (regex_check) +if (regexCheck) { - if (case_sensitive) + if (caseSensitive) keywordRegex = new(keyword, RegexOptions.Compiled); else keywordRegex = new(keyword, RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -50,7 +50,7 @@ await StopProgressBarUpdater(); await Task.Run(SortResults); UpdateProgressStatus("Generating result list..."); -await ClickableSearchOutput("Search results.", keyword, result_count, resultsSorted, false, failedSorted); +await ClickableSearchOutput("Search results.", keyword, resultCount, resultsSorted, false, failedSorted); HideProgressBar(); EnableUI(); @@ -86,26 +86,26 @@ void DumpCode(UndertaleCode code) { try { - var line_number = 1; + var lineNumber = 1; StringReader assemblyText = new(code != null ? code.Disassemble(Data.Variables, Data.CodeLocals.For(code)) : ""); - bool name_written = false; + bool nameWritten = false; string lineInt; while ((lineInt = assemblyText.ReadLine()) is not null) { if (lineInt == string.Empty) continue; - if (((regex_check && RegexContains(in lineInt)) || ((!regex_check && case_sensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) + if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { - if (name_written == false) + if (nameWritten == false) { - resultsDict[code.Name.Content] = new List(); - name_written = true; + resultsDict[code.Name.Content] = new List<(int, string)>(); + nameWritten = true; } - resultsDict[code.Name.Content].Add($"Line {line_number}: {lineInt}"); - Interlocked.Increment(ref result_count); + resultsDict[code.Name.Content].Add((lineNumber, lineInt)); + Interlocked.Increment(ref resultCount); } - line_number += 1; + lineNumber += 1; } } catch (Exception e) diff --git a/UndertaleModTool/Scripts/Builtin Scripts/SearchLimited.csx b/UndertaleModTool/Scripts/Builtin Scripts/SearchLimited.csx index 316a55917..2de068b4b 100644 --- a/UndertaleModTool/Scripts/Builtin Scripts/SearchLimited.csx +++ b/UndertaleModTool/Scripts/Builtin Scripts/SearchLimited.csx @@ -17,10 +17,10 @@ using System.Text.RegularExpressions; EnsureDataLoaded(); -int result_count = 0; +int resultCount = 0; StringBuilder results = new(); string searchNames = ""; -Dictionary> resultsDict = new(); +Dictionary> resultsDict = new(); List failedList = new(); List codeToDump = new(); List gameObjectCandidates = new(); @@ -32,8 +32,8 @@ if (Data.IsYYC()) return; } -bool case_sensitive = ScriptQuestion("Case sensitive?"); -bool regex_check = ScriptQuestion("Regex search?"); +bool caseSensitive = ScriptQuestion("Case sensitive?"); +bool regexCheck = ScriptQuestion("Regex search?"); string keyword = SimpleTextInput("Enter your search", "Search box below", "", false); if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) { @@ -48,9 +48,9 @@ if (String.IsNullOrEmpty(searchNames) || String.IsNullOrWhiteSpace(searchNames)) } Regex keywordRegex; -if (regex_check) +if (regexCheck) { - if (case_sensitive) + if (caseSensitive) keywordRegex = new(keyword, RegexOptions.Compiled); else keywordRegex = new(keyword, RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -107,7 +107,8 @@ for (var j = 0; j < gameObjectCandidates.Count; j++) SetProgressBar(null, "Code Entries", 0, codeToDump.Count); StartProgressBarUpdater(); -await Task.Run(() => { +await Task.Run(() => +{ for (var j = 0; j < codeToDump.Count; j++) { DumpCode(Data.Code.ByName(codeToDump[j])); @@ -117,7 +118,7 @@ await Task.Run(() => { await StopProgressBarUpdater(); UpdateProgressStatus("Generating result list..."); -await ClickableSearchOutput("Search results.", keyword, result_count, resultsDict, true, failedList); +await ClickableSearchOutput("Search results.", keyword, resultCount, resultsDict, true, failedList); HideProgressBar(); EnableUI(); @@ -138,26 +139,26 @@ void DumpCode(UndertaleCode code) { if (code.ParentEntry is null) { - var line_number = 1; + int lineNumber = 1; StringReader decompiledText = new(code != null ? Decompiler.Decompile(code, DECOMPILE_CONTEXT.Value) : ""); - bool name_written = false; + bool nameWritten = false; string lineInt; while ((lineInt = decompiledText.ReadLine()) is not null) { if (lineInt == string.Empty) continue; - if (((regex_check && RegexContains(in lineInt)) || ((!regex_check && case_sensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) + if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { - if (name_written == false) + if (nameWritten == false) { - resultsDict.Add(code.Name.Content, new List()); - name_written = true; + resultsDict.Add(code.Name.Content, new List<(int, string)>()); + nameWritten = true; } - resultsDict[code.Name.Content].Add($"Line {line_number}: {lineInt}"); - result_count += 1; + resultsDict[code.Name.Content].Add((lineNumber, lineInt)); + resultCount += 1; } - line_number += 1; + lineNumber += 1; } } } diff --git a/UndertaleModTool/Tab.cs b/UndertaleModTool/Tab.cs index 5cc87fb0c..8792c7d99 100644 --- a/UndertaleModTool/Tab.cs +++ b/UndertaleModTool/Tab.cs @@ -903,9 +903,9 @@ public void PrepareCodeEditor() { #pragma warning disable CA1416 if (codeTabState.IsDecompiledOpen) - MainWindow.CodeEditorDecompile = MainWindow.CodeEditorMode.Decompile; + UndertaleCodeEditor.EditorTab = UndertaleCodeEditor.CodeEditorTab.Decompiled; else - MainWindow.CodeEditorDecompile = MainWindow.CodeEditorMode.DontDecompile; + UndertaleCodeEditor.EditorTab = UndertaleCodeEditor.CodeEditorTab.Disassembly; UndertaleCodeEditor.OverriddenDecompPos = codeTabState.DecompiledCodePosition; UndertaleCodeEditor.OverriddenDisasmPos = codeTabState.DisassemblyCodePosition; diff --git a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs index 69aa7c0e9..33a0c2766 100644 --- a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs +++ b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs @@ -13,7 +13,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; -using static UndertaleModTool.MainWindow; +using static UndertaleModTool.UndertaleCodeEditor; namespace UndertaleModTool.Windows { @@ -28,12 +28,13 @@ public partial class ClickableTextOutput : Window public string Query { get; } public int ResultsCount { get; } - private IDictionary> resultsDict; - private IEnumerable failedList; - private CodeEditorMode editorDecompile; + private readonly IDictionary> resultsDict; + private readonly IEnumerable failedList; + private readonly CodeEditorTab editorTab; - public ClickableTextOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) + public ClickableTextOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) { + #pragma warning disable CA1416 InitializeComponent(); linkContextMenu = FindResource("linkContextMenu") as ContextMenuDark; @@ -42,11 +43,13 @@ public ClickableTextOutput(string title, string query, int resultsCount, IOrdere Query = query; ResultsCount = resultsCount; this.resultsDict = resultsDict.ToDictionary(x => x.Key, x => x.Value); - this.editorDecompile = editorDecompile ? CodeEditorMode.Decompile : CodeEditorMode.DontDecompile; + this.editorTab = editorDecompile ? CodeEditorTab.Decompiled : CodeEditorTab.Disassembly; this.failedList = failedList?.ToList(); + #pragma warning restore CA1416 } - public ClickableTextOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) + public ClickableTextOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) { + #pragma warning disable CA1416 InitializeComponent(); linkContextMenu = FindResource("linkContextMenu") as ContextMenuDark; @@ -55,8 +58,9 @@ public ClickableTextOutput(string title, string query, int resultsCount, IDictio Query = query; ResultsCount = resultsCount; this.resultsDict = resultsDict; - this.editorDecompile = editorDecompile ? CodeEditorMode.Decompile : CodeEditorMode.DontDecompile; + this.editorTab = editorDecompile ? CodeEditorTab.Decompiled : CodeEditorTab.Disassembly; this.failedList = failedList; + #pragma warning restore CA1416 } private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { @@ -131,7 +135,8 @@ public void GenerateResults() headerPara.Inlines.Add(new LineBreak()); doc.Blocks.Add(headerPara); - foreach (KeyValuePair> result in resultsDict) + bool tooManyLines = resultsDict.Select(x => x.Value.Count).Sum() > 10000; + foreach (KeyValuePair> result in resultsDict) { int lineCount = result.Value.Count; Paragraph resPara = new(); @@ -144,9 +149,24 @@ public void GenerateResults() resPara.Inlines.Add(resHeader); int i = 1; - foreach (string line in result.Value) + foreach (var linePair in result.Value) { - resPara.Inlines.Add(new Run(line)); + if (!tooManyLines) + { + Hyperlink lineLink = new(new Run($"Line {linePair.Item1}") + { + Tag = result.Key // code entry name + }); + + resPara.Inlines.Add(lineLink); + resPara.Inlines.Add(new Run($": {linePair.Item2}")); + } + else + { + Run lineRun = new($"Line {linePair.Item1}: {linePair.Item2}"); + + resPara.Inlines.Add(lineRun); + } if (i < lineCount) resPara.Inlines.Add(new LineBreak()); @@ -197,27 +217,54 @@ private void OutTextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) { if (mainWindow is null) return; - if (e.OriginalSource is not Run linkRun || linkRun.Parent is not Hyperlink) + if (e.OriginalSource is not Run linkRun || linkRun.Parent is not Hyperlink + || String.IsNullOrEmpty(linkRun.Text)) return; - string codeName = linkRun.Text; - if (e.ChangedButton == MouseButton.Right && linkContextMenu is not null) + if (linkRun.Text.StartsWith("Line ")) { - linkContextMenu.DataContext = codeName; - linkContextMenu.IsOpen = true; + if (!Int32.TryParse(linkRun.Text[5..], out int lineNum)) + { + e.Handled = true; + return; + } + + string codeName = linkRun.Tag as string; + if (String.IsNullOrEmpty(codeName)) + { + e.Handled = true; + return; + } + + if (e.ChangedButton == MouseButton.Right && linkContextMenu is not null) + { + linkContextMenu.DataContext = (lineNum, codeName); + linkContextMenu.IsOpen = true; + } + else + mainWindow.OpenCodeEntry(codeName, lineNum, editorTab, e.ChangedButton == MouseButton.Middle); } else - mainWindow.OpenCodeFile(codeName, editorDecompile, e.ChangedButton == MouseButton.Middle); + { + string codeName = linkRun.Text; + if (e.ChangedButton == MouseButton.Right && linkContextMenu is not null) + { + linkContextMenu.DataContext = (1, codeName); + linkContextMenu.IsOpen = true; + } + else + mainWindow.OpenCodeEntry(codeName, editorTab, e.ChangedButton == MouseButton.Middle); + } e.Handled = true; } private void OpenInNewTabItem_Click(object sender, RoutedEventArgs e) { - string codeName = (sender as FrameworkElement)?.DataContext as string; - if (String.IsNullOrEmpty(codeName)) + if ((sender as FrameworkElement)?.DataContext is not ValueTuple codeNamePair + || String.IsNullOrEmpty(codeNamePair.Item2)) return; - mainWindow.OpenCodeFile(codeName, editorDecompile, true); + mainWindow.OpenCodeEntry(codeNamePair.Item2, codeNamePair.Item1, editorTab, true); } private void Button_Click(object sender, RoutedEventArgs e) From eb13b20f3278fe11cebcfe62ed9539fcadb43663 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 24 Dec 2023 02:12:34 +0300 Subject: [PATCH 02/14] Fix #1580. --- UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx b/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx index e0b290c5d..8fee834ae 100644 --- a/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx +++ b/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx @@ -93,7 +93,10 @@ void DumpCode(UndertaleCode code) while ((lineInt = assemblyText.ReadLine()) is not null) { if (lineInt == string.Empty) + { + lineNumber++; continue; + } if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { @@ -105,7 +108,7 @@ void DumpCode(UndertaleCode code) resultsDict[code.Name.Content].Add((lineNumber, lineInt)); Interlocked.Increment(ref resultCount); } - lineNumber += 1; + lineNumber++; } } catch (Exception e) From 424d807efecec1714316a95cab27b6da3b317d0b Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 24 Dec 2023 02:14:44 +0300 Subject: [PATCH 03/14] Update `ClickableSearchOutput()` for CLI. --- UndertaleModCli/Program.UMTLibInherited.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/UndertaleModCli/Program.UMTLibInherited.cs b/UndertaleModCli/Program.UMTLibInherited.cs index 4bfa19006..413b11871 100644 --- a/UndertaleModCli/Program.UMTLibInherited.cs +++ b/UndertaleModCli/Program.UMTLibInherited.cs @@ -521,13 +521,13 @@ public string SimpleTextInput(string title, string label, string defaultValue, b } /// - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) { await ClickableSearchOutput(title, query, resultsCount, resultsDict.ToDictionary(pair => pair.Key, pair => pair.Value), editorDecompile, failedList); } /// - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) { await Task.Delay(1); //dummy await @@ -555,17 +555,17 @@ public async Task ClickableSearchOutput(string title, string query, int resultsC Console.WriteLine(); // Print in a pattern of: - // results in code_file - // line3: code - // line6: code + // Results in code_file + // Line 3: line of code + // Line 6: line of code // - // results in a codefile2 - //etc. + // Results in code_file_1 + // etc. foreach (var dictEntry in resultsDict) { Console.WriteLine($"Results in {dictEntry.Key}:"); foreach (var resultEntry in dictEntry.Value) - Console.WriteLine(resultEntry); + Console.WriteLine($"Line {resultEntry.Item1}: {resultEntry.Item2}"); Console.WriteLine(); } From 7010ee403b0f0ecf9d206808fd75b2f21203bd70 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 24 Dec 2023 19:42:37 +0300 Subject: [PATCH 04/14] Fix a bug with the current code line selection. --- UndertaleModTool/MainWindow.xaml.cs | 4 ++++ UndertaleModTool/Tab.cs | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index 65247c39f..265b74519 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -2448,11 +2448,15 @@ public void OpenCodeEntry(string name, int lineNum, UndertaleCodeEditor.CodeEdit var editor = editorTab == UndertaleCodeEditor.CodeEditorTab.Decompiled ? codeEditor.DecompiledEditor : codeEditor.DisassemblyEditor; + CurrentTab?.SaveTabContentState(); UndertaleCodeEditor.ChangeLineNumber(lineNum, editor); } } else { + if (CurrentTab?.CurrentObject is UndertaleCode) + CurrentTab.SaveTabContentState(); + UndertaleCodeEditor.EditorTab = editorTab; UndertaleCodeEditor.ChangeLineNumber(lineNum, editorTab); } diff --git a/UndertaleModTool/Tab.cs b/UndertaleModTool/Tab.cs index 8792c7d99..9f4cc358b 100644 --- a/UndertaleModTool/Tab.cs +++ b/UndertaleModTool/Tab.cs @@ -265,8 +265,15 @@ public static void SetTabTitleBinding(object obj, object prevObj, TextBlock text } /// Saves the current tab content state. - public void SaveTabContentState() + /// + /// Whether to overwrite the current tab content state if it's already set. + /// It's by default. + /// + public void SaveTabContentState(bool overwrite = false) { + if (!overwrite && LastContentState is not null) + return; + ContentControl dataEditor = mainWindow.DataEditor; if (dataEditor is null || dataEditor.Content is null From 306b1b90165eb8976e63986b9ddc9ed249bed64b Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 24 Dec 2023 19:56:53 +0300 Subject: [PATCH 05/14] Minor changes in `OpenCodeEntry()`. --- UndertaleModTool/MainWindow.xaml.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index 265b74519..c4576723e 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -2460,7 +2460,6 @@ public void OpenCodeEntry(string name, int lineNum, UndertaleCodeEditor.CodeEdit UndertaleCodeEditor.EditorTab = editorTab; UndertaleCodeEditor.ChangeLineNumber(lineNum, editorTab); } - #pragma warning restore CA1416 HighlightObject(code); @@ -2468,7 +2467,7 @@ public void OpenCodeEntry(string name, int lineNum, UndertaleCodeEditor.CodeEdit } else { - this.ShowError($"Can't find code \"{name}\".\n(probably, different game data was loaded)"); + this.ShowError($"Can't find code entry \"{name}\".\n(probably, different game data was loaded)"); } } From c1bb63c2571a98c66c1cdaa24dbdcbc0ccf6fb04 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 24 Dec 2023 20:14:48 +0300 Subject: [PATCH 06/14] Add a message about disabled clickable lines for too many lines. --- UndertaleModTool/Windows/ClickableTextOutput.xaml.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs index 33a0c2766..dd6d57924 100644 --- a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs +++ b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs @@ -135,7 +135,11 @@ public void GenerateResults() headerPara.Inlines.Add(new LineBreak()); doc.Blocks.Add(headerPara); - bool tooManyLines = resultsDict.Select(x => x.Value.Count).Sum() > 10000; + int totalLineCount = resultsDict.Select(x => x.Value.Count).Sum(); + bool tooManyLines = totalLineCount > 10000; + if (tooManyLines) + mainWindow.ShowWarning($"There are too many code lines to display ({totalLineCount}), so there would be no clickable line numbers."); + foreach (KeyValuePair> result in resultsDict) { int lineCount = result.Value.Count; From e2365ca383cbacca045806a50b87842803ed17a0 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 24 Dec 2023 20:17:50 +0300 Subject: [PATCH 07/14] Remove redundant blank line. --- UndertaleModTool/MainWindow.xaml.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index c4576723e..fdeb3845a 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -116,7 +116,6 @@ public object Selected public string ExePath { get; private set; } = Program.GetExecutableDirectory(); public string ScriptErrorType { get; set; } = ""; - public enum SaveResult { NotSaved, From 8d2d532db8fdd47e086da5de6680bfa4bb94e843 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 24 Dec 2023 21:24:36 +0300 Subject: [PATCH 08/14] Address @Miepee suggestions. --- UndertaleModCli/Program.UMTLibInherited.cs | 6 +++--- UndertaleModLib/Scripting/IScriptInterface.cs | 4 ++-- .../Editors/UndertaleCodeEditor.xaml.cs | 14 +++++++------- UndertaleModTool/MainWindow.xaml.cs | 4 ++-- .../Windows/ClickableTextOutput.xaml.cs | 16 ++++++++-------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/UndertaleModCli/Program.UMTLibInherited.cs b/UndertaleModCli/Program.UMTLibInherited.cs index 413b11871..4a3ab2519 100644 --- a/UndertaleModCli/Program.UMTLibInherited.cs +++ b/UndertaleModCli/Program.UMTLibInherited.cs @@ -521,13 +521,13 @@ public string SimpleTextInput(string title, string label, string defaultValue, b } /// - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) { await ClickableSearchOutput(title, query, resultsCount, resultsDict.ToDictionary(pair => pair.Key, pair => pair.Value), editorDecompile, failedList); } /// - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) { await Task.Delay(1); //dummy await @@ -565,7 +565,7 @@ public async Task ClickableSearchOutput(string title, string query, int resultsC { Console.WriteLine($"Results in {dictEntry.Key}:"); foreach (var resultEntry in dictEntry.Value) - Console.WriteLine($"Line {resultEntry.Item1}: {resultEntry.Item2}"); + Console.WriteLine($"Line {resultEntry.lineNum}: {resultEntry.codeLine}"); Console.WriteLine(); } diff --git a/UndertaleModLib/Scripting/IScriptInterface.cs b/UndertaleModLib/Scripting/IScriptInterface.cs index f21cd8325..5b0ed834e 100644 --- a/UndertaleModLib/Scripting/IScriptInterface.cs +++ b/UndertaleModLib/Scripting/IScriptInterface.cs @@ -297,7 +297,7 @@ bool AreFilesIdentical(string file1, string file2) /// Whether to open the "Decompiled" view or the "Disassembly" view when clicking on an entry name. /// A list of code entries that encountered an error while searching. /// A task that represents the search output. - Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null); + Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null); /// /// Shows search output with clickable text to the user. @@ -310,7 +310,7 @@ bool AreFilesIdentical(string file1, string file2) /// Whether to open the "Decompiled" view or the "Disassembly" view when clicking on an entry name. /// A list of code entries that encountered an error while searching. /// A task that represents the search output. - Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null); + Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null); /// /// Sets . diff --git a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs index e821c225c..69c5e6488 100644 --- a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs @@ -76,11 +76,11 @@ public partial class UndertaleCodeEditor : DataUserControl public enum CodeEditorTab { - Unstated, + Unknown, Disassembly, Decompiled } - public static CodeEditorTab EditorTab { get; set; } = CodeEditorTab.Unstated; + public static CodeEditorTab EditorTab { get; set; } = CodeEditorTab.Unknown; public UndertaleCodeEditor() { @@ -305,7 +305,7 @@ private async void UserControl_DataContextChanged(object sender, DependencyPrope CurrentDecompiled = null; CurrentDisassembled = null; - if (EditorTab != CodeEditorTab.Unstated) //if opened from the code search results "link" + if (EditorTab != CodeEditorTab.Unknown) // if opened from the code search results "link" { if (EditorTab == CodeEditorTab.Disassembly && code != CurrentDisassembled) { @@ -323,7 +323,7 @@ private async void UserControl_DataContextChanged(object sender, DependencyPrope _ = DecompileCode(code, true); } - EditorTab = CodeEditorTab.Unstated; + EditorTab = CodeEditorTab.Unknown; } else FillInCodeViewer(true); @@ -399,9 +399,9 @@ public static void ChangeLineNumber(int lineNum, CodeEditorTab editorTab) if (lineNum < 1) return; - if (editorTab == CodeEditorTab.Unstated) + if (editorTab == CodeEditorTab.Unknown) { - Debug.WriteLine("The \"editorTab\" argument of \"GoToLine()\" is \"Unstated\"."); + Debug.WriteLine($"The \"{nameof(editorTab)}\" argument of \"{nameof(ChangeLineNumber)}()\" is \"{nameof(CodeEditorTab.Unknown)}\"."); return; } @@ -417,7 +417,7 @@ public static void ChangeLineNumber(int lineNum, TextEditor textEditor) if (textEditor is null) { - Debug.WriteLine("The \"textEditor\" argument of \"GoToLine()\" is null."); + Debug.WriteLine($"The \"{nameof(textEditor)}\" argument of \"{nameof(ChangeLineNumber)}()\" is null."); return; } diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index fdeb3845a..decc66666 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -2724,7 +2724,7 @@ public void SimpleTextOutput(string titleText, string labelText, string message, TextInput textOutput = new TextInput(labelText, titleText, message, isMultiline, true); //read-only mode textOutput.Show(); } - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null) { await Task.Delay(150); //wait until progress bar status is displayed @@ -2737,7 +2737,7 @@ public async Task ClickableSearchOutput(string title, string query, int resultsC PlayInformationSound(); } - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null) { await Task.Delay(150); diff --git a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs index dd6d57924..a3ab2c0a2 100644 --- a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs +++ b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs @@ -28,11 +28,11 @@ public partial class ClickableTextOutput : Window public string Query { get; } public int ResultsCount { get; } - private readonly IDictionary> resultsDict; + private readonly IDictionary> resultsDict; private readonly IEnumerable failedList; private readonly CodeEditorTab editorTab; - public ClickableTextOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) + public ClickableTextOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) { #pragma warning disable CA1416 InitializeComponent(); @@ -47,7 +47,7 @@ public ClickableTextOutput(string title, string query, int resultsCount, IOrdere this.failedList = failedList?.ToList(); #pragma warning restore CA1416 } - public ClickableTextOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) + public ClickableTextOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) { #pragma warning disable CA1416 InitializeComponent(); @@ -140,7 +140,7 @@ public void GenerateResults() if (tooManyLines) mainWindow.ShowWarning($"There are too many code lines to display ({totalLineCount}), so there would be no clickable line numbers."); - foreach (KeyValuePair> result in resultsDict) + foreach (KeyValuePair> result in resultsDict) { int lineCount = result.Value.Count; Paragraph resPara = new(); @@ -153,21 +153,21 @@ public void GenerateResults() resPara.Inlines.Add(resHeader); int i = 1; - foreach (var linePair in result.Value) + foreach (var (lineNum, codeLine) in result.Value) { if (!tooManyLines) { - Hyperlink lineLink = new(new Run($"Line {linePair.Item1}") + Hyperlink lineLink = new(new Run($"Line {lineNum}") { Tag = result.Key // code entry name }); resPara.Inlines.Add(lineLink); - resPara.Inlines.Add(new Run($": {linePair.Item2}")); + resPara.Inlines.Add(new Run($": {codeLine}")); } else { - Run lineRun = new($"Line {linePair.Item1}: {linePair.Item2}"); + Run lineRun = new($"Line {lineNum}: {codeLine}"); resPara.Inlines.Add(lineRun); } From 15f416f5b6238ddb49847eab643002e472666a0a Mon Sep 17 00:00:00 2001 From: VladiStep Date: Thu, 28 Dec 2023 19:39:57 +0300 Subject: [PATCH 09/14] Add `UndertaleCode.Insert()` and `UndertaleInstruction.Clone()` (by @neprim). --- UndertaleModLib/Models/UndertaleCode.cs | 65 +++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/UndertaleModLib/Models/UndertaleCode.cs b/UndertaleModLib/Models/UndertaleCode.cs index dbedb4abb..8a54c610f 100644 --- a/UndertaleModLib/Models/UndertaleCode.cs +++ b/UndertaleModLib/Models/UndertaleCode.cs @@ -843,6 +843,42 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader) return 0; } + public UndertaleInstruction Clone() + { + return new UndertaleInstruction() + { + Kind = this.Kind, + ComparisonKind = this.ComparisonKind, + Type1 = this.Type1, + Type2 = this.Type2, + TypeInst = this.TypeInst, + Value = ( + this.Value != null + ? ( + this.Value.GetType() == typeof(Reference) + ? new Reference(((Reference)(this.Value)).Target, ((Reference)(this.Value)).Type) + : ( + this.Value.GetType() == typeof(Reference) + ? new Reference(((Reference)(this.Value)).Target) + : ( + this.Value.GetType() == typeof(UndertaleResourceById) + ? new UndertaleResourceById(((UndertaleResourceById)(this.Value)).Resource, ((UndertaleResourceById)(this.Value)).CachedId) + : this.Value + ) + ) + ) + : null + ), + Destination = (this.Destination != null ? new Reference(this.Destination.Target, this.Destination.Type) : null), + Function = (this.Function != null ? new Reference(this.Function.Target, this.Function.Type) : null), + JumpOffset = this.JumpOffset, + JumpOffsetPopenvExitMagic = this.JumpOffsetPopenvExitMagic, + ArgumentsCount = this.ArgumentsCount, + Extra = this.Extra, + SwapExtra = this.SwapExtra, + }; + } + /// public override string ToString() { @@ -1343,6 +1379,35 @@ public void Replace(IList instructions) Append(instructions); } + public void Insert(IList instructions, int index) + { + uint offsetU = 0; + uint address = Instructions[index].Address; + foreach (UndertaleInstruction instr in instructions) + offsetU += instr.CalculateInstructionSize(); + + int offset = (int)offsetU; + + for (int i = 0; i < Instructions.Count; i++) + { + if (UndertaleInstruction.GetInstructionType(Instructions[i].Kind) == UndertaleInstruction.InstructionType.GotoInstruction) + { + if (i < index && Instructions[i].Address + Instructions[i].JumpOffset > address) + { + Instructions[i] = Instructions[i].Clone(); + Instructions[i].JumpOffset += offset; + } + else if (i > index && Instructions[i].Address + Instructions[i].JumpOffset <= address) + { + Instructions[i] = Instructions[i].Clone(); + Instructions[i].JumpOffset -= offset; + } + } + } + + Instructions.InsertRange(index + 1, instructions); + } + /// /// Append GML instructions at the end of this code entry. /// From 90a83aa3712e6d1533f0c849e437090808664b2e Mon Sep 17 00:00:00 2001 From: VladiStep Date: Thu, 28 Dec 2023 19:47:16 +0300 Subject: [PATCH 10/14] Add XML documentation for the new methods. --- UndertaleModLib/Models/UndertaleCode.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/UndertaleModLib/Models/UndertaleCode.cs b/UndertaleModLib/Models/UndertaleCode.cs index 8a54c610f..821e30c85 100644 --- a/UndertaleModLib/Models/UndertaleCode.cs +++ b/UndertaleModLib/Models/UndertaleCode.cs @@ -843,6 +843,10 @@ public static uint UnserializeChildObjectCount(UndertaleReader reader) return 0; } + /// + /// Creates a new that is a copy of this instruction. + /// + /// A new that is a copy of this instruction. public UndertaleInstruction Clone() { return new UndertaleInstruction() @@ -1379,6 +1383,11 @@ public void Replace(IList instructions) Append(instructions); } + /// + /// Insert instructions at specified index in this code entry. + /// + /// The instructions to insert. + /// The index of insertion. public void Insert(IList instructions, int index) { uint offsetU = 0; From 150ba532c936847d52bd3240c521c0d923838316 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Fri, 29 Dec 2023 01:19:18 +0300 Subject: [PATCH 11/14] Simplify `UndertaleInstruction.Clone()`, make some classes `ICloneable`. --- UndertaleModLib/Models/UndertaleCode.cs | 38 +++++++++++-------------- UndertaleModLib/UndertaleIO.cs | 9 +++++- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/UndertaleModLib/Models/UndertaleCode.cs b/UndertaleModLib/Models/UndertaleCode.cs index 821e30c85..fbc709ef2 100644 --- a/UndertaleModLib/Models/UndertaleCode.cs +++ b/UndertaleModLib/Models/UndertaleCode.cs @@ -226,7 +226,7 @@ public interface ReferencedObject int NameStringID { get; set; } } - public class Reference : UndertaleObject where T : class, UndertaleObject, ReferencedObject + public class Reference : UndertaleObject, ICloneable where T : class, UndertaleObject, ReferencedObject { public uint NextOccurrenceOffset { get; set; } = 0xdead; public VariableType Type { get; set; } @@ -268,6 +268,18 @@ public void Unserialize(UndertaleReader reader) Type = (VariableType)((int32Value >> 24) & 0xF8); } + /// + public Reference Clone() + { + return new Reference() + { + NextOccurrenceOffset = this.NextOccurrenceOffset, + Target = (T)this.Target, + Type = this.Type + }; + } + object ICloneable.Clone() => Clone(); + /// public override string ToString() { @@ -856,30 +868,14 @@ public UndertaleInstruction Clone() Type1 = this.Type1, Type2 = this.Type2, TypeInst = this.TypeInst, - Value = ( - this.Value != null - ? ( - this.Value.GetType() == typeof(Reference) - ? new Reference(((Reference)(this.Value)).Target, ((Reference)(this.Value)).Type) - : ( - this.Value.GetType() == typeof(Reference) - ? new Reference(((Reference)(this.Value)).Target) - : ( - this.Value.GetType() == typeof(UndertaleResourceById) - ? new UndertaleResourceById(((UndertaleResourceById)(this.Value)).Resource, ((UndertaleResourceById)(this.Value)).CachedId) - : this.Value - ) - ) - ) - : null - ), - Destination = (this.Destination != null ? new Reference(this.Destination.Target, this.Destination.Type) : null), - Function = (this.Function != null ? new Reference(this.Function.Target, this.Function.Type) : null), + Value = this.Value is ICloneable cloneable ? cloneable.Clone() : this.Value, + Destination = this.Destination?.Clone(), + Function = this.Function?.Clone(), JumpOffset = this.JumpOffset, JumpOffsetPopenvExitMagic = this.JumpOffsetPopenvExitMagic, ArgumentsCount = this.ArgumentsCount, Extra = this.Extra, - SwapExtra = this.SwapExtra, + SwapExtra = this.SwapExtra }; } diff --git a/UndertaleModLib/UndertaleIO.cs b/UndertaleModLib/UndertaleIO.cs index 7ced9747b..598e7496c 100644 --- a/UndertaleModLib/UndertaleIO.cs +++ b/UndertaleModLib/UndertaleIO.cs @@ -26,7 +26,7 @@ public interface UndertaleResourceRef : UndertaleObject int SerializeById(UndertaleWriter writer); } - public class UndertaleResourceById : UndertaleResourceRef, IStaticChildObjectsSize, IDisposable where T : UndertaleResource, new() where ChunkT : UndertaleListChunk + public class UndertaleResourceById : UndertaleResourceRef, IStaticChildObjectsSize, ICloneable, IDisposable where T : UndertaleResource, new() where ChunkT : UndertaleListChunk { /// public static readonly uint ChildObjectsSize = 4; @@ -120,6 +120,13 @@ public override string ToString() return (Resource?.ToString() ?? "(null)") + GetMarkerSuffix(); } + /// + public UndertaleResourceById Clone() + { + return new UndertaleResourceById(Resource, CachedId); + } + object ICloneable.Clone() => Clone(); + /// public void Dispose() { From 97af910ab74d4f5e2367744b256f93bab099da7d Mon Sep 17 00:00:00 2001 From: VladiStep Date: Fri, 29 Dec 2023 01:24:17 +0300 Subject: [PATCH 12/14] Change XML comments for instances of `Clone()`. --- UndertaleModLib/Models/UndertaleCode.cs | 5 ++++- UndertaleModLib/UndertaleIO.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/UndertaleModLib/Models/UndertaleCode.cs b/UndertaleModLib/Models/UndertaleCode.cs index fbc709ef2..15d4ad2cb 100644 --- a/UndertaleModLib/Models/UndertaleCode.cs +++ b/UndertaleModLib/Models/UndertaleCode.cs @@ -268,7 +268,10 @@ public void Unserialize(UndertaleReader reader) Type = (VariableType)((int32Value >> 24) & 0xF8); } - /// + /// + /// Creates a new that is a copy of this . + /// + /// A new that is a copy of this . public Reference Clone() { return new Reference() diff --git a/UndertaleModLib/UndertaleIO.cs b/UndertaleModLib/UndertaleIO.cs index 598e7496c..baadbd011 100644 --- a/UndertaleModLib/UndertaleIO.cs +++ b/UndertaleModLib/UndertaleIO.cs @@ -120,7 +120,10 @@ public override string ToString() return (Resource?.ToString() ?? "(null)") + GetMarkerSuffix(); } - /// + /// + /// Creates a new that is a copy of this . + /// + /// A new that is a copy of this . public UndertaleResourceById Clone() { return new UndertaleResourceById(Resource, CachedId); From 52bb596dc5783da79f772129a4a7f77330396a5c Mon Sep 17 00:00:00 2001 From: VladiStep Date: Fri, 29 Dec 2023 01:27:00 +0300 Subject: [PATCH 13/14] Add `UndertaleCode.Insert(instruction, index)`. --- UndertaleModLib/Models/UndertaleCode.cs | 31 ++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/UndertaleModLib/Models/UndertaleCode.cs b/UndertaleModLib/Models/UndertaleCode.cs index 15d4ad2cb..33de7fb8a 100644 --- a/UndertaleModLib/Models/UndertaleCode.cs +++ b/UndertaleModLib/Models/UndertaleCode.cs @@ -277,7 +277,7 @@ public Reference Clone() return new Reference() { NextOccurrenceOffset = this.NextOccurrenceOffset, - Target = (T)this.Target, + Target = this.Target, Type = this.Type }; } @@ -1415,6 +1415,35 @@ public void Insert(IList instructions, int index) Instructions.InsertRange(index + 1, instructions); } + /// + /// Insert an instruction at specified index in this code entry. + /// + /// The instruction to insert. + /// The index of insertion. + public void Insert(UndertaleInstruction instruction, int index) + { + uint address = Instructions[index].Address; + int offset = (int)instruction.CalculateInstructionSize(); + + for (int i = 0; i < Instructions.Count; i++) + { + if (UndertaleInstruction.GetInstructionType(Instructions[i].Kind) == UndertaleInstruction.InstructionType.GotoInstruction) + { + if (i < index && Instructions[i].Address + Instructions[i].JumpOffset > address) + { + Instructions[i] = Instructions[i].Clone(); + Instructions[i].JumpOffset += offset; + } + else if (i > index && Instructions[i].Address + Instructions[i].JumpOffset <= address) + { + Instructions[i] = Instructions[i].Clone(); + Instructions[i].JumpOffset -= offset; + } + } + } + + Instructions.Insert(index + 1, instruction); + } /// /// Append GML instructions at the end of this code entry. From de0ee1b92469af9a4e64c9c5f0cbe2732b5f6653 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sat, 30 Dec 2023 21:29:26 +0300 Subject: [PATCH 14/14] Revert "Merge branch 'clickableCodeLineNumbers' into codeInstructionImprovements" This reverts commit f31fc2992c4ef11f36299dec6467a19efa017abb, reversing changes made to acd4a35d818cdd41fb5ef7032251a34adf8f8063. --- UndertaleModCli/Program.UMTLibInherited.cs | 16 ++-- UndertaleModLib/Scripting/IScriptInterface.cs | 26 +++++- .../Editors/UndertaleCodeEditor.xaml.cs | 58 ++---------- UndertaleModTool/MainWindow.xaml.cs | 44 ++++----- .../Scripts/Builtin Scripts/Search.csx | 54 +++++------ .../Scripts/Builtin Scripts/SearchASM.csx | 39 ++++---- .../Scripts/Builtin Scripts/SearchLimited.csx | 35 ++++--- UndertaleModTool/Tab.cs | 13 +-- .../Windows/ClickableTextOutput.xaml.cs | 91 ++++--------------- 9 files changed, 143 insertions(+), 233 deletions(-) diff --git a/UndertaleModCli/Program.UMTLibInherited.cs b/UndertaleModCli/Program.UMTLibInherited.cs index 4a3ab2519..4bfa19006 100644 --- a/UndertaleModCli/Program.UMTLibInherited.cs +++ b/UndertaleModCli/Program.UMTLibInherited.cs @@ -521,13 +521,13 @@ public string SimpleTextInput(string title, string label, string defaultValue, b } /// - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) { await ClickableSearchOutput(title, query, resultsCount, resultsDict.ToDictionary(pair => pair.Key, pair => pair.Value), editorDecompile, failedList); } /// - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) { await Task.Delay(1); //dummy await @@ -555,17 +555,17 @@ public async Task ClickableSearchOutput(string title, string query, int resultsC Console.WriteLine(); // Print in a pattern of: - // Results in code_file - // Line 3: line of code - // Line 6: line of code + // results in code_file + // line3: code + // line6: code // - // Results in code_file_1 - // etc. + // results in a codefile2 + //etc. foreach (var dictEntry in resultsDict) { Console.WriteLine($"Results in {dictEntry.Key}:"); foreach (var resultEntry in dictEntry.Value) - Console.WriteLine($"Line {resultEntry.lineNum}: {resultEntry.codeLine}"); + Console.WriteLine(resultEntry); Console.WriteLine(); } diff --git a/UndertaleModLib/Scripting/IScriptInterface.cs b/UndertaleModLib/Scripting/IScriptInterface.cs index 5b0ed834e..e896e4373 100644 --- a/UndertaleModLib/Scripting/IScriptInterface.cs +++ b/UndertaleModLib/Scripting/IScriptInterface.cs @@ -293,11 +293,20 @@ bool AreFilesIdentical(string file1, string file2) /// The query that was searched for. /// How many results have been found. /// An of type , - /// with TKey being the name of the code entry an TValue being a list of tuples where the first item is the matching code line number and the second one is the code line itself. + /// with TKey being the name of the code entry an TValue being a list of matching code lines with their line number prepended. /// Whether to open the "Decompiled" view or the "Disassembly" view when clicking on an entry name. /// A list of code entries that encountered an error while searching. /// A task that represents the search output. - Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null); + Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null); + + /// + /// Obsolete. + /// + [Obsolete("Use ClickableSearchOutput instead!")] + sealed Task ClickableTextOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null) + { + return ClickableSearchOutput(title, query, resultsCount, resultsDict, showInDecompiledView, failedList); + } /// /// Shows search output with clickable text to the user. @@ -306,11 +315,20 @@ bool AreFilesIdentical(string file1, string file2) /// The query that was searched for. /// How many results have been found. /// A with TKey being the name of the code entry and - /// TValue being a list of tuples where the first item is the matching code line number and the second one is the code line itself. + /// TValue being a list of matching code lines with their line number prepended. /// Whether to open the "Decompiled" view or the "Disassembly" view when clicking on an entry name. /// A list of code entries that encountered an error while searching. /// A task that represents the search output. - Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null); + Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null); + + /// + /// Obsolete. + /// + [Obsolete("Use ClickableSearchOutput instead!")] + sealed Task ClickableTextOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null) + { + return ClickableSearchOutput(title, query, resultsCount, resultsDict, showInDecompiledView, failedList); + } /// /// Sets . diff --git a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs index df9d2f647..22bf5e8e0 100644 --- a/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs +++ b/UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs @@ -34,6 +34,7 @@ using UndertaleModLib.Compiler; using UndertaleModLib.Decompiler; using UndertaleModLib.Models; +using static UndertaleModTool.MainWindow.CodeEditorMode; using Input = System.Windows.Input; namespace UndertaleModTool @@ -58,14 +59,14 @@ public partial class UndertaleCodeEditor : DataUserControl public bool DecompiledYet = false; public bool DecompiledSkipped = false; public SearchPanel DecompiledSearchPanel; - public static (int Line, int Column, double ScrollPos) OverriddenDecompPos { get; set; } + public static (int Line, int Column, double ScrollPos) OverriddenDecompPos; public bool DisassemblyFocused = false; public bool DisassemblyChanged = false; public bool DisassembledYet = false; public bool DisassemblySkipped = false; public SearchPanel DisassemblySearchPanel; - public static (int Line, int Column, double ScrollPos) OverriddenDisasmPos { get; set; } + public static (int Line, int Column, double ScrollPos) OverriddenDisasmPos; public static RoutedUICommand Compile = new RoutedUICommand("Compile code", "Compile", typeof(UndertaleCodeEditor)); @@ -74,14 +75,6 @@ public partial class UndertaleCodeEditor : DataUserControl private static readonly Dictionary FunctionsDict = new(); private static readonly Dictionary CodeDict = new(); - public enum CodeEditorTab - { - Unknown, - Disassembly, - Decompiled - } - public static CodeEditorTab EditorTab { get; set; } = CodeEditorTab.Unknown; - public UndertaleCodeEditor() { InitializeComponent(); @@ -305,9 +298,9 @@ private async void UserControl_DataContextChanged(object sender, DependencyPrope CurrentDecompiled = null; CurrentDisassembled = null; - if (EditorTab != CodeEditorTab.Unknown) // if opened from the code search results "link" + if (MainWindow.CodeEditorDecompile != Unstated) //if opened from the code search results "link" { - if (EditorTab == CodeEditorTab.Disassembly && code != CurrentDisassembled) + if (MainWindow.CodeEditorDecompile == DontDecompile && code != CurrentDisassembled) { if (CodeModeTabs.SelectedItem != DisassemblyTab) CodeModeTabs.SelectedItem = DisassemblyTab; @@ -315,7 +308,7 @@ private async void UserControl_DataContextChanged(object sender, DependencyPrope DisassembleCode(code, true); } - if (EditorTab == CodeEditorTab.Decompiled && code != CurrentDecompiled) + if (MainWindow.CodeEditorDecompile == Decompile && code != CurrentDecompiled) { if (CodeModeTabs.SelectedItem != DecompiledTab) CodeModeTabs.SelectedItem = DecompiledTab; @@ -323,7 +316,7 @@ private async void UserControl_DataContextChanged(object sender, DependencyPrope _ = DecompileCode(code, true); } - EditorTab = CodeEditorTab.Unknown; + MainWindow.CodeEditorDecompile = Unstated; } else FillInCodeViewer(true); @@ -374,9 +367,6 @@ private static void RestoreCaretPosition(TextEditor textEditor, int linePos, int { if (linePos <= textEditor.LineCount) { - if (linePos == -1) - linePos = textEditor.Document.LineCount; - int lineLen = textEditor.Document.GetLineByNumber(linePos).Length; textEditor.TextArea.Caret.Line = linePos; if (columnPos != -1) @@ -385,8 +375,7 @@ private static void RestoreCaretPosition(TextEditor textEditor, int linePos, int textEditor.TextArea.Caret.Column = lineLen + 1; textEditor.ScrollToLine(linePos); - if (scrollPos != -1) - textEditor.ScrollToVerticalOffset(scrollPos); + textEditor.ScrollToVerticalOffset(scrollPos); } else { @@ -394,36 +383,7 @@ private static void RestoreCaretPosition(TextEditor textEditor, int linePos, int textEditor.ScrollToEnd(); } } - public static void ChangeLineNumber(int lineNum, CodeEditorTab editorTab) - { - if (lineNum < 1) - return; - - if (editorTab == CodeEditorTab.Unknown) - { - Debug.WriteLine($"The \"{nameof(editorTab)}\" argument of \"{nameof(ChangeLineNumber)}()\" is \"{nameof(CodeEditorTab.Unknown)}\"."); - return; - } - - if (editorTab == CodeEditorTab.Decompiled) - OverriddenDecompPos = (lineNum, -1, -1); - else - OverriddenDisasmPos = (lineNum, -1, -1); - } - public static void ChangeLineNumber(int lineNum, TextEditor textEditor) - { - if (lineNum < 1) - return; - - if (textEditor is null) - { - Debug.WriteLine($"The \"{nameof(textEditor)}\" argument of \"{nameof(ChangeLineNumber)}()\" is null."); - return; - } - - RestoreCaretPosition(textEditor, lineNum, -1, -1); - } - + private static void FillObjectDicts() { var data = mainWindow.Data; diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index 86f6b9f0e..df1d9ad3b 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -116,6 +116,12 @@ public object Selected public string ExePath { get; private set; } = Program.GetExecutableDirectory(); public string ScriptErrorType { get; set; } = ""; + public enum CodeEditorMode + { + Unstated, + DontDecompile, + Decompile + } public enum SaveResult { NotSaved, @@ -128,6 +134,9 @@ public enum ScrollDirection Right } + // TODO: move this to the code editor + public static CodeEditorMode CodeEditorDecompile { get; set; } = CodeEditorMode.Unstated; + private int progressValue; private Task updater; private CancellationTokenSource cts; @@ -2421,11 +2430,7 @@ public async Task StopProgressBarUpdater() //async because "Wait()" blocks UI th updater.Dispose(); } - public void OpenCodeEntry(string name, UndertaleCodeEditor.CodeEditorTab editorTab, bool inNewTab = false) - { - OpenCodeEntry(name, -1, editorTab, inNewTab); - } - public void OpenCodeEntry(string name, int lineNum, UndertaleCodeEditor.CodeEditorTab editorTab, bool inNewTab = false) + public void OpenCodeFile(string name, CodeEditorMode editorDecompile, bool inNewTab = false) { UndertaleCode code = Data.Code.ByName(name); @@ -2433,49 +2438,38 @@ public void OpenCodeEntry(string name, int lineNum, UndertaleCodeEditor.CodeEdit { Focus(); - #pragma warning disable CA1416 if (Selected == code) { + #pragma warning disable CA1416 var codeEditor = FindVisualChild(DataEditor); if (codeEditor is null) { - Debug.WriteLine("Cannot select the code editor mode tab - the instance is not found."); + Debug.WriteLine("Cannot select the code editor mode tab - its instance is not found."); } else { - if (editorTab == UndertaleCodeEditor.CodeEditorTab.Decompiled + if (editorDecompile == CodeEditorMode.Decompile && !codeEditor.DecompiledTab.IsSelected) { codeEditor.CodeModeTabs.SelectedItem = codeEditor.DecompiledTab; } - else if (editorTab == UndertaleCodeEditor.CodeEditorTab.Disassembly + else if (editorDecompile == CodeEditorMode.DontDecompile && !codeEditor.DisassemblyTab.IsSelected) { codeEditor.CodeModeTabs.SelectedItem = codeEditor.DisassemblyTab; } - - var editor = editorTab == UndertaleCodeEditor.CodeEditorTab.Decompiled - ? codeEditor.DecompiledEditor : codeEditor.DisassemblyEditor; - CurrentTab?.SaveTabContentState(); - UndertaleCodeEditor.ChangeLineNumber(lineNum, editor); } + #pragma warning restore CA1416 } else - { - if (CurrentTab?.CurrentObject is UndertaleCode) - CurrentTab.SaveTabContentState(); - - UndertaleCodeEditor.EditorTab = editorTab; - UndertaleCodeEditor.ChangeLineNumber(lineNum, editorTab); - } - #pragma warning restore CA1416 + CodeEditorDecompile = editorDecompile; HighlightObject(code); ChangeSelection(code, inNewTab); } else { - this.ShowError($"Can't find code entry \"{name}\".\n(probably, different game data was loaded)"); + this.ShowError($"Can't find code \"{name}\".\n(probably, different game data was loaded)"); } } @@ -2733,7 +2727,7 @@ public void SimpleTextOutput(string titleText, string labelText, string message, TextInput textOutput = new TextInput(labelText, titleText, message, isMultiline, true); //read-only mode textOutput.Show(); } - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool showInDecompiledView, IOrderedEnumerable failedList = null) { await Task.Delay(150); //wait until progress bar status is displayed @@ -2746,7 +2740,7 @@ public async Task ClickableSearchOutput(string title, string query, int resultsC PlayInformationSound(); } - public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null) + public async Task ClickableSearchOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool showInDecompiledView, IEnumerable failedList = null) { await Task.Delay(150); diff --git a/UndertaleModTool/Scripts/Builtin Scripts/Search.csx b/UndertaleModTool/Scripts/Builtin Scripts/Search.csx index 4af143523..562f482a0 100644 --- a/UndertaleModTool/Scripts/Builtin Scripts/Search.csx +++ b/UndertaleModTool/Scripts/Builtin Scripts/Search.csx @@ -17,16 +17,16 @@ if (Data.IsYYC()) } StringBuilder results = new(); -ConcurrentDictionary> resultsDict = new(); +ConcurrentDictionary> resultsDict = new(); ConcurrentBag failedList = new(); -IOrderedEnumerable failedSorted; //failedList.OrderBy() -IOrderedEnumerable>> resultsSorted; //resultsDict.OrderBy() -int resultCount = 0; +IOrderedEnumerable failedSorted; //failedList.OrderBy() +IOrderedEnumerable>> resultsSorted; //resultsDict.OrderBy() +int result_count = 0; ThreadLocal DECOMPILE_CONTEXT = new ThreadLocal(() => new GlobalDecompileContext(Data, false)); -bool caseSensitive = ScriptQuestion("Case sensitive?"); -bool regexCheck = ScriptQuestion("Regex search?"); +bool case_sensitive = ScriptQuestion("Case sensitive?"); +bool regex_check = ScriptQuestion("Regex search?"); string keyword = SimpleTextInput("Enter your search", "Search box below", "", false); if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) { @@ -35,9 +35,9 @@ if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) } Regex keywordRegex; -if (regexCheck) +if (regex_check) { - if (caseSensitive) + if (case_sensitive) keywordRegex = new(keyword, RegexOptions.Compiled); else keywordRegex = new(keyword, RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -56,7 +56,7 @@ await StopProgressBarUpdater(); await Task.Run(SortResults); UpdateProgressStatus("Generating result list..."); -await ClickableSearchOutput("Search results.", keyword, resultCount, resultsSorted, true, failedSorted); +await ClickableSearchOutput("Search results.", keyword, result_count, resultsSorted, true, failedSorted); HideProgressBar(); EnableUI(); @@ -108,26 +108,26 @@ void DumpCode(UndertaleCode code) { if (code is not null && code.ParentEntry is null) { - var lineNumber = 1; + var line_number = 1; StringReader decompiledText = new(code != null ? Decompiler.Decompile(code, DECOMPILE_CONTEXT.Value) : ""); - bool nameWritten = false; + bool name_written = false; string lineInt; while ((lineInt = decompiledText.ReadLine()) is not null) { if (lineInt == string.Empty) continue; - if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) + if (((regex_check && RegexContains(in lineInt)) || ((!regex_check && case_sensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { - if (nameWritten == false) + if (name_written == false) { - resultsDict[code.Name.Content] = new List<(int, string)>(); - nameWritten = true; + resultsDict[code.Name.Content] = new List(); + name_written = true; } - resultsDict[code.Name.Content].Add((lineNumber, lineInt)); - Interlocked.Increment(ref resultCount); + resultsDict[code.Name.Content].Add($"Line {line_number}: {lineInt}"); + Interlocked.Increment(ref result_count); } - lineNumber += 1; + line_number += 1; } } } @@ -142,26 +142,26 @@ void ScanCode(KeyValuePair code) { try { - var lineNumber = 1; + var line_number = 1; StringReader decompiledText = new(code.Value); - bool nameWritten = false; + bool name_written = false; string lineInt; while ((lineInt = decompiledText.ReadLine()) is not null) { if (lineInt == string.Empty) continue; - if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) + if (((regex_check && RegexContains(in lineInt)) || ((!regex_check && case_sensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { - if (nameWritten == false) + if (name_written == false) { - resultsDict[code.Key] = new List<(int, string)>(); - nameWritten = true; + resultsDict[code.Key] = new List(); + name_written = true; } - resultsDict[code.Key].Add((lineNumber, lineInt)); - Interlocked.Increment(ref resultCount); + resultsDict[code.Key].Add($"Line {line_number}: {lineInt}"); + Interlocked.Increment(ref result_count); } - lineNumber += 1; + line_number += 1; } } catch (Exception e) diff --git a/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx b/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx index 8fee834ae..239050cc6 100644 --- a/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx +++ b/UndertaleModTool/Scripts/Builtin Scripts/SearchASM.csx @@ -17,13 +17,13 @@ if (Data.IsYYC()) } StringBuilder results = new(); -ConcurrentDictionary> resultsDict = new(); +ConcurrentDictionary> resultsDict = new(); ConcurrentBag failedList = new(); -IOrderedEnumerable failedSorted; //failedList.OrderBy() -IOrderedEnumerable>> resultsSorted; //resultsDict.OrderBy() -int resultCount = 0; -bool caseSensitive = ScriptQuestion("Case sensitive?"); -bool regexCheck = ScriptQuestion("Regex search?"); +IOrderedEnumerable failedSorted; //failedList.OrderBy() +IOrderedEnumerable>> resultsSorted; //resultsDict.OrderBy() +int result_count = 0; +bool case_sensitive = ScriptQuestion("Case sensitive?"); +bool regex_check = ScriptQuestion("Regex search?"); string keyword = SimpleTextInput("Enter your search", "Search box below", "", false); if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) { @@ -32,9 +32,9 @@ if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) } Regex keywordRegex; -if (regexCheck) +if (regex_check) { - if (caseSensitive) + if (case_sensitive) keywordRegex = new(keyword, RegexOptions.Compiled); else keywordRegex = new(keyword, RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -50,7 +50,7 @@ await StopProgressBarUpdater(); await Task.Run(SortResults); UpdateProgressStatus("Generating result list..."); -await ClickableSearchOutput("Search results.", keyword, resultCount, resultsSorted, false, failedSorted); +await ClickableSearchOutput("Search results.", keyword, result_count, resultsSorted, false, failedSorted); HideProgressBar(); EnableUI(); @@ -86,29 +86,26 @@ void DumpCode(UndertaleCode code) { try { - var lineNumber = 1; + var line_number = 1; StringReader assemblyText = new(code != null ? code.Disassemble(Data.Variables, Data.CodeLocals.For(code)) : ""); - bool nameWritten = false; + bool name_written = false; string lineInt; while ((lineInt = assemblyText.ReadLine()) is not null) { if (lineInt == string.Empty) - { - lineNumber++; continue; - } - if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) + if (((regex_check && RegexContains(in lineInt)) || ((!regex_check && case_sensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { - if (nameWritten == false) + if (name_written == false) { - resultsDict[code.Name.Content] = new List<(int, string)>(); - nameWritten = true; + resultsDict[code.Name.Content] = new List(); + name_written = true; } - resultsDict[code.Name.Content].Add((lineNumber, lineInt)); - Interlocked.Increment(ref resultCount); + resultsDict[code.Name.Content].Add($"Line {line_number}: {lineInt}"); + Interlocked.Increment(ref result_count); } - lineNumber++; + line_number += 1; } } catch (Exception e) diff --git a/UndertaleModTool/Scripts/Builtin Scripts/SearchLimited.csx b/UndertaleModTool/Scripts/Builtin Scripts/SearchLimited.csx index 2de068b4b..316a55917 100644 --- a/UndertaleModTool/Scripts/Builtin Scripts/SearchLimited.csx +++ b/UndertaleModTool/Scripts/Builtin Scripts/SearchLimited.csx @@ -17,10 +17,10 @@ using System.Text.RegularExpressions; EnsureDataLoaded(); -int resultCount = 0; +int result_count = 0; StringBuilder results = new(); string searchNames = ""; -Dictionary> resultsDict = new(); +Dictionary> resultsDict = new(); List failedList = new(); List codeToDump = new(); List gameObjectCandidates = new(); @@ -32,8 +32,8 @@ if (Data.IsYYC()) return; } -bool caseSensitive = ScriptQuestion("Case sensitive?"); -bool regexCheck = ScriptQuestion("Regex search?"); +bool case_sensitive = ScriptQuestion("Case sensitive?"); +bool regex_check = ScriptQuestion("Regex search?"); string keyword = SimpleTextInput("Enter your search", "Search box below", "", false); if (String.IsNullOrEmpty(keyword) || String.IsNullOrWhiteSpace(keyword)) { @@ -48,9 +48,9 @@ if (String.IsNullOrEmpty(searchNames) || String.IsNullOrWhiteSpace(searchNames)) } Regex keywordRegex; -if (regexCheck) +if (regex_check) { - if (caseSensitive) + if (case_sensitive) keywordRegex = new(keyword, RegexOptions.Compiled); else keywordRegex = new(keyword, RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -107,8 +107,7 @@ for (var j = 0; j < gameObjectCandidates.Count; j++) SetProgressBar(null, "Code Entries", 0, codeToDump.Count); StartProgressBarUpdater(); -await Task.Run(() => -{ +await Task.Run(() => { for (var j = 0; j < codeToDump.Count; j++) { DumpCode(Data.Code.ByName(codeToDump[j])); @@ -118,7 +117,7 @@ await Task.Run(() => await StopProgressBarUpdater(); UpdateProgressStatus("Generating result list..."); -await ClickableSearchOutput("Search results.", keyword, resultCount, resultsDict, true, failedList); +await ClickableSearchOutput("Search results.", keyword, result_count, resultsDict, true, failedList); HideProgressBar(); EnableUI(); @@ -139,26 +138,26 @@ void DumpCode(UndertaleCode code) { if (code.ParentEntry is null) { - int lineNumber = 1; + var line_number = 1; StringReader decompiledText = new(code != null ? Decompiler.Decompile(code, DECOMPILE_CONTEXT.Value) : ""); - bool nameWritten = false; + bool name_written = false; string lineInt; while ((lineInt = decompiledText.ReadLine()) is not null) { if (lineInt == string.Empty) continue; - if (((regexCheck && RegexContains(in lineInt)) || ((!regexCheck && caseSensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) + if (((regex_check && RegexContains(in lineInt)) || ((!regex_check && case_sensitive) ? lineInt.Contains(keyword) : lineInt.ToLower().Contains(keyword.ToLower())))) { - if (nameWritten == false) + if (name_written == false) { - resultsDict.Add(code.Name.Content, new List<(int, string)>()); - nameWritten = true; + resultsDict.Add(code.Name.Content, new List()); + name_written = true; } - resultsDict[code.Name.Content].Add((lineNumber, lineInt)); - resultCount += 1; + resultsDict[code.Name.Content].Add($"Line {line_number}: {lineInt}"); + result_count += 1; } - lineNumber += 1; + line_number += 1; } } } diff --git a/UndertaleModTool/Tab.cs b/UndertaleModTool/Tab.cs index 9f4cc358b..5cc87fb0c 100644 --- a/UndertaleModTool/Tab.cs +++ b/UndertaleModTool/Tab.cs @@ -265,15 +265,8 @@ public static void SetTabTitleBinding(object obj, object prevObj, TextBlock text } /// Saves the current tab content state. - /// - /// Whether to overwrite the current tab content state if it's already set. - /// It's by default. - /// - public void SaveTabContentState(bool overwrite = false) + public void SaveTabContentState() { - if (!overwrite && LastContentState is not null) - return; - ContentControl dataEditor = mainWindow.DataEditor; if (dataEditor is null || dataEditor.Content is null @@ -910,9 +903,9 @@ public void PrepareCodeEditor() { #pragma warning disable CA1416 if (codeTabState.IsDecompiledOpen) - UndertaleCodeEditor.EditorTab = UndertaleCodeEditor.CodeEditorTab.Decompiled; + MainWindow.CodeEditorDecompile = MainWindow.CodeEditorMode.Decompile; else - UndertaleCodeEditor.EditorTab = UndertaleCodeEditor.CodeEditorTab.Disassembly; + MainWindow.CodeEditorDecompile = MainWindow.CodeEditorMode.DontDecompile; UndertaleCodeEditor.OverriddenDecompPos = codeTabState.DecompiledCodePosition; UndertaleCodeEditor.OverriddenDisasmPos = codeTabState.DisassemblyCodePosition; diff --git a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs index a3ab2c0a2..69aa7c0e9 100644 --- a/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs +++ b/UndertaleModTool/Windows/ClickableTextOutput.xaml.cs @@ -13,7 +13,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; -using static UndertaleModTool.UndertaleCodeEditor; +using static UndertaleModTool.MainWindow; namespace UndertaleModTool.Windows { @@ -28,13 +28,12 @@ public partial class ClickableTextOutput : Window public string Query { get; } public int ResultsCount { get; } - private readonly IDictionary> resultsDict; - private readonly IEnumerable failedList; - private readonly CodeEditorTab editorTab; + private IDictionary> resultsDict; + private IEnumerable failedList; + private CodeEditorMode editorDecompile; - public ClickableTextOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) + public ClickableTextOutput(string title, string query, int resultsCount, IOrderedEnumerable>> resultsDict, bool editorDecompile, IOrderedEnumerable failedList = null) { - #pragma warning disable CA1416 InitializeComponent(); linkContextMenu = FindResource("linkContextMenu") as ContextMenuDark; @@ -43,13 +42,11 @@ public ClickableTextOutput(string title, string query, int resultsCount, IOrdere Query = query; ResultsCount = resultsCount; this.resultsDict = resultsDict.ToDictionary(x => x.Key, x => x.Value); - this.editorTab = editorDecompile ? CodeEditorTab.Decompiled : CodeEditorTab.Disassembly; + this.editorDecompile = editorDecompile ? CodeEditorMode.Decompile : CodeEditorMode.DontDecompile; this.failedList = failedList?.ToList(); - #pragma warning restore CA1416 } - public ClickableTextOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) + public ClickableTextOutput(string title, string query, int resultsCount, IDictionary> resultsDict, bool editorDecompile, IEnumerable failedList = null) { - #pragma warning disable CA1416 InitializeComponent(); linkContextMenu = FindResource("linkContextMenu") as ContextMenuDark; @@ -58,9 +55,8 @@ public ClickableTextOutput(string title, string query, int resultsCount, IDictio Query = query; ResultsCount = resultsCount; this.resultsDict = resultsDict; - this.editorTab = editorDecompile ? CodeEditorTab.Decompiled : CodeEditorTab.Disassembly; + this.editorDecompile = editorDecompile ? CodeEditorMode.Decompile : CodeEditorMode.DontDecompile; this.failedList = failedList; - #pragma warning restore CA1416 } private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { @@ -135,12 +131,7 @@ public void GenerateResults() headerPara.Inlines.Add(new LineBreak()); doc.Blocks.Add(headerPara); - int totalLineCount = resultsDict.Select(x => x.Value.Count).Sum(); - bool tooManyLines = totalLineCount > 10000; - if (tooManyLines) - mainWindow.ShowWarning($"There are too many code lines to display ({totalLineCount}), so there would be no clickable line numbers."); - - foreach (KeyValuePair> result in resultsDict) + foreach (KeyValuePair> result in resultsDict) { int lineCount = result.Value.Count; Paragraph resPara = new(); @@ -153,24 +144,9 @@ public void GenerateResults() resPara.Inlines.Add(resHeader); int i = 1; - foreach (var (lineNum, codeLine) in result.Value) + foreach (string line in result.Value) { - if (!tooManyLines) - { - Hyperlink lineLink = new(new Run($"Line {lineNum}") - { - Tag = result.Key // code entry name - }); - - resPara.Inlines.Add(lineLink); - resPara.Inlines.Add(new Run($": {codeLine}")); - } - else - { - Run lineRun = new($"Line {lineNum}: {codeLine}"); - - resPara.Inlines.Add(lineRun); - } + resPara.Inlines.Add(new Run(line)); if (i < lineCount) resPara.Inlines.Add(new LineBreak()); @@ -221,54 +197,27 @@ private void OutTextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) { if (mainWindow is null) return; - if (e.OriginalSource is not Run linkRun || linkRun.Parent is not Hyperlink - || String.IsNullOrEmpty(linkRun.Text)) + if (e.OriginalSource is not Run linkRun || linkRun.Parent is not Hyperlink) return; - if (linkRun.Text.StartsWith("Line ")) + string codeName = linkRun.Text; + if (e.ChangedButton == MouseButton.Right && linkContextMenu is not null) { - if (!Int32.TryParse(linkRun.Text[5..], out int lineNum)) - { - e.Handled = true; - return; - } - - string codeName = linkRun.Tag as string; - if (String.IsNullOrEmpty(codeName)) - { - e.Handled = true; - return; - } - - if (e.ChangedButton == MouseButton.Right && linkContextMenu is not null) - { - linkContextMenu.DataContext = (lineNum, codeName); - linkContextMenu.IsOpen = true; - } - else - mainWindow.OpenCodeEntry(codeName, lineNum, editorTab, e.ChangedButton == MouseButton.Middle); + linkContextMenu.DataContext = codeName; + linkContextMenu.IsOpen = true; } else - { - string codeName = linkRun.Text; - if (e.ChangedButton == MouseButton.Right && linkContextMenu is not null) - { - linkContextMenu.DataContext = (1, codeName); - linkContextMenu.IsOpen = true; - } - else - mainWindow.OpenCodeEntry(codeName, editorTab, e.ChangedButton == MouseButton.Middle); - } + mainWindow.OpenCodeFile(codeName, editorDecompile, e.ChangedButton == MouseButton.Middle); e.Handled = true; } private void OpenInNewTabItem_Click(object sender, RoutedEventArgs e) { - if ((sender as FrameworkElement)?.DataContext is not ValueTuple codeNamePair - || String.IsNullOrEmpty(codeNamePair.Item2)) + string codeName = (sender as FrameworkElement)?.DataContext as string; + if (String.IsNullOrEmpty(codeName)) return; - mainWindow.OpenCodeEntry(codeNamePair.Item2, codeNamePair.Item1, editorTab, true); + mainWindow.OpenCodeFile(codeName, editorDecompile, true); } private void Button_Click(object sender, RoutedEventArgs e)