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 Mesen.GUI.Config;
11 using Mesen.GUI.Controls;
12 
13 namespace Mesen.GUI.Forms.Cheats
14 {
15 	public partial class ctrlCheatFinder : BaseControl
16 	{
17 		public event EventHandler OnAddCheat;
18 		public bool TabIsFocused
19 		{
20 			set
21 			{
22 				_tabIsFocused = value;
23 				if(chkPauseGameWhileWindowActive.Checked) {
24 					if(_tabIsFocused) {
25 						InteropEmu.Pause();
26 					} else {
27 						InteropEmu.Resume();
28 					}
29 				}
30 			}
31 		}
32 
33 		private List<byte[]> _memorySnapshots;
34 		private List<FilterInfo> _filters;
35 		private bool _tabIsFocused = false;
36 
37 		private enum CheatPrevFilterType
38 		{
39 			Smaller,
40 			Equal,
41 			NotEqual,
42 			Greater
43 		}
44 
45 		private enum CheatCurrentFilterType
46 		{
47 			Smaller,
48 			Equal,
49 			NotEqual,
50 			Greater
51 		}
52 
53 		private class FilterInfo
54 		{
55 			public CheatCurrentFilterType? CurrentType;
56 			public CheatPrevFilterType? PrevType;
57 			public int Operand;
58 		}
59 
ctrlCheatFinder()60 		public ctrlCheatFinder()
61 		{
62 			InitializeComponent();
63 
64 			BaseConfigForm.InitializeComboBox(cboPrevFilterType, typeof(CheatPrevFilterType));
65 			BaseConfigForm.InitializeComboBox(cboCurrentFilterType, typeof(CheatCurrentFilterType));
66 			cboPrevFilterType.SelectedIndex = 0;
67 			cboCurrentFilterType.SelectedIndex = 0;
68 
69 			btnUndo.Enabled = false;
70 
71 			if(LicenseManager.UsageMode != LicenseUsageMode.Designtime) {
72 				Reset();
73 				tmrRefresh.Start();
74 			}
75 		}
76 
Reset()77 		private void Reset()
78 		{
79 			_filters = new List<FilterInfo>();
80 			_memorySnapshots = new List<byte[]>();
81 			TakeSnapshot();
82 		}
83 
TakeSnapshot()84 		private void TakeSnapshot()
85 		{
86 			if(!InteropEmu.IsRunning()) {
87 				return;
88 			}
89 
90 			byte[] memory = GetSnapshot();
91 			_memorySnapshots.Add(memory);
92 			RefreshAddressList();
93 
94 			UpdateUI();
95 		}
96 
GetSnapshot()97 		private byte[] GetSnapshot()
98 		{
99 			bool release = !InteropEmu.DebugIsDebuggerRunning();
100 			byte[] memory = InteropEmu.DebugGetInternalRam();
101 			if(release) {
102 				InteropEmu.DebugRelease();
103 			}
104 
105 			return memory;
106 		}
107 
tmrRefresh_Tick(object sender, EventArgs e)108 		private void tmrRefresh_Tick(object sender, EventArgs e)
109 		{
110 			if(_tabIsFocused) {
111 				RefreshAddressList();
112 			}
113 		}
114 
RefreshAddressList()115 		private void RefreshAddressList()
116 		{
117 			UpdateUI();
118 			if(!InteropEmu.IsRunning()) {
119 				return;
120 			}
121 
122 			HashSet<int> matchingAddresses = new HashSet<int>();
123 			for(int i = 0; i < 0x800; i++) {
124 				matchingAddresses.Add(i);
125 			}
126 
127 			if(_memorySnapshots.Count > 1) {
128 				for(int i = 0; i < _memorySnapshots.Count - 1; i++) {
129 					matchingAddresses = new HashSet<int>(matchingAddresses.Where(addr => {
130 						if(_filters[i].PrevType.HasValue) {
131 							switch(_filters[i].PrevType) {
132 								case CheatPrevFilterType.Smaller: return _memorySnapshots[i+1][addr] > _memorySnapshots[i][addr];
133 								case CheatPrevFilterType.Equal: return _memorySnapshots[i+1][addr] == _memorySnapshots[i][addr];
134 								case CheatPrevFilterType.NotEqual: return _memorySnapshots[i+1][addr] != _memorySnapshots[i][addr];
135 								case CheatPrevFilterType.Greater: return _memorySnapshots[i+1][addr] < _memorySnapshots[i][addr];
136 							}
137 						} else {
138 							switch(_filters[i].CurrentType) {
139 								case CheatCurrentFilterType.Smaller: return _memorySnapshots[i+1][addr] < _filters[i].Operand;
140 								case CheatCurrentFilterType.Equal: return _memorySnapshots[i+1][addr] == _filters[i].Operand;
141 								case CheatCurrentFilterType.NotEqual: return _memorySnapshots[i+1][addr] != _filters[i].Operand;
142 								case CheatCurrentFilterType.Greater: return _memorySnapshots[i+1][addr] > _filters[i].Operand;
143 							}
144 						}
145 						return false;
146 					}));
147 				}
148 			}
149 
150 			byte[] memory = GetSnapshot();
151 			List<byte> values = new List<byte>(0x800);
152 			List<int> addresses = new List<int>(0x800);
153 			for(int i = 0; i < 0x800; i++) {
154 				if(matchingAddresses.Contains(i)) {
155 					addresses.Add(i);
156 					values.Add(memory[i]);
157 				}
158 			}
159 			lstAddresses.SetData(values.ToArray(), addresses.ToArray());
160 		}
161 
btnReset_Click(object sender, EventArgs e)162 		private void btnReset_Click(object sender, EventArgs e)
163 		{
164 			Reset();
165 		}
166 
btnUndo_Click(object sender, EventArgs e)167 		private void btnUndo_Click(object sender, EventArgs e)
168 		{
169 			if(_filters.Count > 0) {
170 				_filters.RemoveAt(_filters.Count-1);
171 				_memorySnapshots.RemoveAt(_memorySnapshots.Count-1);
172 				UpdateUI();
173 			}
174 		}
175 
UpdateUI()176 		private void UpdateUI()
177 		{
178 			btnUndo.Enabled = _filters.Count > 0;
179 			chkPauseGameWhileWindowActive.Enabled = btnAddCurrentFilter.Enabled = btnAddPrevFilter.Enabled = btnReset.Enabled = cboCurrentFilterType.Enabled = cboPrevFilterType.Enabled = nudCurrentFilterValue.Enabled = InteropEmu.IsRunning();
180 			mnuCreateCheat.Enabled = btnCreateCheat.Enabled = lstAddresses.CurrentAddress.HasValue;
181 
182 			if(lstAddresses.CurrentAddress.HasValue) {
183 				lblAddress.Visible = true;
184 				lblAtAddress.Visible = true;
185 				lblAddress.Text = "$" + lstAddresses.CurrentAddress?.ToString("X4");
186 			} else {
187 				lblAddress.Visible = false;
188 				lblAtAddress.Visible = false;
189 			}
190 		}
191 
btnAddPrevFilter_Click(object sender, EventArgs e)192 		private void btnAddPrevFilter_Click(object sender, EventArgs e)
193 		{
194 			_filters.Add(new FilterInfo { PrevType = (CheatPrevFilterType)cboPrevFilterType.SelectedIndex });
195 			TakeSnapshot();
196 		}
197 
btnAddCurrentFilter_Click(object sender, EventArgs e)198 		private void btnAddCurrentFilter_Click(object sender, EventArgs e)
199 		{
200 			_filters.Add(new FilterInfo { CurrentType = (CheatCurrentFilterType)cboCurrentFilterType.SelectedIndex, Operand = (int)nudCurrentFilterValue.Value });
201 			TakeSnapshot();
202 		}
203 
contextMenuStrip_Opening(object sender, CancelEventArgs e)204 		private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
205 		{
206 			UpdateUI();
207 		}
208 
btnCreateCheat_Click(object sender, EventArgs e)209 		private void btnCreateCheat_Click(object sender, EventArgs e)
210 		{
211 			RomInfo romInfo = InteropEmu.GetRomInfo();
212 			CheatInfo newCheat = new CheatInfo {
213 				GameCrc = romInfo.GetPrgCrcString(),
214 				GameName = romInfo.GetRomName(),
215 				Address = (uint)lstAddresses.CurrentAddress,
216 				CheatType = CheatType.Custom
217 			};
218 
219 			using(frmCheat frm = new frmCheat(newCheat)) {
220 				if(frm.ShowDialog() == DialogResult.OK) {
221 					OnAddCheat?.Invoke(newCheat, new EventArgs());
222 				}
223 			}
224 		}
225 
chkPauseGameWhileWindowActive_CheckedChanged(object sender, EventArgs e)226 		private void chkPauseGameWhileWindowActive_CheckedChanged(object sender, EventArgs e)
227 		{
228 			if(chkPauseGameWhileWindowActive.Checked) {
229 				InteropEmu.Pause();
230 			} else {
231 				InteropEmu.Resume();
232 			}
233 		}
234 	}
235 }
236