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