Mesen-X/GUI.NET/Forms/Cheats/frmCheat.cs

349 lines
9.9 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
namespace Mesen.GUI.Forms.Cheats
{
public partial class frmCheat : BaseConfigForm
{
const int GGShortCodeLength = 6;
const int GGLongCodeLength = 8;
const int PARCodeLength = 8;
private string _gameCrc;
public frmCheat(CheatInfo cheat)
{
InitializeComponent();
Entity = cheat;
_gameCrc = cheat.GameCrc;
radGameGenie.Tag = CheatType.GameGenie;
radProActionRocky.Tag = CheatType.ProActionRocky;
radCustom.Tag = CheatType.Custom;
radRelativeAddress.Tag = true;
radAbsoluteAddress.Tag = false;
AddBinding("Enabled", chkEnabled);
AddBinding("CheatName", txtCheatName);
AddBinding("GameName", txtGameName);
AddBinding("CheatType", radGameGenie.Parent);
AddBinding("GameGenieCode", txtGameGenie);
AddBinding("ProActionRockyCode", txtProActionRocky);
AddBinding("Address", txtAddress);
AddBinding("Value", txtValue);
AddBinding("UseCompareValue", chkCompareValue);
AddBinding("CompareValue", txtCompare);
AddBinding("IsRelativeAddress", radRelativeAddress.Parent);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
txtCheatName.Focus();
}
protected override bool ApplyChangesOnOK
{
get { return false; }
}
protected override void UpdateConfig()
{
((CheatInfo)Entity).GameCrc = _gameCrc;
}
private void LoadGame(string romPath)
{
ResourcePath resource = romPath;
if(frmSelectRom.SelectRom(ref resource)) {
RomInfo romInfo = InteropEmu.GetRomInfo(resource);
_gameCrc = romInfo.GetPrgCrcString();
if(_gameCrc != null) {
((CheatInfo)Entity).GameName = romInfo.GetRomName();
txtGameName.Text = ((CheatInfo)Entity).GameName;
}
}
}
private void btnBrowse_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.SetFilter(ResourceHelper.GetMessage("FilterRom"));
if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
LoadGame(ofd.FileName);
}
}
protected override bool ValidateInput()
{
UInt32 val;
if(_gameCrc == null) {
return false;
}
if(string.IsNullOrWhiteSpace(txtGameName.Text)) {
return false;
}
if(string.IsNullOrWhiteSpace(txtCheatName.Text)) {
return false;
}
if(radGameGenie.Checked) {
string ggCode = txtGameGenie.Text.Trim();
if(ggCode.Length != frmCheat.GGShortCodeLength && ggCode.Length != frmCheat.GGLongCodeLength) {
return false;
}
if(ggCode.Count(c => !"APZLGITYEOXUKSVN".Contains(c.ToString().ToUpper())) > 0) {
return false;
}
} else if(radProActionRocky.Checked) {
string parCode = txtProActionRocky.Text.Trim();
if(parCode.Length != frmCheat.PARCodeLength) {
return false;
}
if(!UInt32.TryParse(parCode, System.Globalization.NumberStyles.AllowHexSpecifier, null, out val)) {
return false;
}
if(parCode.Count(c => !"1234567890ABCDEF".Contains(c.ToString().ToUpper())) > 0) {
return false;
}
} else {
Byte byteVal;
if(!UInt32.TryParse(txtAddress.Text.Trim(), System.Globalization.NumberStyles.AllowHexSpecifier, null, out val)) {
return false;
}
if(radRelativeAddress.Checked && val > 0xFFFF) {
//Do not allow cheats outside the 0-0xFFFF range in relative addressing mode
return false;
}
if(!Byte.TryParse(txtValue.Text.Trim(), System.Globalization.NumberStyles.AllowHexSpecifier, null, out byteVal)) {
return false;
}
if(!string.IsNullOrWhiteSpace(txtCompare.Text.Trim()) && !Byte.TryParse(txtCompare.Text.Trim(), System.Globalization.NumberStyles.AllowHexSpecifier, null, out byteVal)) {
return false;
}
}
ConvertCode();
return true;
}
private void ConvertCode()
{
//Automatically update the other fields to match the active code (when possible)
UpdateObject();
CheatInfo cheat = (CheatInfo)this.Entity;
if(cheat.CheatType == CheatType.GameGenie) {
CheatData convertedData = ConvertGameGenieCode(cheat.GameGenieCode.ToUpper());
txtProActionRocky.Text = ToProActionReplayCode(convertedData.Address, convertedData.Value, convertedData.CompareValue);
txtAddress.Text = convertedData.Address.ToString("X4");
txtValue.Text = convertedData.Value.ToString("X2");
txtCompare.Text = convertedData.CompareValue >= 0 ? convertedData.CompareValue.ToString("X2") : "";
chkCompareValue.Checked = convertedData.CompareValue >= 0;
} else if(cheat.CheatType == CheatType.ProActionRocky) {
CheatData convertedData = ConvertProActionReplayCode(cheat.ProActionRockyCode);
txtGameGenie.Text = ToGameGenieCode(convertedData.Address, convertedData.Value, convertedData.CompareValue);
txtAddress.Text = convertedData.Address.ToString("X4");
txtValue.Text = convertedData.Value.ToString("X2");
txtCompare.Text = convertedData.CompareValue.ToString("X2");
radRelativeAddress.Checked = true;
chkCompareValue.Checked = true;
} else {
if(cheat.IsRelativeAddress && cheat.Address >= 0x8000) {
txtGameGenie.Text = ToGameGenieCode((int)cheat.Address, cheat.Value, cheat.UseCompareValue ? cheat.CompareValue : -1);
txtProActionRocky.Text = ToProActionReplayCode((int)cheat.Address, cheat.Value, cheat.UseCompareValue ? cheat.CompareValue : -1);
} else {
txtProActionRocky.Text = "";
txtGameGenie.Text = "";
}
}
}
private void txtGameGenie_Enter(object sender, EventArgs e)
{
radGameGenie.Checked = true;
}
private void txtProActionRocky_Enter(object sender, EventArgs e)
{
radProActionRocky.Checked = true;
}
private void customField_Enter(object sender, EventArgs e)
{
radCustom.Checked = true;
}
private void chkCompareValue_CheckedChanged(object sender, EventArgs e)
{
txtCompare.Enabled = chkCompareValue.Checked;
}
private int DecodeValue(int code, int[] bitIndexes)
{
int result = 0;
for(int i = 0; i < bitIndexes.Length; i++) {
result <<= 1;
result |= (code >> bitIndexes[i]) & 0x01;
}
return result;
}
private int EncodeValue(int code, int[] bitIndexes)
{
int result = 0;
for(int i = 0; i < bitIndexes.Length; i++) {
result |= ((code >> i) & 0x01) << bitIndexes[bitIndexes.Length - i - 1];
}
return result;
}
private CheatData ConvertGameGenieCode(string ggCode)
{
string ggLetters = "APZLGITYEOXUKSVN";
int rawCode = 0;
for(int i = 0, len = ggCode.Length; i < len; i++) {
rawCode |= ggLetters.IndexOf(ggCode[i]) << (i * 4);
}
int[] addressBits = { 14, 13, 12, 19, 22, 21, 20, 7, 10, 9, 8, 15, 18, 17, 16 };
int[] valueBits = { 3, 6, 5, 4, 23, 2, 1, 0 };
int compareValue = -1;
if(ggCode.Length == 8) {
//Bit 5 of the value is stored in a different location for 8-character codes
valueBits[4] = 31;
int[] compareValueBits = { 27, 30, 29, 28, 23, 26, 25, 24 };
compareValue = DecodeValue(rawCode, compareValueBits);
}
int address = DecodeValue(rawCode, addressBits) + 0x8000;
int value = DecodeValue(rawCode, valueBits);
return new CheatData() {
Address = address,
Value = value,
CompareValue = compareValue
};
}
private string ToGameGenieCode(int address, int value, int compareValue)
{
string ggLetters = "APZLGITYEOXUKSVN";
int[] addressBits = { 14, 13, 12, 19, 22, 21, 20, 7, 10, 9, 8, 15, 18, 17, 16 };
int[] valueBits = { 3, 6, 5, 4, 23, 2, 1, 0 };
int[] compareValueBits = { 27, 30, 29, 28, 23, 26, 25, 24 };
if(compareValue >= 0) {
//Bit 5 of the value is stored in a different location for 8-character codes
valueBits[4] = 31;
}
UInt32 encodedAddress = (UInt32)(EncodeValue(address - 0x8000, addressBits) | 0x800);
UInt32 encodedValue = (UInt32)EncodeValue(value, valueBits);
UInt32 encodedCode = encodedValue | encodedAddress;
if(compareValue >= 0) {
UInt32 encodedCompareValue = (UInt32)EncodeValue(compareValue, compareValueBits);
encodedCode |= encodedCompareValue;
}
int codeSize = compareValue >= 0 ? 8 : 6;
string codeString = "";
for(int i = 0; i < codeSize; i++) {
codeString += ggLetters[(int)encodedCode & 0x0F];
encodedCode >>= 4;
}
return codeString;
}
private CheatData ConvertProActionReplayCode(UInt32 parCode)
{
int[] shiftValues = {
3, 13, 14, 1, 6, 9, 5, 0, 12, 7, 2, 8, 10, 11, 4, //address
19, 21, 23, 22, 20, 17, 16, 18, //compare
29, 31, 24, 26, 25, 30, 27, 28 //value
};
UInt32 key = 0x7E5EE93A;
UInt32 xorValue = 0x5C184B91;
//Throw away bit 0, not used.
parCode >>= 1;
UInt32 result = 0;
for(int i = 30; i >= 0; i--) {
if((((key ^ parCode) >> 30) & 0x01) != 0) {
result |= (UInt32)(0x01 << shiftValues[i]);
key ^= xorValue;
}
parCode <<= 1;
key <<= 1;
}
return new CheatData() {
Address = (int)((result & 0x7fff) + 0x8000),
Value = (int)(result >> 24) & 0xFF,
CompareValue = (int)(result >> 16) & 0xFF
};
}
private string ToProActionReplayCode(int address, int value, int compareValue)
{
if(compareValue < 0) {
return "";
}
int[] shiftValues = {
3, 13, 14, 1, 6, 9, 5, 0, 12, 7, 2, 8, 10, 11, 4, //address
19, 21, 23, 22, 20, 17, 16, 18, //compare
29, 31, 24, 26, 25, 30, 27, 28 //value
};
int encodedValue = (address & 0x7FFF) | (compareValue << 16) | (value << 24);
UInt32 key = 0x7E5EE93A;
UInt32 xorValue = 0x5C184B91;
UInt32 result = 0;
for(int i = 30; i >= 0; i--) {
int bit = (int)(encodedValue >> shiftValues[i]) & 0x01;
if((((key >> 30) ^ bit) & 0x01) != 0) {
result |= (UInt32)2 << i;
}
if((bit & 0x01) != 0) {
key ^= xorValue;
}
key <<= 1;
}
return result.ToString("X8");
}
struct CheatData
{
public int Address;
public int Value;
public int CompareValue;
}
}
}