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.Client.Common; namespace BizHawk.Client.EmuHawk { public partial class GBGameGenie : Form, IToolFormAutoConfig { // TODO: fix the use of Global.Game.System and Emulator.SystemId [RequiredService] private IEmulator Emulator { get; set; } [RequiredService] private IMemoryDomains MemoryDomains { get; set; } private readonly Dictionary _gameGenieTable = new Dictionary(); private bool _processing; public bool AskSaveChanges() { return true; } public bool UpdateBefore { get { return false; } } public void Restart() { if ((Emulator.SystemId != "GB") && (Global.Game.System != "GG")) { Close(); } } public void UpdateValues() { if ((Emulator.SystemId != "GB") && (Global.Game.System != "GG")) { Close(); } } public void FastUpdate() { // Do nothing } public GBGameGenie() { InitializeComponent(); _gameGenieTable.Add('0', 0); // 0000 _gameGenieTable.Add('1', 1); // 0001 _gameGenieTable.Add('2', 2); // 0010 _gameGenieTable.Add('3', 3); // 0011 _gameGenieTable.Add('4', 4); // 0100 _gameGenieTable.Add('5', 5); // 0101 _gameGenieTable.Add('6', 6); // 0110 _gameGenieTable.Add('7', 7); // 0111 _gameGenieTable.Add('8', 8); // 1000 _gameGenieTable.Add('9', 9); // 1001 _gameGenieTable.Add('A', 10); // 1010 _gameGenieTable.Add('B', 11); // 1011 _gameGenieTable.Add('C', 12); // 1100 _gameGenieTable.Add('D', 13); // 1101 _gameGenieTable.Add('E', 14); // 1110 _gameGenieTable.Add('F', 15); // 1111 } public void GBGGDecode(string code, ref int val, ref int add, ref int cmp) { // No cypher on value // Char # | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | // 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|3|2|1|0| // maps to| Value |A|B|C|D|E|F|G|H|I|J|K|L|XOR 0xF|a|b|c|c|NotUsed|e|f|g|h| // proper | Value |XOR 0xF|A|B|C|D|E|F|G|H|I|J|K|L|g|h|a|b|Nothing|c|d|e|f| 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 << 8; } else { add = -1; } 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; } if (code.Length > 5) { _gameGenieTable.TryGetValue(code[5], out x); add |= (x ^ 0xF) << 12; } // compare need to be full if (code.Length > 8) { int comp = 0; _gameGenieTable.TryGetValue(code[6], out x); comp = x << 2; // 8th character ignored _gameGenieTable.TryGetValue(code[8], out x); comp |= (x & 0xC) >> 2; comp |= (x & 0x3) << 6; cmp = comp ^ 0xBA; } else { cmp = -1; } } private string GBGGEncode(int val, int add, int cmp) { char[] letters = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; string code = string.Empty; int[] num = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; num[0] = (val & 0xF0) >> 4; num[1] = val & 0x0F; num[2] = (add & 0x0F00) >> 8; num[3] = (add & 0x00F0) >> 4; num[4] = add & 0x000F; num[5] = ((add & 0xF000) >> 12) ^ 0xF; if (cmp > -1) { int xoredcomp = (cmp & 0xFF) ^ 0xBA; num[6] = (xoredcomp & 0x30) >> 2; num[6] |= (xoredcomp & 0x0C) >> 2; // 8th char has no real use (its value is not reflected in the address:value:compare // probably a protection to stop people making code up, xor the 7th digit with 0x8 // to get back what the original code had (Might be more to it) num[7] = num[6] ^ 8; num[8] = (xoredcomp & 0xC0) >> 6; num[8] |= (xoredcomp & 0x03) << 2; for (int i = 0; i < 9; i++) { code += letters[num[i]]; } } else { for (int i = 0; i < 6; i++) { code += letters[num[i]]; } } return code; } private void GBGameGenie_Load(object sender, EventArgs e) { addcheatbt.Enabled = false; //"Game Boy/Game Gear Game Genie Encoder / Decoder" if (Emulator.SystemId == "GB") { Text = "Game Boy Game Genie Encoder/Decoder"; } else { Text = "Game Gear Game Genie Encoder/Decoder"; } } #region Dialog and Control Events private void AddCheatClick(object sender, EventArgs e) { if ((Emulator.SystemId == "GB") || (Global.Game.System == "GG")) { string name; var address = 0; var value = 0; int? compare = null; if (!string.IsNullOrWhiteSpace(cheatname.Text)) { name = cheatname.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); } if (!string.IsNullOrWhiteSpace(ValueBox.Text)) { value = (byte)int.Parse(ValueBox.Text, NumberStyles.HexNumber); } if (!string.IsNullOrWhiteSpace(CompareBox.Text)) { try { compare = byte.Parse(CompareBox.Text, NumberStyles.HexNumber); } catch { compare = null; } } var watch = Watch.GenerateWatch( MemoryDomains["System Bus"], address, Watch.WatchSize.Byte, Watch.DisplayType.Hex, name, false); Global.CheatList.Add(new Cheat( watch, value, compare)); } } private void ClearButton_Click(object sender, EventArgs e) { AddressBox.Text = string.Empty; ValueBox.Text = string.Empty; CompareBox.Text = string.Empty; GGCodeMaskBox.Text = string.Empty; addcheatbt.Enabled = false; } private void CompareBox_TextChanged(object sender, EventArgs e) { if (_processing == false) { _processing = true; // remove invalid character when pasted if (Regex.IsMatch(CompareBox.Text, @"[^a-fA-F0-9]")) { CompareBox.Text = Regex.Replace(CompareBox.Text, @"[^a-fA-F0-9]", string.Empty); } if ((CompareBox.Text.Length == 2) || (CompareBox.Text.Length == 0)) { if ((AddressBox.Text.Length > 0) || (ValueBox.Text.Length > 0)) { int val = 0; int add = 0; int cmp = -1; if (ValueBox.Text.Length > 0) { val = int.Parse(ValueBox.Text, NumberStyles.HexNumber); } if (AddressBox.Text.Length > 0) { add = int.Parse(AddressBox.Text, NumberStyles.HexNumber); } if (CompareBox.Text.Length == 2) { cmp = int.Parse(CompareBox.Text, NumberStyles.HexNumber); } GGCodeMaskBox.Text = GBGGEncode(val, add, cmp); 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)) { var val = 0; var add = 0; var cmp = -1; if (ValueBox.Text.Length > 0) { val = int.Parse(ValueBox.Text, NumberStyles.HexNumber); } if (AddressBox.Text.Length > 0) { add = int.Parse(AddressBox.Text, NumberStyles.HexNumber); } if (CompareBox.Text.Length == 2) { cmp = int.Parse(CompareBox.Text, NumberStyles.HexNumber); } GGCodeMaskBox.Text = GBGGEncode(val, add, cmp); addcheatbt.Enabled = true; } else { GGCodeMaskBox.Text = string.Empty; addcheatbt.Enabled = false; } _processing = false; } } 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 ((AddressBox.Text.Length > 0) || (ValueBox.Text.Length > 0)) { var val = 0; var add = 0; var cmp = -1; if (ValueBox.Text.Length > 0) { val = int.Parse(ValueBox.Text, NumberStyles.HexNumber); } if (AddressBox.Text.Length > 0) { add = int.Parse(AddressBox.Text, NumberStyles.HexNumber); } if (CompareBox.Text.Length == 2) { cmp = int.Parse(CompareBox.Text, NumberStyles.HexNumber); } GGCodeMaskBox.Text = GBGGEncode(val, add, cmp); addcheatbt.Enabled = true; } else { GGCodeMaskBox.Text = string.Empty; addcheatbt.Enabled = false; } _processing = false; } } private void Keypad_Click(object sender, EventArgs e) { if (GGCodeMaskBox.Text.Length < 9) { var code = string.Empty; if (sender == B0) { code = "0"; } else if (sender == B1) { code = "1"; } else if (sender == B2) { code = "2"; } else if (sender == B3) { code = "3"; } else if (sender == B4) { code = "4"; } else if (sender == B5) { code = "5"; } else if (sender == B6) { code = "6"; } else if (sender == B7) { code = "7"; } else if (sender == B8) { code = "8"; } else if (sender == B9) { code = "9"; } else if (sender == BA) { code = "A"; } else if (sender == BB) { code = "B"; } else if (sender == BC) { code = "C"; } else if (sender == BD) { code = "D"; } else if (sender == BE) { code = "E"; } else if (sender == BF) { code = "F"; } GGCodeMaskBox.Text += code; } } private void GGCodeMaskBox_KeyPress(object sender, KeyPressEventArgs e) { // Find a better way to remove all NON HEX char, while still allowing copy/paste // Right now its all done through removing em GGCodeMaskBox_TextChanged } 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) { var val = -1; var add = -1; var cmp = -1; GBGGDecode(GGCodeMaskBox.Text, ref val, ref add, ref cmp); if (add > -1) { AddressBox.Text = string.Format("{0:X4}", add); } if (val > -1) { ValueBox.Text = string.Format("{0:X2}", val); } if (cmp > -1) { CompareBox.Text = string.Format("{0:X2}", cmp); } else { CompareBox.Text = string.Empty; } addcheatbt.Enabled = true; } else { AddressBox.Text = string.Empty; ValueBox.Text = string.Empty; CompareBox.Text = string.Empty; addcheatbt.Enabled = false; } _processing = false; } } #endregion } }