using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Windows.Forms; using BizHawk.Emulation.Common; using BizHawk.Emulation.Common.IEmulatorExtensions; using BizHawk.Emulation.Cores.Nintendo.SNES; using BizHawk.Client.Common; namespace BizHawk.Client.EmuHawk { public partial class SNESGameGenie : Form, IToolFormAutoConfig { [RequiredService] public LibsnesCore Emulator { get; set; } [RequiredService] private IMemoryDomains MemoryDomains { get; set; } // including transposition // Code: D F 4 7 0 9 1 5 6 B C 8 A 2 3 E // Hex: 0 1 2 3 4 5 6 7 8 9 A B C D E F private readonly Dictionary _gameGenieTable = new Dictionary { { 'D', 0 }, // 0000 { 'F', 1 }, // 0001 { '4', 2 }, // 0010 { '7', 3 }, // 0011 { '0', 4 }, // 0100 { '9', 5 }, // 0101 { '1', 6 }, // 0110 { '5', 7 }, // 0111 { '6', 8 }, // 1000 { 'B', 9 }, // 1001 { 'C', 10 }, // 1010 { '8', 11 }, // 1011 { 'A', 12 }, // 1100 { '2', 13 }, // 1101 { '3', 14 }, // 1110 { 'E', 15 } // 1111 }; private bool _processing; public SNESGameGenie() { InitializeComponent(); } private void SNESGameGenie_Load(object sender, EventArgs e) { addcheatbt.Enabled = false; } #region Public API public bool AskSaveChanges() { return true; } public bool UpdateBefore { get { return false; } } public void Restart() { // Do nothing } public void UpdateValues() { // Do nothing } public void FastUpdate() { // Do nothing } #endregion private void SnesGGDecode(string code, ref int val, ref int add) { // Code: D F 4 7 0 9 1 5 6 B C 8 A 2 3 E // Hex: 0 1 2 3 4 5 6 7 8 9 A B C D E F // XXYY-YYYY, where XX is the value, and YY-YYYY is the address. // Char # | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | // Bit # |3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0| // maps to| Value |i|j|k|l|q|r|s|t|o|p|a|b|c|d|u|v|w|x|e|f|g|h|m|n| // order | Value |a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x| int x; // Getting Value if (code.Length > 0) { _gameGenieTable.TryGetValue(code[0], out x); val = x << 4; } if (code.Length > 1) { _gameGenieTable.TryGetValue(code[1], out x); val |= x; } // Address if (code.Length > 2) { _gameGenieTable.TryGetValue(code[2], out x); add = x << 12; } if (code.Length > 3) { _gameGenieTable.TryGetValue(code[3], out x); add |= x << 4; } if (code.Length > 4) { _gameGenieTable.TryGetValue(code[4], out x); add |= (x & 0xC) << 6; add |= (x & 0x3) << 22; } if (code.Length > 5) { _gameGenieTable.TryGetValue(code[5], out x); add |= (x & 0xC) << 18; add |= (x & 0x3) << 2; } if (code.Length > 6) { _gameGenieTable.TryGetValue(code[6], out x); add |= (x & 0xC) >> 2; add |= (x & 0x3) << 18; } if (code.Length > 7) { _gameGenieTable.TryGetValue(code[7], out x); add |= (x & 0xC) << 14; add |= (x & 0x3) << 10; } } private static string SnesGGEncode(int val, int add) { // Code: D F 4 7 0 9 1 5 6 B C 8 A 2 3 E // Hex: 0 1 2 3 4 5 6 7 8 9 A B C D E F // Char # | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | // Bit # |3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0|3|2|1|0| // maps to| Value |i|j|k|l|q|r|s|t|o|p|a|b|c|d|u|v|w|x|e|f|g|h|m|n| // order | Value |a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x| char[] letters = { 'D', 'F', '4', '7', '0', '9', '1', '5', '6', 'B', 'C', '8', 'A', '2', '3', 'E' }; var code = string.Empty; int[] num = { 0, 0, 0, 0, 0, 0, 0, 0 }; num[0] = (val & 0xF0) >> 4; num[1] = val & 0x0F; num[2] = (add & 0x00F000) >> 12; // ijkl num[3] = (add & 0x0000F0) >> 4; // qrst num[4] = ((add & 0x000300) >> 6) | ((add & 0xC00000) >> 22); // opab num[5] = ((add & 0x300000) >> 18) | ((add & 0x00000C) >> 2); // cduv num[6] = ((add & 0x000003) << 2) | ((add & 0x0C0000) >> 18); // wxef num[7] = ((add & 0x030000) >> 14) | ((add & 0x000C00) >> 10); // ghmn for (int i = 0; i < 8; i++) { code += letters[num[i]]; } return code; } #region Dialog and Control Events private void ClearButton_Click(object sender, EventArgs e) { AddressBox.Text = string.Empty; ValueBox.Text = string.Empty; GGCodeMaskBox.Text = string.Empty; addcheatbt.Enabled = false; } private void AddCheat_Click(object sender, EventArgs e) { string name; var address = 0; var value = 0; if (!string.IsNullOrWhiteSpace(CheatNameBox.Text)) { name = CheatNameBox.Text; } else { _processing = true; GGCodeMaskBox.TextMaskFormat = MaskFormat.IncludeLiterals; name = GGCodeMaskBox.Text; GGCodeMaskBox.TextMaskFormat = MaskFormat.ExcludePromptAndLiterals; _processing = false; } if (!string.IsNullOrWhiteSpace(AddressBox.Text)) { address = int.Parse(AddressBox.Text, NumberStyles.HexNumber) + 0x8000; } if (!string.IsNullOrWhiteSpace(ValueBox.Text)) { value = byte.Parse(ValueBox.Text, NumberStyles.HexNumber); } var watch = Watch.GenerateWatch( MemoryDomains["BUS"], address, Watch.WatchSize.Byte, Watch.DisplayType.Hex, name, bigEndian: false ); Global.CheatList.Add(new Cheat( watch, value )); } private void AddressBox_TextChanged(object sender, EventArgs e) { // Remove invalid character when pasted if (_processing == false) { _processing = true; if (Regex.IsMatch(AddressBox.Text, @"[^a-fA-F0-9]")) { AddressBox.Text = Regex.Replace(AddressBox.Text, @"[^a-fA-F0-9]", string.Empty); } if (!string.IsNullOrEmpty(AddressBox.Text) || !string.IsNullOrEmpty(ValueBox.Text)) { int val = 0, add = 0; if (!string.IsNullOrEmpty(AddressBox.Text)) { add = int.Parse(AddressBox.Text, NumberStyles.HexNumber); } if (!string.IsNullOrEmpty(ValueBox.Text)) { val = int.Parse(ValueBox.Text, NumberStyles.HexNumber); } GGCodeMaskBox.Text = SnesGGEncode(val, add); addcheatbt.Enabled = true; } else { GGCodeMaskBox.Text = string.Empty; addcheatbt.Enabled = false; } _processing = false; } } private void ValueBox_TextChanged(object sender, EventArgs e) { if (_processing == false) { _processing = true; // remove invalid character when pasted if (Regex.IsMatch(ValueBox.Text, @"[^a-fA-F0-9]")) { ValueBox.Text = Regex.Replace(ValueBox.Text, @"[^a-fA-F0-9]", string.Empty); } if ((AddressBox.Text.Length > 0) || (ValueBox.Text.Length > 0)) { int val = 0, add = 0; if (ValueBox.Text.Length > 0) { val = int.Parse(ValueBox.Text, NumberStyles.HexNumber); } if (AddressBox.Text.Length > 0) { add = int.Parse(AddressBox.Text, NumberStyles.HexNumber); } GGCodeMaskBox.Text = SnesGGEncode(val, add); addcheatbt.Enabled = true; } else { GGCodeMaskBox.Text = string.Empty; addcheatbt.Enabled = false; } _processing = false; } } private void GGCodeMaskBox_TextChanged(object sender, EventArgs e) { if (_processing == false) { _processing = true; // insert REGEX Remove non HEXA char if (Regex.IsMatch(GGCodeMaskBox.Text, @"[^a-fA-F0-9]")) { GGCodeMaskBox.Text = Regex.Replace(GGCodeMaskBox.Text, @"[^a-fA-F0-9]", string.Empty); } if (GGCodeMaskBox.Text.Length > 0) { int val = 0, add = 0; SnesGGDecode(GGCodeMaskBox.Text, ref val, ref add); AddressBox.Text = string.Format("{0:X6}", add); ValueBox.Text = string.Format("{0:X2}", val); addcheatbt.Enabled = true; } else { AddressBox.Text = string.Empty; ValueBox.Text = string.Empty; addcheatbt.Enabled = false; } _processing = false; } } private void Keypad_Click(object sender, EventArgs e) { if (GGCodeMaskBox.Text.Length < 8) { var code = string.Empty; if (sender == B0) code = "0"; if (sender == B1) code = "1"; if (sender == B2) code = "2"; if (sender == B3) code = "3"; if (sender == B4) code = "4"; if (sender == B5) code = "5"; if (sender == B6) code = "6"; if (sender == B7) code = "7"; if (sender == B8) code = "8"; if (sender == B9) code = "9"; if (sender == BA) code = "A"; if (sender == BB) code = "B"; if (sender == BC) code = "C"; if (sender == BD) code = "D"; if (sender == BE) code = "E"; if (sender == BF) code = "F"; GGCodeMaskBox.Text += code; } } #endregion } }