redesign nes decoder to simpler design

This commit is contained in:
adelikat 2020-02-29 13:45:26 -06:00
parent addb861f50
commit 3cdd36699f
4 changed files with 137 additions and 87 deletions

View File

@ -0,0 +1,51 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common.cheats
{
/// <summary>
/// Represents a decoded cheat value
/// </summary>
public interface IDecodeResult
{
int Address { get; }
int Value { get; }
int? Compare { get; }
WatchSize Size { get; }
bool IsValid { get; }
}
public class DecodeResult : IDecodeResult
{
public int Address { get; internal set; }
public int Value { get; internal set; }
public int? Compare { get; internal set; }
public WatchSize Size { get; internal set; }
public bool IsValid => true;
}
public class InvalidResult : IDecodeResult
{
public int Address => int.MaxValue;
public int Value => int.MaxValue;
public int? Compare => null;
public WatchSize Size => WatchSize.Separator;
public bool IsValid => false;
}
public static class DecodeResultExtensions
{
public static Cheat ToCheat(this IDecodeResult result, MemoryDomain domain, string description)
{
var watch = Watch.GenerateWatch(
domain,
result.Address,
result.Size,
DisplayType.Hex,
domain.EndianType == MemoryDomain.Endian.Big,
description);
return result.Compare.HasValue
? new Cheat(watch, result.Value, result.Compare.Value, true, Cheat.CompareType.Equal)
: new Cheat(watch, result.Value);
}
}
}

View File

@ -1,12 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace BizHawk.Client.Common.cheats
{
public class NesGameGenieDecoder
public static class NesGameGenieDecoder
{
private readonly string _code;
private readonly Dictionary<char, int> _gameGenieTable = new Dictionary<char, int>
private static readonly Dictionary<char, int> GameGenieTable = new Dictionary<char, int>
{
['A'] = 0, // 0000
['P'] = 1, // 0001
@ -26,90 +25,93 @@ namespace BizHawk.Client.Common.cheats
['N'] = 15 // 1111
};
public NesGameGenieDecoder(string code)
public static IDecodeResult Decode(string code)
{
_code = code;
Decode();
}
if (code == null)
{
throw new ArgumentNullException(nameof(code));
}
public int Address { get; private set; }
public int Value { get; private set; }
public int? Compare { get; private set; }
if (code.Length != 6 && code.Length != 8)
{
return new InvalidResult();
}
public void Decode()
{
var result = new DecodeResult { Size = WatchSize.Byte };
// char 3 bit 3 denotes the code length.
if (_code.Length == 6)
if (code.Length == 6)
{
// Char # | 1 | 2 | 3 | 4 | 5 | 6 |
// 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|
// maps to|1|6|7|8|H|2|3|4|-|I|J|K|L|A|B|C|D|M|N|O|5|E|F|G|
Value = 0;
Address = 0x8000;
result.Value = 0;
result.Address = 0x8000;
_gameGenieTable.TryGetValue(_code[0], out var x);
Value |= x & 0x07;
Value |= (x & 0x08) << 4;
GameGenieTable.TryGetValue(code[0], out var x);
result.Value |= x & 0x07;
result.Value |= (x & 0x08) << 4;
_gameGenieTable.TryGetValue(_code[1], out x);
Value |= (x & 0x07) << 4;
Address |= (x & 0x08) << 4;
GameGenieTable.TryGetValue(code[1], out x);
result.Value |= (x & 0x07) << 4;
result.Address |= (x & 0x08) << 4;
_gameGenieTable.TryGetValue(_code[2], out x);
Address |= (x & 0x07) << 4;
GameGenieTable.TryGetValue(code[2], out x);
result.Address |= (x & 0x07) << 4;
_gameGenieTable.TryGetValue(_code[3], out x);
Address |= (x & 0x07) << 12;
Address |= x & 0x08;
GameGenieTable.TryGetValue(code[3], out x);
result.Address |= (x & 0x07) << 12;
result.Address |= x & 0x08;
_gameGenieTable.TryGetValue(_code[4], out x);
Address |= x & 0x07;
Address |= (x & 0x08) << 8;
GameGenieTable.TryGetValue(code[4], out x);
result.Address |= x & 0x07;
result.Address |= (x & 0x08) << 8;
_gameGenieTable.TryGetValue(_code[5], out x);
Address |= (x & 0x07) << 8;
Value |= x & 0x08;
GameGenieTable.TryGetValue(code[5], out x);
result.Address |= (x & 0x07) << 8;
result.Value |= x & 0x08;
}
else if (_code.Length == 8)
else
{
// Char # | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
// 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|1|6|7|8|H|2|3|4|-|I|J|K|L|A|B|C|D|M|N|O|%|E|F|G|!|^|&|*|5|@|#|$|
Value = 0;
Address = 0x8000;
Compare = 0;
result.Value = 0;
result.Address = 0x8000;
result.Compare = 0;
_gameGenieTable.TryGetValue(_code[0], out var x);
Value |= x & 0x07;
Value |= (x & 0x08) << 4;
GameGenieTable.TryGetValue(code[0], out var x);
result.Value |= x & 0x07;
result.Value |= (x & 0x08) << 4;
_gameGenieTable.TryGetValue(_code[1], out x);
Value |= (x & 0x07) << 4;
Address |= (x & 0x08) << 4;
GameGenieTable.TryGetValue(code[1], out x);
result.Value |= (x & 0x07) << 4;
result.Address |= (x & 0x08) << 4;
_gameGenieTable.TryGetValue(_code[2], out x);
Address |= (x & 0x07) << 4;
GameGenieTable.TryGetValue(code[2], out x);
result.Address |= (x & 0x07) << 4;
_gameGenieTable.TryGetValue(_code[3], out x);
Address |= (x & 0x07) << 12;
Address |= x & 0x08;
GameGenieTable.TryGetValue(code[3], out x);
result.Address |= (x & 0x07) << 12;
result.Address |= x & 0x08;
_gameGenieTable.TryGetValue(_code[4], out x);
Address |= x & 0x07;
Address |= (x & 0x08) << 8;
GameGenieTable.TryGetValue(code[4], out x);
result.Address |= x & 0x07;
result.Address |= (x & 0x08) << 8;
_gameGenieTable.TryGetValue(_code[5], out x);
Address |= (x & 0x07) << 8;
Compare |= x & 0x08;
GameGenieTable.TryGetValue(code[5], out x);
result.Address |= (x & 0x07) << 8;
result.Compare |= x & 0x08;
_gameGenieTable.TryGetValue(_code[6], out x);
Compare |= x & 0x07;
Compare |= (x & 0x08) << 4;
GameGenieTable.TryGetValue(code[6], out x);
result.Compare |= x & 0x07;
result.Compare |= (x & 0x08) << 4;
_gameGenieTable.TryGetValue(_code[7], out x);
Compare |= (x & 0x07) << 4;
Value |= x & 0x08;
GameGenieTable.TryGetValue(code[7], out x);
result.Compare |= (x & 0x07) << 4;
result.Value |= x & 0x08;
}
return result;
}
}
}

