1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Drawing; 5 using System.Data; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 using System.Collections; 11 using Mesen.GUI.Controls; 12 using Mesen.GUI.Config; 13 using Mesen.GUI.Forms; 14 15 namespace Mesen.GUI.Debugger.Controls 16 { 17 public partial class ctrlLabelList : BaseControl 18 { 19 public event EventHandler OnFindOccurrence; 20 public event GoToDestinationEventHandler OnLabelSelected; 21 22 private List<ListViewItem> _listItems = new List<ListViewItem>(); 23 private int _sortColumn = 0; 24 private bool _descSort = false; 25 ctrlLabelList()26 public ctrlLabelList() 27 { 28 InitializeComponent(); 29 } 30 OnLoad(EventArgs e)31 protected override void OnLoad(EventArgs e) 32 { 33 base.OnLoad(e); 34 if(!IsDesignMode) { 35 mnuShowComments.Checked = ConfigManager.Config.DebugInfo.ShowCommentsInLabelList; 36 mnuShowJumpLabels.Checked = ConfigManager.Config.DebugInfo.ShowJumpLabels; 37 InitShortcuts(); 38 } 39 } 40 InitShortcuts()41 private void InitShortcuts() 42 { 43 mnuAdd.InitShortcut(this, nameof(DebuggerShortcutsConfig.LabelList_Add)); 44 mnuEdit.InitShortcut(this, nameof(DebuggerShortcutsConfig.LabelList_Edit)); 45 mnuDelete.InitShortcut(this, nameof(DebuggerShortcutsConfig.LabelList_Delete)); 46 47 mnuAddToWatch.InitShortcut(this, nameof(DebuggerShortcutsConfig.LabelList_AddToWatch)); 48 mnuAddBreakpoint.InitShortcut(this, nameof(DebuggerShortcutsConfig.LabelList_AddBreakpoint)); 49 mnuFindOccurrences.InitShortcut(this, nameof(DebuggerShortcutsConfig.LabelList_FindOccurrences)); 50 51 mnuViewInCpuMemory.InitShortcut(this, nameof(DebuggerShortcutsConfig.LabelList_ViewInCpuMemory)); 52 mnuViewInMemoryType.InitShortcut(this, nameof(DebuggerShortcutsConfig.LabelList_ViewInMemoryType)); 53 } 54 EditLabel(UInt32 address, AddressType type)55 public static void EditLabel(UInt32 address, AddressType type) 56 { 57 CodeLabel existingLabel = LabelManager.GetLabel(address, type); 58 CodeLabel newLabel = new CodeLabel() { Address = address, AddressType = type, Label = existingLabel?.Label, Comment = existingLabel?.Comment, Length = existingLabel?.Length ?? 1 }; 59 60 frmEditLabel frm = new frmEditLabel(newLabel, existingLabel); 61 if(frm.ShowDialog() == DialogResult.OK) { 62 bool empty = string.IsNullOrWhiteSpace(newLabel.Label) && string.IsNullOrWhiteSpace(newLabel.Comment); 63 if(existingLabel != null) { 64 LabelManager.DeleteLabel(existingLabel, empty); 65 } 66 if(!empty) { 67 LabelManager.SetLabel(newLabel.Address, newLabel.AddressType, newLabel.Label, newLabel.Comment, true, CodeLabelFlags.None, newLabel.Length); 68 } 69 } 70 } 71 CompareLabels(ListViewItem x, ListViewItem y)72 public int CompareLabels(ListViewItem x, ListViewItem y) 73 { 74 int result = String.Compare(((ListViewItem)x).SubItems[_sortColumn].Text, ((ListViewItem)y).SubItems[_sortColumn].Text); 75 if(result == 0 && (_sortColumn == 0 || _sortColumn == 3)) { 76 result = String.Compare(((ListViewItem)x).SubItems[2].Text, ((ListViewItem)y).SubItems[2].Text); 77 } 78 return result * (_descSort ? -1 : 1); 79 } 80 SortItems()81 private void SortItems() 82 { 83 _listItems.Sort(CompareLabels); 84 } 85 UpdateLabelListAddresses()86 public void UpdateLabelListAddresses() 87 { 88 bool needUpdate = false; 89 Font italicFont = null; 90 Font regularFont = null; 91 92 foreach(ListViewItem item in _listItems) { 93 CodeLabel label = (CodeLabel)item.SubItems[1].Tag; 94 95 Int32 relativeAddress = InteropEmu.DebugGetRelativeAddress(label.Address, label.AddressType); 96 if(relativeAddress != (Int32)item.Tag) { 97 needUpdate = true; 98 if(relativeAddress >= 0) { 99 item.SubItems[1].Text = "$" + relativeAddress.ToString("X4"); 100 item.ForeColor = ThemeHelper.Theme.LabelForeColor; 101 if(regularFont == null) { 102 regularFont = new Font(item.Font, FontStyle.Regular); 103 } 104 item.Font = regularFont; 105 } else { 106 item.SubItems[1].Text = "[n/a]"; 107 item.ForeColor = ThemeHelper.Theme.LabelDisabledForeColor; 108 if(italicFont == null) { 109 italicFont = new Font(item.Font, FontStyle.Italic); 110 } 111 item.Font = italicFont; 112 } 113 item.Tag = relativeAddress; 114 } 115 } 116 if(needUpdate) { 117 SortItems(); 118 } 119 } 120 UpdateLabelList()121 public void UpdateLabelList() 122 { 123 List<CodeLabel> labels = LabelManager.GetLabels(); 124 List<ListViewItem> items = new List<ListViewItem>(labels.Count); 125 Font italicFont = null; 126 foreach(CodeLabel label in labels) { 127 if((label.Label.Length > 0 || ConfigManager.Config.DebugInfo.ShowCommentsInLabelList) && (!label.Flags.HasFlag(CodeLabelFlags.AutoJumpLabel) || ConfigManager.Config.DebugInfo.ShowJumpLabels)) { 128 ListViewItem item = new ListViewItem(label.Label); 129 130 Int32 relativeAddress = label.GetRelativeAddress(); 131 if(relativeAddress >= 0) { 132 item.SubItems.Add("$" + relativeAddress.ToString("X4")); 133 } else { 134 item.SubItems.Add("[n/a]"); 135 item.ForeColor = ThemeHelper.Theme.LabelDisabledForeColor; 136 if(italicFont == null) { 137 italicFont = new Font(item.Font, FontStyle.Italic); 138 } 139 item.Font = italicFont; 140 } 141 string prefix = string.Empty; 142 switch(label.AddressType) { 143 case AddressType.InternalRam: prefix = "RAM: $"; break; 144 case AddressType.PrgRom: prefix = "PRG: $"; break; 145 case AddressType.Register: prefix = "REG: $"; break; 146 case AddressType.SaveRam: prefix = "SRAM: $"; break; 147 case AddressType.WorkRam: prefix = "WRAM: $"; break; 148 } 149 item.SubItems.Add(prefix + label.Address.ToString("X4")); 150 item.SubItems.Add(ConfigManager.Config.DebugInfo.ShowCommentsInLabelList ? label.Comment : ""); 151 item.SubItems[1].Tag = label; 152 153 item.Tag = relativeAddress; 154 items.Add(item); 155 } 156 } 157 158 _listItems = items; 159 SortItems(); 160 161 lstLabels.BeginUpdate(); 162 lstLabels.VirtualMode = true; 163 lstLabels.VirtualListSize = items.Count; 164 lstLabels.EndUpdate(); 165 166 colComment.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent); 167 if(!ConfigManager.Config.DebugInfo.ShowCommentsInLabelList) { 168 colComment.Width = 0; 169 } 170 } 171 GetSelectedItem()172 private ListViewItem GetSelectedItem() 173 { 174 return _listItems[lstLabels.SelectedIndices[0]]; 175 } 176 lstLabels_DoubleClick(object sender, EventArgs e)177 private void lstLabels_DoubleClick(object sender, EventArgs e) 178 { 179 if(lstLabels.SelectedIndices.Count > 0) { 180 Int32 relativeAddress = (Int32)GetSelectedItem().Tag; 181 CodeLabel label = (CodeLabel)GetSelectedItem().SubItems[1].Tag; 182 OnLabelSelected?.Invoke(new GoToDestination() { 183 CpuAddress = relativeAddress, 184 Label = label 185 }); 186 } 187 } 188 lstLabels_SelectedIndexChanged(object sender, EventArgs e)189 private void lstLabels_SelectedIndexChanged(object sender, EventArgs e) 190 { 191 mnuDelete.Enabled = lstLabels.SelectedIndices.Count > 0; 192 mnuEdit.Enabled = lstLabels.SelectedIndices.Count == 1; 193 mnuFindOccurrences.Enabled = lstLabels.SelectedIndices.Count == 1; 194 mnuAddToWatch.Enabled = lstLabels.SelectedIndices.Count == 1; 195 mnuAddBreakpoint.Enabled = lstLabels.SelectedIndices.Count == 1; 196 197 if(lstLabels.SelectedIndices.Count == 1) { 198 ListViewItem item = GetSelectedItem(); 199 200 bool availableInCpuMemory = (int)item.Tag >= 0; 201 mnuViewInCpuMemory.Enabled = availableInCpuMemory; 202 203 CodeLabel label = (CodeLabel)item.SubItems[1].Tag; 204 if(label.AddressType != AddressType.Register && label.AddressType != AddressType.InternalRam) { 205 mnuViewInMemoryType.Text = "View in " + ResourceHelper.GetEnumText(label.AddressType); 206 mnuViewInMemoryType.Visible = true; 207 } else { 208 mnuViewInMemoryType.Visible = false; 209 } 210 } else { 211 mnuViewInCpuMemory.Enabled = false; 212 mnuViewInMemoryType.Visible = false; 213 } 214 } 215 mnuDelete_Click(object sender, EventArgs e)216 private void mnuDelete_Click(object sender, EventArgs e) 217 { 218 if(lstLabels.SelectedIndices.Count > 0) { 219 int topIndex = lstLabels.TopItem.Index; 220 int lastSelectedIndex = lstLabels.SelectedIndices[lstLabels.SelectedIndices.Count - 1]; 221 List<int> selectedIndexes = new List<int>(lstLabels.SelectedIndices.Cast<int>().ToList()); 222 for(int i = selectedIndexes.Count - 1; i >= 0; i--) { 223 CodeLabel label = (CodeLabel)_listItems[selectedIndexes[i]].SubItems[1].Tag; 224 LabelManager.DeleteLabel(label, i == 0); 225 } 226 227 //Reposition scroll bar and selected/focused item 228 if(lstLabels.Items.Count > topIndex) { 229 lstLabels.TopItem = lstLabels.Items[topIndex]; 230 } 231 if(lastSelectedIndex < lstLabels.Items.Count) { 232 lstLabels.Items[lastSelectedIndex].Selected = true; 233 } else if(lstLabels.Items.Count > 0) { 234 lstLabels.Items[lstLabels.Items.Count - 1].Selected = true; 235 } 236 if(lstLabels.SelectedIndices.Count > 0) { 237 GetSelectedItem().Focused = true; 238 } 239 } 240 } 241 mnuAdd_Click(object sender, EventArgs e)242 private void mnuAdd_Click(object sender, EventArgs e) 243 { 244 CodeLabel newLabel = new CodeLabel() { Address = 0, AddressType = AddressType.InternalRam, Label = "", Comment = "" }; 245 246 frmEditLabel frm = new frmEditLabel(newLabel); 247 if(frm.ShowDialog() == DialogResult.OK) { 248 LabelManager.SetLabel(newLabel.Address, newLabel.AddressType, newLabel.Label, newLabel.Comment, true, CodeLabelFlags.None, newLabel.Length); 249 } 250 } 251 mnuEdit_Click(object sender, EventArgs e)252 private void mnuEdit_Click(object sender, EventArgs e) 253 { 254 if(lstLabels.SelectedIndices.Count > 0) { 255 CodeLabel label = (CodeLabel)GetSelectedItem().SubItems[1].Tag; 256 EditLabel(label.Address, label.AddressType); 257 } 258 } 259 mnuFindOccurrences_Click(object sender, EventArgs e)260 private void mnuFindOccurrences_Click(object sender, EventArgs e) 261 { 262 OnFindOccurrence?.Invoke(GetSelectedItem().SubItems[1].Tag, null); 263 } 264 lstLabels_ColumnClick(object sender, ColumnClickEventArgs e)265 private void lstLabels_ColumnClick(object sender, ColumnClickEventArgs e) 266 { 267 lstLabels.BeginUpdate(); 268 if(_sortColumn == e.Column) { 269 _descSort = !_descSort; 270 } 271 _sortColumn = e.Column; 272 SortItems(); 273 lstLabels.EndUpdate(); 274 } 275 mnuAddBreakpoint_Click(object sender, EventArgs e)276 private void mnuAddBreakpoint_Click(object sender, EventArgs e) 277 { 278 if(lstLabels.SelectedIndices.Count > 0) { 279 CodeLabel label = (CodeLabel)GetSelectedItem().SubItems[1].Tag; 280 if(label.AddressType == AddressType.InternalRam || label.AddressType == AddressType.Register) { 281 AddressTypeInfo info = new AddressTypeInfo(); 282 InteropEmu.DebugGetAbsoluteAddressAndType(label.Address, info); 283 if(BreakpointManager.GetMatchingBreakpoint((Int32)label.Address, info) == null) { 284 BreakpointManager.AddBreakpoint(new Breakpoint() { 285 MemoryType = DebugMemoryType.CpuMemory, 286 BreakOnExec = true, 287 BreakOnRead = true, 288 BreakOnWrite = true, 289 Address = label.Address, 290 StartAddress = label.Address, 291 EndAddress = label.Address, 292 AddressType = BreakpointAddressType.SingleAddress 293 }); 294 } 295 } else { 296 BreakpointManager.AddBreakpoint(new Breakpoint() { 297 MemoryType = DebugMemoryType.PrgRom, 298 BreakOnExec = true, 299 BreakOnRead = true, 300 BreakOnWrite = false, 301 Address = label.Address, 302 StartAddress = label.Address, 303 EndAddress = label.Address, 304 AddressType = BreakpointAddressType.SingleAddress 305 }); 306 } 307 } 308 } 309 mnuAddToWatch_Click(object sender, EventArgs e)310 private void mnuAddToWatch_Click(object sender, EventArgs e) 311 { 312 if(lstLabels.SelectedIndices.Count > 0) { 313 CodeLabel label = (CodeLabel)GetSelectedItem().SubItems[1].Tag; 314 WatchManager.AddWatch("[" + label.Label + "]"); 315 } 316 } 317 mnuShowComments_Click(object sender, EventArgs e)318 private void mnuShowComments_Click(object sender, EventArgs e) 319 { 320 ConfigManager.Config.DebugInfo.ShowCommentsInLabelList = mnuShowComments.Checked; 321 ConfigManager.ApplyChanges(); 322 this.UpdateLabelList(); 323 } 324 mnuShowJumpLabels_Click(object sender, EventArgs e)325 private void mnuShowJumpLabels_Click(object sender, EventArgs e) 326 { 327 ConfigManager.Config.DebugInfo.ShowJumpLabels = mnuShowJumpLabels.Checked; 328 ConfigManager.ApplyChanges(); 329 this.UpdateLabelList(); 330 } 331 lstLabels_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)332 private void lstLabels_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) 333 { 334 e.Item = _listItems[e.ItemIndex]; 335 } 336 lstLabels_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e)337 private void lstLabels_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e) 338 { 339 for(int i = 0; i < _listItems.Count; i++) { 340 if(_listItems[i].Text.StartsWith(e.Text, StringComparison.InvariantCultureIgnoreCase)) { 341 e.Index = i; 342 return; 343 } 344 } 345 } 346 mnuViewInCpuMemory_Click(object sender, EventArgs e)347 private void mnuViewInCpuMemory_Click(object sender, EventArgs e) 348 { 349 if(lstLabels.SelectedIndices.Count == 1) { 350 ListViewItem item = GetSelectedItem(); 351 int address = (int)item.Tag; 352 if(address >= 0) { 353 DebugWindowManager.OpenMemoryViewer(address, DebugMemoryType.CpuMemory); 354 } 355 } 356 } 357 mnuViewInMemoryType_Click(object sender, EventArgs e)358 private void mnuViewInMemoryType_Click(object sender, EventArgs e) 359 { 360 if(lstLabels.SelectedIndices.Count == 1) { 361 ListViewItem item = GetSelectedItem(); 362 CodeLabel label = (CodeLabel)item.SubItems[1].Tag; 363 if(label.AddressType != AddressType.Register && label.AddressType != AddressType.InternalRam) { 364 DebugWindowManager.OpenMemoryViewer((int)label.Address, label.AddressType.ToMemoryType()); 365 } 366 } 367 } 368 } 369 } 370