Some things to note:
- The algorithm here is in the form of a property (getter method) of a Cheat class. It has access to properties: Address (unsigned short), Value (byte), NeedsComparison (boolean) and CompareValue (byte)
- When NeedsComparison is False, CompareValue is ignored and two 6-letter Game Genie code variants are generated using Address and Value only. This is a substitute-only code
- When NeedsComparison is True, CompareValue is taken into consideration and two 8-letter Game Genie code variants are generated using Address, Value and CompareValue. This is a compare-and-substitute code
- The algorithm generates two code variants because bit 7 of the 3rd letter nybble in the code is not used when decoding the cheat properties and can hence be either 1 or 0. The two variants differ only in the 3rd letter. For example, the codes GOSSIP and GOISIP both represent the same cheat (substitute $14 at address $D1DD)
- The algorithm does not work with cheats that target the lower 32K of the address space since when decoded, the addresses are mapped in the upper 32K (3rd letter bit 7 could have been used for this purpose, in hindsight)
So here goes:
Code: Select all
public string GameGenieCode
{
get
{
byte[] nybbles = new byte[NeedsComparison ? 8 : 6];
// reverse of
// address = 0x8000 + ((n3 & 7) << 12) | ((n5 & 7) << 8) | ((n4 & 8) << 8) | ((n2 & 7) << 4) | ((n1 & 8) << 4) | (n4 & 7) | (n3 & 8);
// common to both 6-letter and 8-letter codes
nybbles[0] = (byte)(((Value >> 4) & 8) | ((Value >> 0) & 7));
nybbles[1] = (byte)(((Address >> 4) & 8) | ((Value >> 4) & 7));
nybbles[2] = (byte)(((Address >> 4) & 7)); // unknown bit 7 produces 2 variants
nybbles[3] = (byte)(((Address >> 0) & 8) | ((Address >> 12) & 7));
nybbles[4] = (byte)(((Address >> 8) & 8) | ((Address >> 0) & 7));
if (NeedsComparison)
{
// reverse of
// data = ((n1 & 7) << 4) | ((n0 & 8) << 4) | (n0 & 7) | (n7 & 8);
// compare = ((n7 & 7) << 4) | ((n6 & 8) << 4) | (n6 & 7) | (n5 & 8);
nybbles[5] = (byte)(((CompareValue >> 0) & 8) | ((Address >> 8) & 7));
nybbles[6] = (byte)(((CompareValue >> 4) & 8) | ((CompareValue >> 0) & 7));
nybbles[7] = (byte)(((Value >> 0) & 8) | ((CompareValue >> 4) & 7));
}
else // 6 letter code
{
// reverse of
// data = ((n1 & 7) << 4) | ((n0 & 8) << 4) | (n0 & 7) | (n5 & 8);
nybbles[5] = (byte)(((Value >> 0) & 8) | ((Address >> 8) & 7));
}
StringBuilder stringBuilder = new StringBuilder();
foreach (byte nybble in nybbles)
stringBuilder.Append(gameGenieNybbleToChar[nybble]);
stringBuilder.Append(" / ");
nybbles[2] |= 8; // variant
foreach (byte nybble in nybbles)
stringBuilder.Append(gameGenieNybbleToChar[nybble]);
return stringBuilder.ToString();
}
}
public static char[] gameGenieNybbleToChar = new char[]
{ 'A', 'P', 'Z', 'L', 'G', 'I', 'T', 'Y',
'E', 'O', 'X', 'U', 'K', 'S', 'V', 'N' };