11using System . Collections . Generic ;
22
3+ using BizHawk . Common . StringExtensions ;
4+
35namespace BizHawk . Client . Common . cheats
46{
57 public static class SnesGameGenieDecoder
68 {
7- // including transposition
8- // Code: D F 4 7 0 9 1 5 6 B C 8 A 2 3 E
9- // Hex: 0 1 2 3 4 5 6 7 8 9 A B C D E F
10- // This only applies to the SNES
11- private static readonly Dictionary < char , int > SNESGameGenieTable = new Dictionary < char , int >
9+ private const string ERR_MSG_MALFORMED = "Game genie codes must be 9 characters with a format of xxyy-yyyy" ;
10+
11+ private static void RotLeft16 ( ref uint value , int offset )
12+ => value = ( ( value << offset ) & 0xFFFF ) | ( value >> ( 16 - offset ) ) ;
13+
14+ /// <remarks>
15+ /// encr: D F 4 7 0 9 1 5 6 B C 8 A 2 3 E
16+ /// decr: 0 1 2 3 4 5 6 7 8 9 A B C D E F
17+ /// </remarks>
18+ private static readonly Dictionary < char , byte > NybbleDecodeLookup = new ( )
1219 {
13- [ 'D ' ] = 0 , // 0000
14- [ 'F ' ] = 1 , // 0001
15- [ '4 ' ] = 2 , // 0010
16- [ '7 ' ] = 3 , // 0011
17- [ '0 ' ] = 4 , // 0100
18- [ '9 ' ] = 5 , // 0101
19- [ '1 ' ] = 6 , // 0110
20- [ '5 ' ] = 7 , // 0111
21- [ '6 ' ] = 8 , // 1000
22- [ 'B ' ] = 9 , // 1001
23- [ 'C ' ] = 10 , // 1010
24- [ '8 ' ] = 11 , // 1011
25- [ 'A ' ] = 12 , // 1100
26- [ '2 ' ] = 13 , // 1101
27- [ '3 ' ] = 14 , // 1110
28- [ 'E ' ] = 15 // 1111
20+ [ '0 ' ] = 0x4 ,
21+ [ '1 ' ] = 0x6 ,
22+ [ '2 ' ] = 0xD ,
23+ [ '3 ' ] = 0xE ,
24+ [ '4 ' ] = 0x2 ,
25+ [ '5 ' ] = 0x7 ,
26+ [ '6 ' ] = 0x8 ,
27+ [ '7 ' ] = 0x3 ,
28+ [ '8 ' ] = 0xB ,
29+ [ '9 ' ] = 0x5 ,
30+ [ 'A ' ] = 0xC ,
31+ [ 'B ' ] = 0x9 ,
32+ [ 'C ' ] = 0xA ,
33+ [ 'D ' ] = 0x0 ,
34+ [ 'E ' ] = 0xF ,
35+ [ 'F ' ] = 0x1 ,
2936 } ;
3037
3138 public static IDecodeResult Decode ( string code )
@@ -34,11 +41,9 @@ public static IDecodeResult Decode(string code)
3441 {
3542 throw new ArgumentNullException ( nameof ( code ) ) ;
3643 }
37-
38- if ( ! code . Contains ( "-" ) && code . Length != 9 )
39- {
40- return new InvalidCheatCode ( "Game genie codes must be 9 characters with a format of xxyy-yyyy" ) ;
41- }
44+ if ( code . Length is not 9 || code [ 4 ] is not '-' ) return new InvalidCheatCode ( ERR_MSG_MALFORMED ) ;
45+ code = code . OnlyHex ( ) ;
46+ if ( code . Length is not 8 ) return new InvalidCheatCode ( ERR_MSG_MALFORMED ) ;
4247
4348 // Code: D F 4 7 0 9 1 5 6 B C 8 A 2 3 E
4449 // Hex: 0 1 2 3 4 5 6 7 8 9 A B C D E F
@@ -47,65 +52,25 @@ public static IDecodeResult Decode(string code)
4752 // 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|
4853 // 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|
4954 // 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|
50- var result = new DecodeResult { Size = WatchSize . Byte } ;
51-
52- int x ;
53-
54- // Value
55- if ( code . Length > 0 )
55+ var addrBitsIJKL = ( uint ) NybbleDecodeLookup [ code [ 2 ] ] ;
56+ var addrBitsQRST = ( uint ) NybbleDecodeLookup [ code [ 3 ] ] ;
57+ var bunchaBits = unchecked ( ( uint ) ( ( NybbleDecodeLookup [ code [ 4 ] ] << 12 )
58+ | ( NybbleDecodeLookup [ code [ 5 ] ] << 8 )
59+ | ( NybbleDecodeLookup [ code [ 6 ] ] << 4 )
60+ | NybbleDecodeLookup [ code [ 7 ] ] ) ) ;
61+ RotLeft16 ( ref bunchaBits , 2 ) ;
62+ var addr = ( ( bunchaBits & 0xF000U ) << 8 ) // offset 12 to 20
63+ | ( ( bunchaBits & 0x00F0U ) << 12 ) // offset 4 to 16
64+ | ( addrBitsIJKL << 12 )
65+ | ( ( bunchaBits & 0x000FU ) << 8 ) // offset 0 to 8
66+ | ( addrBitsQRST << 4 )
67+ | ( ( bunchaBits & 0x0F00U ) >> 8 ) ; // offset 8 to 0
68+ return new DecodeResult
5669 {
57- _ = SNESGameGenieTable . TryGetValue ( code [ 0 ] , out x ) ;
58- result . Value = x << 4 ;
59- }
60-
61- if ( code . Length > 1 )
62- {
63- _ = SNESGameGenieTable . TryGetValue ( code [ 1 ] , out x ) ;
64- result . Value |= x ;
65- }
66-
67- // Address
68- if ( code . Length > 2 )
69- {
70- _ = SNESGameGenieTable . TryGetValue ( code [ 2 ] , out x ) ;
71- result . Address = x << 12 ;
72- }
73-
74- if ( code . Length > 3 )
75- {
76- _ = SNESGameGenieTable . TryGetValue ( code [ 3 ] , out x ) ;
77- result . Address |= x << 4 ;
78- }
79-
80- if ( code . Length > 4 )
81- {
82- _ = SNESGameGenieTable . TryGetValue ( code [ 4 ] , out x ) ;
83- result . Address |= ( x & 0xC ) << 6 ;
84- result . Address |= ( x & 0x3 ) << 22 ;
85- }
86-
87- if ( code . Length > 5 )
88- {
89- _ = SNESGameGenieTable . TryGetValue ( code [ 5 ] , out x ) ;
90- result . Address |= ( x & 0xC ) << 18 ;
91- result . Address |= ( x & 0x3 ) << 2 ;
92- }
93-
94- if ( code . Length > 6 )
95- {
96- _ = SNESGameGenieTable . TryGetValue ( code [ 6 ] , out x ) ;
97- result . Address |= ( x & 0xC ) >> 2 ;
98- result . Address |= ( x & 0x3 ) << 18 ;
99- }
100-
101- if ( code . Length > 7 )
102- {
103- _ = SNESGameGenieTable . TryGetValue ( code [ 7 ] , out x ) ;
104- result . Address |= ( x & 0xC ) << 14 ;
105- result . Address |= ( x & 0x3 ) << 10 ;
106- }
107-
108- return result ;
70+ Address = unchecked ( ( int ) addr ) ,
71+ Size = WatchSize . Byte ,
72+ Value = ( NybbleDecodeLookup [ code [ 0 ] ] << 4 ) | NybbleDecodeLookup [ code [ 1 ] ] ,
73+ } ;
10974 }
11075 }
11176}
0 commit comments