View File

@ -45,19 +45,11 @@ namespace BizHawk.Client.Common
{
if (NESAvailable && MemoryDomains != null)
{
var decoder = new NesGameGenieDecoder(code);
var watch = Watch.GenerateWatch(
MemoryDomains["System Bus"],
decoder.Address,
WatchSize.Byte,
DisplayType.Hex,
false,
code);
Global.CheatList.Add(new Cheat(
watch,
decoder.Value,
decoder.Compare));
var result = NesGameGenieDecoder.Decode(code);
if (result.IsValid)
{
Global.CheatList.Add(result.ToCheat(MemoryDomains.SystemBus, ""));
}
}
}
@ -127,9 +119,12 @@ namespace BizHawk.Client.Common
{
if (NESAvailable)
{
var decoder = new NesGameGenieDecoder(code);
Global.CheatList.RemoveRange(
Global.CheatList.Where(c => c.Address == decoder.Address));
var decoder = NesGameGenieDecoder.Decode(code);
if (decoder.IsValid)
{
Global.CheatList.RemoveRange(
Global.CheatList.Where(c => c.Address == decoder.Address));
}
}
}

View File

@ -277,21 +277,23 @@ namespace BizHawk.Client.EmuHawk
Global.CheatList.Add(new Cheat(watch, decoder.Value));
}
private void Nes(string cheat)
private void Nes(string code)
{
if (cheat.Length != 6 && cheat.Length != 8)
if (code.Length != 6 && code.Length != 8)
{
InputError("Game Genie codes need to be six or eight characters in length.");
}
var description = Description(cheat);
var decoder = new NesGameGenieDecoder(cheat);
var watch = Watch.GenerateWatch(MemoryDomains["System Bus"], decoder.Address, WatchSize.Byte, Common.DisplayType.Hex, false, description);
Global.CheatList.Add(
decoder.Compare.HasValue
? new Cheat(watch, decoder.Value, decoder.Compare.Value, true, Cheat.CompareType.Equal)
: new Cheat(watch, decoder.Value));
var description = Description(code);
var result = NesGameGenieDecoder.Decode(code);
if (result.IsValid)
{
Global.CheatList.Add(result.ToCheat(MemoryDomains.SystemBus, description));
}
else
{
InputError("Invalid Game Genie code");
}
}
private void Psx(string cheat)