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