1 using Mesen.GUI.Debugger.Controls; 2 using Mesen.GUI.Forms; 3 using System; 4 using System.Collections.Generic; 5 using System.ComponentModel; 6 using System.Data; 7 using System.Drawing; 8 using System.IO; 9 using System.Linq; 10 using System.Text; 11 using System.Threading.Tasks; 12 using System.Windows.Forms; 13 14 namespace Mesen.GUI.Debugger 15 { 16 public partial class frmGoToAll : BaseForm 17 { 18 private const int MaxResultCount = 30; 19 20 private List<ctrlSearchResult> _results = new List<ctrlSearchResult>(); 21 private int _selectedResult = 0; 22 private int _resultCount = 0; 23 private Ld65DbgImporter _symbolProvider; 24 private bool _allowOutOfScope; 25 private bool _showFilesAndConstants; 26 27 public GoToDestination Destination { get; private set; } 28 frmGoToAll(bool allowOutOfScope, bool showFilesAndConstants)29 public frmGoToAll(bool allowOutOfScope, bool showFilesAndConstants) 30 { 31 InitializeComponent(); 32 33 Icon = Properties.Resources.Find; 34 _symbolProvider = DebugWorkspaceManager.SymbolProvider; 35 _allowOutOfScope = allowOutOfScope; 36 _showFilesAndConstants = showFilesAndConstants; 37 38 tlpResults.SuspendLayout(); 39 for(int i = 0; i < MaxResultCount; i++) { 40 ctrlSearchResult searchResult = new ctrlSearchResult(); 41 searchResult.Dock = DockStyle.Top; 42 searchResult.BackColor = i % 2 == 0 ? SystemColors.ControlLight : SystemColors.ControlLightLight; 43 searchResult.Visible = false; 44 searchResult.Click += SearchResult_Click; 45 searchResult.DoubleClick += SearchResult_DoubleClick; 46 tlpResults.Controls.Add(searchResult, 0, i); 47 tlpResults.RowStyles.Add(new RowStyle(SizeType.AutoSize)); 48 49 _results.Add(searchResult); 50 } 51 tlpResults.ResumeLayout(); 52 53 UpdateResults(); 54 } 55 SearchResult_Click(object sender, EventArgs e)56 private void SearchResult_Click(object sender, EventArgs e) 57 { 58 SelectedResult = _results.IndexOf(sender as ctrlSearchResult); 59 } 60 SearchResult_DoubleClick(object sender, EventArgs e)61 private void SearchResult_DoubleClick(object sender, EventArgs e) 62 { 63 SelectedResult = _results.IndexOf(sender as ctrlSearchResult); 64 SelectAndClose(); 65 } 66 ProcessCmdKey(ref Message msg, Keys keyData)67 protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 68 { 69 if(keyData == Keys.Up) { 70 SelectedResult--; 71 return true; 72 } else if(keyData == Keys.Down) { 73 SelectedResult++; 74 return true; 75 } else if(keyData == Keys.PageUp) { 76 SelectedResult -= pnlResults.ClientSize.Height / _results[0].Height; 77 return true; 78 } else if(keyData == Keys.PageDown) { 79 SelectedResult += pnlResults.ClientSize.Height / _results[0].Height; 80 return true; 81 } else if(keyData == Keys.Enter) { 82 SelectAndClose(); 83 } else if(keyData == Keys.Escape) { 84 Close(); 85 } 86 87 return base.ProcessCmdKey(ref msg, keyData); 88 } 89 90 private int SelectedResult 91 { 92 get { return _selectedResult; } 93 set 94 { 95 //Reset currently highlighted element's color 96 _results[_selectedResult].BackColor = _selectedResult % 2 == 0 ? SystemColors.ControlLight : SystemColors.ControlLightLight; 97 98 _selectedResult = Math.Max(0, Math.Min(_resultCount - 1, value)); 99 if(_resultCount == 0) { 100 _results[0].BackColor = SystemColors.ControlLight; 101 } else { 102 _results[_selectedResult].BackColor = Color.LightBlue; 103 } 104 105 if(_resultCount > 0) { 106 if(Program.IsMono) { 107 //Use this logic to replace ScrollControlIntoView (which doesn't work properly on Mono) 108 int startPos = (_results[0].Height + 1) * _selectedResult; 109 int endPos = startPos + _results[0].Height + 1; 110 111 int minVisiblePos = pnlResults.VerticalScroll.Value; 112 int maxVisiblePos = pnlResults.Height + pnlResults.VerticalScroll.Value; 113 114 if(startPos < minVisiblePos) { 115 pnlResults.VerticalScroll.Value = startPos; 116 } else if(endPos > maxVisiblePos) { 117 pnlResults.VerticalScroll.Value = endPos - pnlResults.Height; 118 } 119 } else { 120 pnlResults.ScrollControlIntoView(_results[_selectedResult]); 121 } 122 } 123 } 124 } 125 Contains(string label, List<string> searchStrings)126 private bool Contains(string label, List<string> searchStrings) 127 { 128 label = label.ToLower(); 129 if(searchStrings.Count == 1) { 130 return label.Contains(searchStrings[0]); 131 } else { 132 for(int i = 1; i < searchStrings.Count; i++) { 133 if(!label.Contains(searchStrings[i])) { 134 return false; 135 } 136 } 137 return true; 138 } 139 } 140 UpdateResults()141 private void UpdateResults() 142 { 143 string searchString = txtSearch.Text.Trim(); 144 145 List<string> searchStrings = new List<string>(); 146 searchStrings.Add(searchString.ToLower()); 147 searchStrings.AddRange(searchString.ToLower().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); 148 for(int i = 0; i < searchString.Length; i++) { 149 char ch = searchString[i]; 150 if(ch >= 'A' && ch <= 'Z') { 151 searchString = searchString.Remove(i, 1).Insert(i, " " + (char)(ch + 'a' - 'A')); 152 } 153 } 154 searchStrings.AddRange(searchString.ToLower().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); 155 searchStrings = searchStrings.Distinct().ToList(); 156 157 _resultCount = 0; 158 159 HashSet<int> entryPoints = new HashSet<int>(InteropEmu.DebugGetFunctionEntryPoints()); 160 byte[] cdlData = InteropEmu.DebugGetPrgCdlData(); 161 162 List<SearchResultInfo> searchResults = new List<SearchResultInfo>(); 163 bool isEmptySearch = string.IsNullOrWhiteSpace(searchString); 164 if(!isEmptySearch) { 165 if(_symbolProvider != null) { 166 if(_showFilesAndConstants) { 167 foreach(Ld65DbgImporter.FileInfo file in _symbolProvider.Files.Values) { 168 if(Contains(file.Name, searchStrings)) { 169 searchResults.Add(new SearchResultInfo() { 170 Caption = Path.GetFileName(file.Name), 171 AbsoluteAddress = -1, 172 MemoryType = AddressType.InternalRam, 173 SearchResultType = SearchResultType.File, 174 Filename = file.Name, 175 FileLineNumber = 0, 176 RelativeAddress = -1, 177 CodeLabel = null 178 }); 179 } 180 } 181 } 182 183 foreach(Ld65DbgImporter.SymbolInfo symbol in _symbolProvider.GetSymbols()) { 184 if(Contains(symbol.Name, searchStrings)) { 185 Ld65DbgImporter.ReferenceInfo def = _symbolProvider.GetSymbolDefinition(symbol); 186 AddressTypeInfo addressInfo = _symbolProvider.GetSymbolAddressInfo(symbol); 187 int value = 0; 188 int relAddress = -1; 189 bool isConstant = addressInfo == null; 190 if(!_showFilesAndConstants && isConstant) { 191 continue; 192 } 193 194 if(addressInfo != null) { 195 value = InteropEmu.DebugGetMemoryValue(addressInfo.Type.ToMemoryType(), (uint)addressInfo.Address); 196 relAddress = InteropEmu.DebugGetRelativeAddress((uint)addressInfo.Address, addressInfo.Type); 197 } else { 198 //For constants, the address field contains the constant's value 199 value = symbol.Address ?? 0; 200 } 201 202 SearchResultType resultType = SearchResultType.Data; 203 if(addressInfo?.Type == AddressType.PrgRom && entryPoints.Contains(addressInfo.Address)) { 204 resultType = SearchResultType.Function; 205 } else if(addressInfo?.Type == AddressType.PrgRom && addressInfo.Address < cdlData.Length && (cdlData[addressInfo.Address] & (byte)CdlPrgFlags.JumpTarget) != 0) { 206 resultType = SearchResultType.JumpTarget; 207 } else if(isConstant) { 208 resultType = SearchResultType.Constant; 209 } 210 211 searchResults.Add(new SearchResultInfo() { 212 Caption = symbol.Name, 213 AbsoluteAddress = addressInfo?.Address ?? -1, 214 Length = _symbolProvider.GetSymbolSize(symbol), 215 MemoryType = addressInfo?.Type ?? AddressType.InternalRam, 216 SearchResultType = resultType, 217 Value = value, 218 Filename = def?.FileName ?? "", 219 FileLineNumber = def?.LineNumber ?? 0, 220 RelativeAddress = relAddress, 221 CodeLabel = LabelManager.GetLabel(symbol.Name) 222 }); 223 } 224 } 225 } else { 226 foreach(CodeLabel label in LabelManager.GetLabels()) { 227 if(Contains(label.Label, searchStrings)) { 228 SearchResultType resultType = SearchResultType.Data; 229 if(label.AddressType == AddressType.PrgRom && entryPoints.Contains((int)label.Address)) { 230 resultType = SearchResultType.Function; 231 } else if(label.AddressType == AddressType.PrgRom && label.Address < cdlData.Length && (cdlData[label.Address] & (byte)CdlPrgFlags.JumpTarget) != 0) { 232 resultType = SearchResultType.JumpTarget; 233 } 234 235 int relativeAddress = label.GetRelativeAddress(); 236 237 searchResults.Add(new SearchResultInfo() { 238 Caption = label.Label, 239 AbsoluteAddress = (int)label.Address, 240 Length = (int)label.Length, 241 Value = label.GetValue(), 242 MemoryType = label.AddressType, 243 SearchResultType = resultType, 244 Filename = "", 245 Disabled = !_allowOutOfScope && relativeAddress < 0, 246 RelativeAddress = relativeAddress, 247 CodeLabel = label 248 }); 249 } 250 } 251 } 252 } 253 254 searchResults.Sort((SearchResultInfo a, SearchResultInfo b) => { 255 int comparison = a.Disabled.CompareTo(b.Disabled); 256 257 if(comparison == 0) { 258 bool aStartsWithSearch = a.Caption.StartsWith(searchString, StringComparison.InvariantCultureIgnoreCase); 259 bool bStartsWithSearch = b.Caption.StartsWith(searchString, StringComparison.InvariantCultureIgnoreCase); 260 261 comparison = bStartsWithSearch.CompareTo(aStartsWithSearch); 262 if(comparison == 0) { 263 comparison = a.Caption.CompareTo(b.Caption); 264 } 265 } 266 return comparison; 267 }); 268 269 _resultCount = Math.Min(searchResults.Count, MaxResultCount); 270 SelectedResult = 0; 271 272 lblResultCount.Visible = !isEmptySearch; 273 lblResultCount.Text = searchResults.Count.ToString() + (searchResults.Count == 1 ? " result" : " results"); 274 if(searchResults.Count > MaxResultCount) { 275 lblResultCount.Text += " (" + MaxResultCount.ToString() + " shown)"; 276 } 277 278 if(searchResults.Count == 0 && !isEmptySearch) { 279 _resultCount++; 280 searchResults.Add(new SearchResultInfo() { Caption = "No results found.", AbsoluteAddress = -1 }); 281 pnlResults.BackColor = SystemColors.ControlLight; 282 } else { 283 pnlResults.BackColor = SystemColors.ControlDarkDark; 284 } 285 286 if(Program.IsMono) { 287 pnlResults.Visible = false; 288 } else { 289 //Suspend layout causes a crash on Mono 290 tlpResults.SuspendLayout(); 291 } 292 293 for(int i = 0; i < _resultCount; i++) { 294 _results[i].Initialize(searchResults[i]); 295 _results[i].Tag = searchResults[i]; 296 _results[i].Visible = true; 297 } 298 299 for(int i = _resultCount; i < MaxResultCount; i++) { 300 _results[i].Visible = false; 301 } 302 303 pnlResults.VerticalScroll.Value = 0; 304 tlpResults.Height = (_results[0].Height + 1) * _resultCount; 305 306 pnlResults.ResumeLayout(); 307 if(Program.IsMono) { 308 pnlResults.Visible = true; 309 tlpResults.Width = pnlResults.ClientSize.Width - 17; 310 } else { 311 tlpResults.ResumeLayout(); 312 tlpResults.Width = pnlResults.ClientSize.Width - 1; 313 } 314 } 315 txtSearch_TextChanged(object sender, EventArgs e)316 private void txtSearch_TextChanged(object sender, EventArgs e) 317 { 318 UpdateResults(); 319 } 320 SelectAndClose()321 private void SelectAndClose() 322 { 323 if(_resultCount > 0) { 324 SearchResultInfo searchResult = _results[_selectedResult].Tag as SearchResultInfo; 325 if(!searchResult.Disabled) { 326 AddressTypeInfo addressInfo = new AddressTypeInfo() { Address = searchResult.AbsoluteAddress, Type = searchResult.MemoryType }; 327 Destination = new GoToDestination() { 328 AddressInfo = addressInfo, 329 CpuAddress = addressInfo.Address >= 0 ? InteropEmu.DebugGetRelativeAddress((UInt32)addressInfo.Address, addressInfo.Type) : -1, 330 Label = searchResult.CodeLabel, 331 File = searchResult.Filename, 332 Line = searchResult.FileLineNumber 333 }; 334 DialogResult = DialogResult.OK; 335 Close(); 336 } 337 } 338 } 339 } 340 GoToDestinationEventHandler(GoToDestination dest)341 public delegate void GoToDestinationEventHandler(GoToDestination dest); 342 343 public class GoToDestination 344 { 345 public CodeLabel Label; 346 public AddressTypeInfo AddressInfo; 347 public int CpuAddress = -1; 348 public string File; 349 public int Line; 350 } 351 } 352