Clean up and enable SNES Game Genie cheatcode converter
This commit is contained in:
parent
f9d3d91d34
commit
0e6d3a34d7
|
@ -128,12 +128,7 @@ namespace BizHawk.Client.Common.cheats
|
|||
|
||||
private static IDecodeResult Snes(string code)
|
||||
{
|
||||
if (code.Contains("-") && code.Length == 9)
|
||||
{
|
||||
return new InvalidCheatCode("Game genie codes are not currently supported for SNES");
|
||||
////return SnesGameGenieDecoder.Decode(code);
|
||||
}
|
||||
|
||||
if (SnesGameGenieDecoder.Decode(code) is DecodeResult validSNES) return validSNES; // decoder checks format, will not return invalid for any well-formed input
|
||||
if (code.Length == 8)
|
||||
{
|
||||
return SnesActionReplayDecoder.Decode(code);
|
||||
|
|
|
@ -1,31 +1,38 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Common.StringExtensions;
|
||||
|
||||
namespace BizHawk.Client.Common.cheats
|
||||
{
|
||||
public static class SnesGameGenieDecoder
|
||||
{
|
||||
// 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
|
||||
// This only applies to the SNES
|
||||
private static readonly Dictionary<char, int> SNESGameGenieTable = new Dictionary<char, int>
|
||||
private const string ERR_MSG_MALFORMED = "Game genie codes must be 9 characters with a format of xxyy-yyyy";
|
||||
|
||||
private static void RotLeft16(ref uint value, int offset)
|
||||
=> value = ((value << offset) & 0xFFFF) | (value >> (16 - offset));
|
||||
|
||||
/// <remarks>
|
||||
/// encr: D F 4 7 0 9 1 5 6 B C 8 A 2 3 E
|
||||
/// decr: 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
/// </remarks>
|
||||
private static readonly Dictionary<char, byte> NybbleDecodeLookup = new()
|
||||
{
|
||||
['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
|
||||
['0'] = 0x4,
|
||||
['1'] = 0x6,
|
||||
['2'] = 0xD,
|
||||
['3'] = 0xE,
|
||||
['4'] = 0x2,
|
||||
['5'] = 0x7,
|
||||
['6'] = 0x8,
|
||||
['7'] = 0x3,
|
||||
['8'] = 0xB,
|
||||
['9'] = 0x5,
|
||||
['A'] = 0xC,
|
||||
['B'] = 0x9,
|
||||
['C'] = 0xA,
|
||||
['D'] = 0x0,
|
||||
['E'] = 0xF,
|
||||
['F'] = 0x1,
|
||||
};
|
||||
|
||||
public static IDecodeResult Decode(string code)
|
||||
|
@ -34,11 +41,9 @@ namespace BizHawk.Client.Common.cheats
|
|||
{
|
||||
throw new ArgumentNullException(nameof(code));
|
||||
}
|
||||
|
||||
if (!code.Contains("-") && code.Length != 9)
|
||||
{
|
||||
return new InvalidCheatCode("Game genie codes must be 9 characters with a format of xxyy-yyyy");
|
||||
}
|
||||
if (code.Length is not 9 || code[4] is not '-') return new InvalidCheatCode(ERR_MSG_MALFORMED);
|
||||
code = code.OnlyHex();
|
||||
if (code.Length is not 8) return new InvalidCheatCode(ERR_MSG_MALFORMED);
|
||||
|
||||
// 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
|
||||
|
@ -47,65 +52,25 @@ namespace BizHawk.Client.Common.cheats
|
|||
// 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|
|
||||
var result = new DecodeResult { Size = WatchSize.Byte };
|
||||
|
||||
int x;
|
||||
|
||||
// Value
|
||||
if (code.Length > 0)
|
||||
var addrBitsIJKL = (uint) NybbleDecodeLookup[code[2]];
|
||||
var addrBitsQRST = (uint) NybbleDecodeLookup[code[3]];
|
||||
var bunchaBits = unchecked((uint) ((NybbleDecodeLookup[code[4]] << 12)
|
||||
| (NybbleDecodeLookup[code[5]] << 8)
|
||||
| (NybbleDecodeLookup[code[6]] << 4)
|
||||
| NybbleDecodeLookup[code[7]]));
|
||||
RotLeft16(ref bunchaBits, 2);
|
||||
var addr = ((bunchaBits & 0xF000U) << 8) // offset 12 to 20
|
||||
| ((bunchaBits & 0x00F0U) << 12) // offset 4 to 16
|
||||
| (addrBitsIJKL << 12)
|
||||
| ((bunchaBits & 0x000FU) << 8) // offset 0 to 8
|
||||
| (addrBitsQRST << 4)
|
||||
| ((bunchaBits & 0x0F00U) >> 8); // offset 8 to 0
|
||||
return new DecodeResult
|
||||
{
|
||||
_ = SNESGameGenieTable.TryGetValue(code[0], out x);
|
||||
result.Value = x << 4;
|
||||
}
|
||||
|
||||
if (code.Length > 1)
|
||||
{
|
||||
_ = SNESGameGenieTable.TryGetValue(code[1], out x);
|
||||
result.Value |= x;
|
||||
}
|
||||
|
||||
// Address
|
||||
if (code.Length > 2)
|
||||
{
|
||||
_ = SNESGameGenieTable.TryGetValue(code[2], out x);
|
||||
result.Address = x << 12;
|
||||
}
|
||||
|
||||
if (code.Length > 3)
|
||||
{
|
||||
_ = SNESGameGenieTable.TryGetValue(code[3], out x);
|
||||
result.Address |= x << 4;
|
||||
}
|
||||
|
||||
if (code.Length > 4)
|
||||
{
|
||||
_ = SNESGameGenieTable.TryGetValue(code[4], out x);
|
||||
result.Address |= (x & 0xC) << 6;
|
||||
result.Address |= (x & 0x3) << 22;
|
||||
}
|
||||
|
||||
if (code.Length > 5)
|
||||
{
|
||||
_ = SNESGameGenieTable.TryGetValue(code[5], out x);
|
||||
result.Address |= (x & 0xC) << 18;
|
||||
result.Address |= (x & 0x3) << 2;
|
||||
}
|
||||
|
||||
if (code.Length > 6)
|
||||
{
|
||||
_ = SNESGameGenieTable.TryGetValue(code[6], out x);
|
||||
result.Address |= (x & 0xC) >> 2;
|
||||
result.Address |= (x & 0x3) << 18;
|
||||
}
|
||||
|
||||
if (code.Length > 7)
|
||||
{
|
||||
_ = SNESGameGenieTable.TryGetValue(code[7], out x);
|
||||
result.Address |= (x & 0xC) << 14;
|
||||
result.Address |= (x & 0x3) << 10;
|
||||
}
|
||||
|
||||
return result;
|
||||
Address = unchecked((int) addr),
|
||||
Size = WatchSize.Byte,
|
||||
Value = (NybbleDecodeLookup[code[0]] << 4) | NybbleDecodeLookup[code[1]],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,8 @@ namespace BizHawk.Tests.Client.Common.cheats
|
|||
new object?[] { VSystemID.Raw.GB, "BE0-37B-08C", 0x4037, 0xBE, 0xB9, WatchSize.Byte },
|
||||
new object?[] { VSystemID.Raw.GBA, "4012F5B7 3B7801A6", 0x00000006, 0xB7, NO_COMPARE, WatchSize.Byte },
|
||||
new object?[] { VSystemID.Raw.GBA, "686D7FC3 24B5B832", 0x00000032, 0x7FC3, NO_COMPARE, WatchSize.Word },
|
||||
new object?[] { VSystemID.Raw.SNES, "C264-64D7", 0x008E28, 0xAD, NO_COMPARE, WatchSize.Byte },
|
||||
new object?[] { VSystemID.Raw.SNES, "008E28AD", 0x008E28, 0xAD, NO_COMPARE, WatchSize.Byte },
|
||||
new object?[] { VSystemID.Raw.SNES, "7E1F2801", 0x7E1F28, 0x01, NO_COMPARE, WatchSize.Byte },
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue