Cheats: Auto-convert between the different types of cheat codes (when possible)

This commit is contained in:
Sour 2019-12-25 15:54:55 -05:00
parent f5bc7b48ae
commit a29cc4c8d0

View file

@ -102,25 +102,27 @@ namespace Mesen.GUI.Forms.Cheats
}
if(radGameGenie.Checked) {
if(txtGameGenie.Text.Length != frmCheat.GGShortCodeLength && txtGameGenie.Text.Length != frmCheat.GGLongCodeLength) {
string ggCode = txtGameGenie.Text.Trim();
if(ggCode.Length != frmCheat.GGShortCodeLength && ggCode.Length != frmCheat.GGLongCodeLength) {
return false;
}
if(txtGameGenie.Text.Count(c => !"APZLGITYEOXUKSVN".Contains(c.ToString().ToUpper())) > 0) {
if(ggCode.Count(c => !"APZLGITYEOXUKSVN".Contains(c.ToString().ToUpper())) > 0) {
return false;
}
} else if(radProActionRocky.Checked) {
if(txtProActionRocky.Text.Length != frmCheat.PARCodeLength) {
string parCode = txtProActionRocky.Text.Trim();
if(parCode.Length != frmCheat.PARCodeLength) {
return false;
}
if(!UInt32.TryParse(txtProActionRocky.Text, System.Globalization.NumberStyles.AllowHexSpecifier, null, out val)) {
if(!UInt32.TryParse(parCode, System.Globalization.NumberStyles.AllowHexSpecifier, null, out val)) {
return false;
}
if(txtProActionRocky.Text.Count(c => !"1234567890ABCDEF".Contains(c.ToString().ToUpper())) > 0) {
if(parCode.Count(c => !"1234567890ABCDEF".Contains(c.ToString().ToUpper())) > 0) {
return false;
}
} else {
Byte byteVal;
if(!UInt32.TryParse(txtAddress.Text, System.Globalization.NumberStyles.AllowHexSpecifier, null, out val)) {
if(!UInt32.TryParse(txtAddress.Text.Trim(), System.Globalization.NumberStyles.AllowHexSpecifier, null, out val)) {
return false;
}
if(radRelativeAddress.Checked && val > 0xFFFF) {
@ -128,18 +130,52 @@ namespace Mesen.GUI.Forms.Cheats
return false;
}
if(!Byte.TryParse(txtValue.Text, System.Globalization.NumberStyles.AllowHexSpecifier, null, out byteVal)) {
if(!Byte.TryParse(txtValue.Text.Trim(), System.Globalization.NumberStyles.AllowHexSpecifier, null, out byteVal)) {
return false;
}
if(!Byte.TryParse(txtCompare.Text, System.Globalization.NumberStyles.AllowHexSpecifier, null, out byteVal)) {
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;
@ -159,5 +195,155 @@ namespace Mesen.GUI.Forms.Cheats
{
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;
}
}
}