Refactor InputDisplayGenerator + LogEntryGenerator + ControllerDefinition (#3782)

* refactor InputDisplayGenerator and LogEntryGenerator handling
* Fix NRE in Tastudio. I can already see this avalanching into 100 different bugs
* cba, revert 48f4e13de and bring back bullshit dummy default MovieController
* Refactor MnemonicCache + make Bk2LogEntryGenerator and Bk2InputDisplayGenerator static. This should simplify stuff and make the logic clearer
This commit is contained in:
Moritz Bender 2024-09-10 21:19:54 +02:00 committed by GitHub
parent 08bd14e800
commit a0800862b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 241 additions and 403 deletions

View File

@ -50,9 +50,7 @@ namespace BizHawk.Client.Common
return string.Empty; return string.Empty;
} }
var lg = _movieSession.Movie.LogGeneratorInstance( return Bk2LogEntryGenerator.GenerateLogEntry(_movieSession.Movie.GetInputState(frame));
_movieSession.Movie.GetInputState(frame));
return lg.GenerateLogEntry();
} }
public void Save(string filename) public void Save(string filename)

View File

@ -9,8 +9,6 @@ namespace BizHawk.Client.Common
{ {
public class Controller : IController public class Controller : IController
{ {
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public Controller(ControllerDefinition definition) public Controller(ControllerDefinition definition)
{ {
Definition = definition; Definition = definition;
@ -186,4 +184,4 @@ namespace BizHawk.Client.Common
.Select(kvp => kvp.Key) .Select(kvp => kvp.Key)
.ToList(); .ToList();
} }
} }

View File

@ -75,7 +75,7 @@ namespace BizHawk.Client.Common
return sb.ToString(); return sb.ToString();
} }
return _emulator.Frame.ToString(); return _emulator.Frame.ToString();
} }
@ -172,10 +172,10 @@ namespace BizHawk.Client.Common
} }
public string InputStrMovie() public string InputStrMovie()
=> MakeStringFor(_movieSession.MovieController, cache: true); => MakeStringFor(_movieSession.MovieController);
public string InputStrImmediate() public string InputStrImmediate()
=> MakeStringFor(_inputManager.AutofireStickyXorAdapter, cache: true); => MakeStringFor(_inputManager.AutofireStickyXorAdapter);
public string InputPrevious() public string InputPrevious()
{ {
@ -196,15 +196,9 @@ namespace BizHawk.Client.Common
? MakeStringFor(_inputManager.AutofireStickyXorAdapter.Or(_movieSession.Movie.GetInputState(_emulator.Frame - 1))) ? MakeStringFor(_inputManager.AutofireStickyXorAdapter.Or(_movieSession.Movie.GetInputState(_emulator.Frame - 1)))
: InputStrImmediate(); : InputStrImmediate();
private string MakeStringFor(IController controller, bool cache = false) private static string MakeStringFor(IController controller)
{ {
var idg = controller.InputDisplayGenerator; return Bk2InputDisplayGenerator.Generate(controller);
if (idg is null)
{
idg = new Bk2InputDisplayGenerator(_emulator.SystemId, controller);
if (cache) controller.InputDisplayGenerator = idg;
}
return idg.Generate();
} }
public string MakeIntersectImmediatePrevious() public string MakeIntersectImmediatePrevious()
@ -292,7 +286,7 @@ namespace BizHawk.Client.Common
// in order to achieve this we want to avoid drawing anything pink that isn't actually held down right now // in order to achieve this we want to avoid drawing anything pink that isn't actually held down right now
// so we make an AND adapter and combine it using immediate & sticky // so we make an AND adapter and combine it using immediate & sticky
// (adapter creation moved to InputManager) // (adapter creation moved to InputManager)
var autoString = MakeStringFor(_inputManager.WeirdStickyControllerForInputDisplay, cache: true); var autoString = MakeStringFor(_inputManager.WeirdStickyControllerForInputDisplay);
g.DrawString(autoString, autoColor, point.X, point.Y); g.DrawString(autoString, autoColor, point.X, point.Y);
//recolor everything that's changed from the previous input //recolor everything that's changed from the previous input

View File

@ -9,8 +9,6 @@ namespace BizHawk.Client.Common
{ {
public class AutofireController : IController public class AutofireController : IController
{ {
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public AutofireController(IEmulator emulator, int on, int off) public AutofireController(IEmulator emulator, int on, int off)
{ {
On = on < 1 ? 0 : on; On = on < 1 ? 0 : on;

View File

@ -14,8 +14,6 @@ namespace BizHawk.Client.Common
public ControllerDefinition Definition { get; set; } public ControllerDefinition Definition { get; set; }
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public bool IsPressed(string button) => _pressed.Contains(button); public bool IsPressed(string button) => _pressed.Contains(button);
public int AxisValue(string name) => 0; public int AxisValue(string name) => 0;

View File

@ -13,8 +13,6 @@ namespace BizHawk.Client.Common
{ {
public ControllerDefinition Definition { get; } public ControllerDefinition Definition { get; }
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
protected Dictionary<string, int> Axes { get; private set; } = new(); protected Dictionary<string, int> Axes { get; private set; } = new();
protected Dictionary<string, bool> Buttons { get; private set; } = new(); protected Dictionary<string, bool> Buttons { get; private set; } = new();

View File

@ -1,62 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
/// <summary>
/// An implementation of <see cref="IInputDisplayGenerator"/> that
/// uses .bk2 mnemonics as the basis for display
/// </summary>
public class Bk2InputDisplayGenerator : IInputDisplayGenerator
{
/// <remarks>either <c>Range</c> or <c>Mnemonic</c> is always non-null</remarks>
private readonly IReadOnlyList<(string Name, AxisSpec? Range, char? Mnemonic)> _cachedInputSpecs;
private readonly IController _source;
public Bk2InputDisplayGenerator(string systemId, IController source)
{
const string ERR_MSG = nameof(ControllerDefinition.OrderedControlsFlat) + "/" + nameof(ControllerDefinition.ControlsOrdered) + " contains an input name which is neither a button nor an axis";
_cachedInputSpecs = source.Definition.OrderedControlsFlat.Select(button =>
{
if (source.Definition.Axes.TryGetValue(button, out var range)) return (button, range, null);
if (source.Definition.BoolButtons.Contains(button)) return (button, (AxisSpec?) null, (char?) Bk2MnemonicLookup.Lookup(button, systemId));
throw new Exception(ERR_MSG);
}).ToList();
_source = source;
}
public string Generate()
{
var sb = new StringBuilder();
foreach (var (button, range, mnemonicChar) in _cachedInputSpecs)
{
if (range is not null)
{
var val = _source.AxisValue(button);
if (val == range.Value.Neutral)
{
sb.Append(" ");
}
else
{
sb.Append(val.ToString().PadLeft(5, ' ')).Append(',');
}
}
else
{
sb.Append(_source.IsPressed(button)
? mnemonicChar.Value
: ' ');
}
}
return sb.ToString();
}
}
}

View File

@ -0,0 +1,46 @@
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
/// <summary>
/// Generates a display friendly version of the input log entry
/// using .bk2 mnemonics as the basis for display
/// </summary>
public static class Bk2InputDisplayGenerator
{
public static string Generate(IController source)
{
if (source.Definition.MnemonicsCache is null)
throw new InvalidOperationException("Can't generate input display string with empty mnemonics cache");
var sb = new StringBuilder();
foreach ((string buttonName, AxisSpec? axisSpec) in source.Definition.ControlsOrdered.SelectMany(x => x))
{
if (axisSpec.HasValue)
{
int val = source.AxisValue(buttonName);
if (val == axisSpec.Value.Neutral)
{
sb.Append(" ");
}
else
{
sb.Append(val.ToString().PadLeft(5, ' ')).Append(',');
}
}
else
{
sb.Append(source.IsPressed(buttonName)
? source.Definition.MnemonicsCache[buttonName]
: ' ');
}
}
return sb.ToString();
}
}
}

View File

@ -8,8 +8,6 @@ namespace BizHawk.Client.Common
{ {
public ControllerDefinition Definition => Source.Definition; public ControllerDefinition Definition => Source.Definition;
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public bool IsPressed(string button) public bool IsPressed(string button)
{ {
if (Source != null && SourceAnd != null) if (Source != null && SourceAnd != null)
@ -36,8 +34,6 @@ namespace BizHawk.Client.Common
{ {
public ControllerDefinition Definition => Source.Definition; public ControllerDefinition Definition => Source.Definition;
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public bool IsPressed(string button) public bool IsPressed(string button)
{ {
if (Source != null && SourceXor != null) if (Source != null && SourceXor != null)
@ -64,8 +60,6 @@ namespace BizHawk.Client.Common
{ {
public ControllerDefinition Definition => Source.Definition; public ControllerDefinition Definition => Source.Definition;
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public bool IsPressed(string button) public bool IsPressed(string button)
{ {
return (Source?.IsPressed(button) ?? false) return (Source?.IsPressed(button) ?? false)

View File

@ -11,8 +11,6 @@ namespace BizHawk.Client.Common
{ {
public ControllerDefinition Definition => Curr.Definition; public ControllerDefinition Definition => Curr.Definition;
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public bool IsPressed(string button) => Curr.IsPressed(button); public bool IsPressed(string button) => Curr.IsPressed(button);
public int AxisValue(string name) => Curr.AxisValue(name); public int AxisValue(string name) => Curr.AxisValue(name);

View File

@ -54,13 +54,14 @@ namespace BizHawk.Client.Common
public void ResetMainControllers(AutofireController nullAutofireController) public void ResetMainControllers(AutofireController nullAutofireController)
{ {
ActiveController = new(NullController.Instance.Definition); ActiveController = new Controller(NullController.Instance.Definition);
AutoFireController = nullAutofireController; AutoFireController = nullAutofireController;
} }
public void SyncControls(IEmulator emulator, IMovieSession session, Config config) public void SyncControls(IEmulator emulator, IMovieSession session, Config config)
{ {
var def = emulator.ControllerDefinition; var def = emulator.ControllerDefinition;
def.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(emulator.SystemId));
ActiveController = BindToDefinition(def, config.AllTrollers, config.AllTrollersAnalog, config.AllTrollersFeedbacks); ActiveController = BindToDefinition(def, config.AllTrollers, config.AllTrollersAnalog, config.AllTrollersFeedbacks);
AutoFireController = BindToDefinitionAF(emulator, config.AllTrollersAutoFire, config.AutofireOn, config.AutofireOff); AutoFireController = BindToDefinitionAF(emulator, config.AllTrollersAutoFire, config.AutofireOn, config.AutofireOff);
@ -157,4 +158,4 @@ namespace BizHawk.Client.Common
return ret; return ret;
} }
} }
} }

View File

@ -12,8 +12,6 @@ namespace BizHawk.Client.Common
{ {
public ControllerDefinition Definition { get; private set; } public ControllerDefinition Definition { get; private set; }
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
private readonly Dictionary<string, bool> _overrides = new Dictionary<string, bool>(); private readonly Dictionary<string, bool> _overrides = new Dictionary<string, bool>();
private readonly Dictionary<string, int> _axisOverrides = new Dictionary<string, int>(); private readonly Dictionary<string, int> _axisOverrides = new Dictionary<string, int>();
private readonly List<string> _inverses = new List<string>(); private readonly List<string> _inverses = new List<string>();

View File

@ -14,8 +14,6 @@ namespace BizHawk.Client.Common
{ {
public ControllerDefinition Definition => Source.Definition; public ControllerDefinition Definition => Source.Definition;
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public bool IsPressed(string button) public bool IsPressed(string button)
{ {
var source = Source.IsPressed(button); var source = Source.IsPressed(button);
@ -110,8 +108,6 @@ namespace BizHawk.Client.Common
{ {
public ControllerDefinition Definition => Source.Definition; public ControllerDefinition Definition => Source.Definition;
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public bool IsPressed(string button) public bool IsPressed(string button)
{ {
var source = Source.IsPressed(button); var source = Source.IsPressed(button);
@ -216,4 +212,4 @@ namespace BizHawk.Client.Common
_justPressed = buttons; _justPressed = buttons;
} }
} }
} }

View File

@ -17,8 +17,6 @@ namespace BizHawk.Client.Common
public ControllerDefinition Definition => Source.Definition; public ControllerDefinition Definition => Source.Definition;
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public OpposingDirPolicy OpposingDirPolicy { get; set; } public OpposingDirPolicy OpposingDirPolicy { get; set; }
public bool IsPressed(string button) public bool IsPressed(string button)

View File

@ -52,12 +52,13 @@ namespace BizHawk.Client.Common
public IInputAdapter MovieOut { get; } = new CopyControllerAdapter(); public IInputAdapter MovieOut { get; } = new CopyControllerAdapter();
public IStickyAdapter StickySource { get; set; } public IStickyAdapter StickySource { get; set; }
public IMovieController MovieController { get; private set; } = new Bk2Controller("", NullController.Instance.Definition); public IMovieController MovieController { get; private set; } = new Bk2Controller(NullController.Instance.Definition);
public IMovieController GenerateMovieController(ControllerDefinition definition = null) public IMovieController GenerateMovieController(ControllerDefinition definition = null, string logKey = null)
{ {
// TODO: expose Movie.LogKey and pass in here // TODO: should this fallback to Movie.LogKey?
return new Bk2Controller("", definition ?? MovieController.Definition); // this function is kinda weird
return new Bk2Controller(definition ?? MovieController.Definition, logKey);
} }
public void HandleFrameBefore() public void HandleFrameBefore()

View File

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using BizHawk.Common; using BizHawk.Common;
@ -14,38 +13,22 @@ namespace BizHawk.Client.Common
private readonly Dictionary<string, bool> _myBoolButtons = new(); private readonly Dictionary<string, bool> _myBoolButtons = new();
private readonly Bk2ControllerDefinition _type; public Bk2Controller(ControllerDefinition definition, string logKey) : this(definition)
private IReadOnlyList<ControlMap> _controlsOrdered;
private IReadOnlyList<ControlMap> ControlsOrdered
=> _controlsOrdered
??= _type.OrderedControlsFlat.Select(name => new ControlMap(name, _type)).ToArray();
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public Bk2Controller(string key, ControllerDefinition definition) : this(definition)
{ {
if (!string.IsNullOrEmpty(key)) if (!string.IsNullOrEmpty(logKey))
{ Definition = new Bk2ControllerDefinition(definition, logKey);
var groups = key.Split(new[] { "#" }, StringSplitOptions.RemoveEmptyEntries);
_type.ControlsFromLog = groups
.Select(group => group.Split(new[] { "|" }, StringSplitOptions.RemoveEmptyEntries).ToList())
.ToList();
}
} }
public Bk2Controller(ControllerDefinition definition) public Bk2Controller(ControllerDefinition definition)
{ {
_type = new Bk2ControllerDefinition(definition); Definition = definition;
foreach ((string axisName, AxisSpec range) in definition.Axes) foreach ((string axisName, AxisSpec range) in definition.Axes)
{ {
_myAxisControls[axisName] = range.Neutral; _myAxisControls[axisName] = range.Neutral;
} }
} }
public ControllerDefinition Definition => _type; public ControllerDefinition Definition { get; }
public int AxisValue(string name) public int AxisValue(string name)
=> _myAxisControls.GetValueOrDefault(name); => _myAxisControls.GetValueOrDefault(name);
@ -87,16 +70,12 @@ namespace BizHawk.Client.Common
if (string.IsNullOrWhiteSpace(mnemonic)) return; if (string.IsNullOrWhiteSpace(mnemonic)) return;
var iterator = 0; var iterator = 0;
foreach (var key in ControlsOrdered) foreach (var playerControls in Definition.ControlsOrdered)
foreach ((string buttonName, AxisSpec? axisSpec) in playerControls)
{ {
while (mnemonic[iterator] == '|') iterator++; while (mnemonic[iterator] == '|') iterator++;
if (key.IsBool) if (axisSpec.HasValue)
{
_myBoolButtons[key.Name] = mnemonic[iterator] != '.';
iterator++;
}
else if (key.IsAxis)
{ {
var commaIndex = mnemonic.IndexOf(',', iterator); var commaIndex = mnemonic.IndexOf(',', iterator);
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
@ -105,10 +84,15 @@ namespace BizHawk.Client.Common
var axisValueString = mnemonic.Substring(startIndex: iterator, length: commaIndex - iterator); var axisValueString = mnemonic.Substring(startIndex: iterator, length: commaIndex - iterator);
var val = int.Parse(axisValueString); var val = int.Parse(axisValueString);
#endif #endif
_myAxisControls[key.Name] = val; _myAxisControls[buttonName] = val;
iterator = commaIndex + 1; iterator = commaIndex + 1;
} }
else
{
_myBoolButtons[buttonName] = mnemonic[iterator] != '.';
iterator++;
}
} }
} }
@ -122,40 +106,23 @@ namespace BizHawk.Client.Common
_myAxisControls[buttonName] = value; _myAxisControls[buttonName] = value;
} }
private readonly struct ControlMap
{
public readonly bool IsAxis;
public readonly bool IsBool;
public readonly string Name;
public ControlMap(string name, bool isButton, bool isAxis)
{
Debug.Assert(isButton ^ isAxis, "axis conflicts with button of the same name?");
Name = name;
IsBool = isButton;
IsAxis = isAxis;
}
public ControlMap(string name, ControllerDefinition def)
: this(
name: name,
isButton: def.BoolButtons.Contains(name),
isAxis: def.Axes.ContainsKey(name)) {}
}
private class Bk2ControllerDefinition : ControllerDefinition private class Bk2ControllerDefinition : ControllerDefinition
{ {
public IReadOnlyList<IReadOnlyList<string>> ControlsFromLog = null; private readonly IReadOnlyList<IReadOnlyList<(string, AxisSpec?)>> _controlsFromLogKey;
public Bk2ControllerDefinition(ControllerDefinition source) public Bk2ControllerDefinition(ControllerDefinition sourceDefinition, string logKey)
: base(source) : base(sourceDefinition)
{ {
var groups = logKey.Split(new[] { "#" }, StringSplitOptions.RemoveEmptyEntries);
_controlsFromLogKey = groups
.Select(group => group.Split(new[] { "|" }, StringSplitOptions.RemoveEmptyEntries)
.Select(buttonname => (buttonname, sourceDefinition.Axes.TryGetValue(buttonname, out var axisSpec) ? axisSpec : (AxisSpec?)null))
.ToArray())
.ToArray();
} }
protected override IReadOnlyList<IReadOnlyList<string>> GenOrderedControls() protected override IReadOnlyList<IReadOnlyList<(string Name, AxisSpec? AxisSpec)>> GenOrderedControls() => _controlsFromLogKey;
=> ControlsFromLog is not null && ControlsFromLog.Count is not 0 ? ControlsFromLog : base.GenOrderedControls();
} }
} }
} }

View File

@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -6,43 +5,17 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common namespace BizHawk.Client.Common
{ {
// Designed to be able to last the lifetime of an IMovie public static class Bk2LogEntryGenerator
public sealed class Bk2LogEntryGenerator
{ {
private readonly string _systemId; /// <summary>
private readonly IController _source; /// Gets an input log entry that is considered empty. (booleans will be false, axes will be neutral)
/// </summary>
private readonly Dictionary<string, char> _mnemonics = new(); public static string EmptyEntry(IController source) => CreateLogEntry(source, createEmpty: true);
private readonly List<IReadOnlyList<string>> _controlsOrdered;
public Bk2LogEntryGenerator(string systemId, IController source)
{
_systemId = systemId;
_source = source;
_controlsOrdered = _source.Definition.ControlsOrdered.Where(static c => c.Count is not 0).ToList();
foreach (var group in _controlsOrdered) foreach (var button in group)
{
var found = Bk2MnemonicLookup.Lookup(button, _systemId);
try
{
_mnemonics.Add(button, found);
}
catch (ArgumentException e)
{
throw new ArgumentException(innerException: e, paramName: nameof(source), message: $"duplicate KEY {button} in input log mnemonic cache (was {_mnemonics[button]}, attempting to set {found})");
}
}
}
/// <summary> /// <summary>
/// Gets an input log entry that is considered empty. (booleans will be false, axes will be 0) /// Generates an input log entry for the current state of source
/// </summary> /// </summary>
public string EmptyEntry => CreateLogEntry(createEmpty: true); public static string GenerateLogEntry(IController source) => CreateLogEntry(source);
/// <summary>
/// Generates an input log entry for the current state of Source
/// </summary>
public string GenerateLogEntry() => CreateLogEntry();
/// <summary> /// <summary>
/// Generates a human readable key that will specify the names of the /// Generates a human readable key that will specify the names of the
@ -57,55 +30,37 @@ namespace BizHawk.Client.Common
foreach (var group in definition.ControlsOrdered.Where(static c => c.Count is not 0)) foreach (var group in definition.ControlsOrdered.Where(static c => c.Count is not 0))
{ {
sb.Append('#'); sb.Append('#');
foreach (var button in group) foreach ((string buttonName, _) in group)
{ {
sb.Append(button).Append('|'); sb.Append(buttonName).Append('|');
} }
} }
return sb.ToString(); return sb.ToString();
} }
/// <summary> private static string CreateLogEntry(IController source, bool createEmpty = false)
/// Generates a dictionary of button names to their corresponding mnemonic values
/// </summary>
public IDictionary<string, string> Map()
{ {
var dict = new Dictionary<string, string>(); if (!createEmpty && source.Definition.MnemonicsCache is null)
foreach (var button in _source.Definition.OrderedControlsFlat) throw new InvalidOperationException("Can't generate log entry with empty mnemonics cache");
{
if (_source.Definition.BoolButtons.Contains(button))
{
dict.Add(button, Bk2MnemonicLookup.Lookup(button, _systemId).ToString());
}
else if (_source.Definition.Axes.ContainsKey(button))
{
dict.Add(button, Bk2MnemonicLookup.LookupAxis(button, _systemId));
}
}
return dict;
}
private string CreateLogEntry(bool createEmpty = false)
{
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append('|'); sb.Append('|');
foreach (var group in _controlsOrdered) foreach (var group in source.Definition.ControlsOrdered)
{ {
foreach (var button in group) foreach ((string buttonName, var axisSpec) in group)
{ {
if (_source.Definition.Axes.TryGetValue(button, out var range)) if (axisSpec.HasValue)
{ {
var val = createEmpty ? range.Neutral : _source.AxisValue(button); var val = createEmpty ? axisSpec.Value.Neutral : source.AxisValue(buttonName);
sb.Append(val.ToString().PadLeft(5, ' ')).Append(','); sb.Append(val.ToString().PadLeft(5, ' ')).Append(',');
} }
else else
{ {
sb.Append(!createEmpty && _source.IsPressed(button) sb.Append(!createEmpty && source.IsPressed(buttonName)
? _mnemonics[button] ? source.Definition.MnemonicsCache[buttonName]
: '.'); : '.');
} }
} }

View File

@ -1,13 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using BizHawk.Common.StringExtensions; using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Libretro; using BizHawk.Emulation.Cores.Libretro;
// ReSharper disable StyleCop.SA1509
namespace BizHawk.Client.Common namespace BizHawk.Client.Common
{ {
internal static class Bk2MnemonicLookup public static class Bk2MnemonicLookup
{ {
public static char Lookup(string button, string systemId) public static char Lookup(string button, string systemId)
{ {
@ -65,6 +63,8 @@ namespace BizHawk.Client.Common
return button; return button;
} }
public static Func<string, char> MnemonicFunc(string systemId) => buttonName => Lookup(buttonName, systemId);
private static readonly Dictionary<string, char> BaseMnemonicLookupTable = new Dictionary<string, char> private static readonly Dictionary<string, char> BaseMnemonicLookupTable = new Dictionary<string, char>
{ {
["Power"] = 'P', ["Power"] = 'P',

27
src/BizHawk.Client.Common/movie/bk2/Bk2Movie.cs Executable file → Normal file
View File

@ -6,7 +6,7 @@ namespace BizHawk.Client.Common
public partial class Bk2Movie : BasicMovieInfo, IMovie public partial class Bk2Movie : BasicMovieInfo, IMovie
{ {
private Bk2Controller _adapter; private Bk2Controller _adapter;
//private Bk2LogEntryGenerator _logGenerator;
public Bk2Movie(IMovieSession session, string filename) : base(filename) public Bk2Movie(IMovieSession session, string filename) : base(filename)
{ {
Session = session; Session = session;
@ -32,18 +32,6 @@ namespace BizHawk.Client.Common
public virtual bool Changes { get; protected set; } public virtual bool Changes { get; protected set; }
public bool IsCountingRerecords { get; set; } = true; public bool IsCountingRerecords { get; set; } = true;
public Bk2LogEntryGenerator LogGeneratorInstance(IController source)
{
// Hack because initial movie loading is a mess, and you will immediate create a file with an undefined controller
//if (!source.Definition.Any())
{
return new Bk2LogEntryGenerator(Emulator?.SystemId ?? SystemID, source);
}
//_logGenerator ??= new Bk2LogEntryGenerator(Emulator?.SystemId ?? SystemID, source);
//return _logGenerator;
}
public override int FrameCount => Log.Count; public override int FrameCount => Log.Count;
public int InputLogLength => Log.Count; public int InputLogLength => Log.Count;
@ -60,8 +48,7 @@ namespace BizHawk.Client.Common
public void AppendFrame(IController source) public void AppendFrame(IController source)
{ {
var lg = LogGeneratorInstance(source); Log.Add(Bk2LogEntryGenerator.GenerateLogEntry(source));
Log.Add(lg.GenerateLogEntry());
Changes = true; Changes = true;
} }
@ -75,8 +62,7 @@ namespace BizHawk.Client.Common
} }
} }
var lg = LogGeneratorInstance(source); SetFrameAt(frame, Bk2LogEntryGenerator.GenerateLogEntry(source));
SetFrameAt(frame, lg.GenerateLogEntry());
Changes = true; Changes = true;
} }
@ -94,8 +80,8 @@ namespace BizHawk.Client.Common
{ {
if (frame < FrameCount && frame >= -1) if (frame < FrameCount && frame >= -1)
{ {
_adapter ??= new Bk2Controller(LogKey, Session.MovieController.Definition); _adapter ??= new Bk2Controller(Session.MovieController.Definition, LogKey);
_adapter.SetFromMnemonic(frame >= 0 ? Log[frame] : Session.Movie.LogGeneratorInstance(Session.MovieController).EmptyEntry); _adapter.SetFromMnemonic(frame >= 0 ? Log[frame] : Bk2LogEntryGenerator.EmptyEntry(_adapter));
return _adapter; return _adapter;
} }
@ -104,8 +90,7 @@ namespace BizHawk.Client.Common
public virtual void PokeFrame(int frame, IController source) public virtual void PokeFrame(int frame, IController source)
{ {
var lg = LogGeneratorInstance(source); SetFrameAt(frame, Bk2LogEntryGenerator.GenerateLogEntry(source));
SetFrameAt(frame, lg.GenerateLogEntry());
Changes = true; Changes = true;
} }

View File

@ -93,6 +93,7 @@ namespace BizHawk.Client.Common
private void ImportInputFrame(string line) private void ImportInputFrame(string line)
{ {
DeSmuMEControllerDef.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
SimpleController controller = new(DeSmuMEControllerDef); SimpleController controller = new(DeSmuMEControllerDef);
controller["LidOpen"] = false; controller["LidOpen"] = false;

View File

@ -37,6 +37,7 @@ namespace BizHawk.Client.Common.movie.import
NesRightPort = nameof(ControllerNES) NesRightPort = nameof(ControllerNES)
}; };
_deck = controllerSettings.Instantiate((x, y) => true).AddSystemToControllerDef(); _deck = controllerSettings.Instantiate((x, y) => true).AddSystemToControllerDef();
_deck.ControllerDef.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
// 004 4-byte little-endian unsigned int: version number, must be 2 // 004 4-byte little-endian unsigned int: version number, must be 2
uint version = r.ReadUInt32(); uint version = r.ReadUInt32();

View File

@ -26,6 +26,7 @@ namespace BizHawk.Client.Common
}; };
_deck = controllerSettings.Instantiate((x, y) => true).AddSystemToControllerDef(); _deck = controllerSettings.Instantiate((x, y) => true).AddSystemToControllerDef();
_deck.ControllerDef.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
Result.Movie.HeaderEntries[HeaderKeys.Platform] = platform; Result.Movie.HeaderEntries[HeaderKeys.Platform] = platform;
@ -120,6 +121,7 @@ namespace BizHawk.Client.Common
{ {
controllerSettings.NesLeftPort = nameof(ControllerNES); controllerSettings.NesLeftPort = nameof(ControllerNES);
_deck = controllerSettings.Instantiate((x, y) => false).AddSystemToControllerDef(); _deck = controllerSettings.Instantiate((x, y) => false).AddSystemToControllerDef();
_deck.ControllerDef.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
} }
} }
else if (line.StartsWith("port1", StringComparison.OrdinalIgnoreCase)) else if (line.StartsWith("port1", StringComparison.OrdinalIgnoreCase))
@ -128,6 +130,7 @@ namespace BizHawk.Client.Common
{ {
controllerSettings.NesRightPort = nameof(ControllerNES); controllerSettings.NesRightPort = nameof(ControllerNES);
_deck = controllerSettings.Instantiate((x, y) => false).AddSystemToControllerDef(); _deck = controllerSettings.Instantiate((x, y) => false).AddSystemToControllerDef();
_deck.ControllerDef.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
} }
} }
else if (line.StartsWith("port2", StringComparison.OrdinalIgnoreCase)) else if (line.StartsWith("port2", StringComparison.OrdinalIgnoreCase))
@ -148,6 +151,7 @@ namespace BizHawk.Client.Common
} }
_deck = controllerSettings.Instantiate((x, y) => false)/*.AddSystemToControllerDef()*/; //TODO call omitted on purpose? --yoshi _deck = controllerSettings.Instantiate((x, y) => false)/*.AddSystemToControllerDef()*/; //TODO call omitted on purpose? --yoshi
_deck.ControllerDef.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
} }
else else
{ {
@ -157,6 +161,7 @@ namespace BizHawk.Client.Common
syncSettings.Controls = controllerSettings; syncSettings.Controls = controllerSettings;
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings); Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
Result.Movie.LogKey = Bk2LogEntryGenerator.GenerateLogKey(_deck.ControllerDef);
} }
private IControllerDeck _deck; private IControllerDeck _deck;

View File

@ -94,6 +94,7 @@ namespace BizHawk.Client.Common.movie.import
NesRightPort = controller2 ? nameof(ControllerNES) : nameof(UnpluggedNES) NesRightPort = controller2 ? nameof(ControllerNES) : nameof(UnpluggedNES)
}; };
_deck = controllerSettings.Instantiate((x, y) => true).AddSystemToControllerDef(); _deck = controllerSettings.Instantiate((x, y) => true).AddSystemToControllerDef();
_deck.ControllerDef.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
syncSettings.Controls.NesLeftPort = controllerSettings.NesLeftPort; syncSettings.Controls.NesLeftPort = controllerSettings.NesLeftPort;
syncSettings.Controls.NesRightPort = controllerSettings.NesRightPort; syncSettings.Controls.NesRightPort = controllerSettings.NesRightPort;

View File

@ -89,7 +89,8 @@ namespace BizHawk.Client.Common.movie.import
} }
GPGXControlConverter controlConverter = new(input, systemId: VSystemID.Raw.GEN, cdButtons: false); GPGXControlConverter controlConverter = new(input, systemId: VSystemID.Raw.GEN, cdButtons: false);
controlConverter.ControllerDef.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
SimpleController controller = new(controlConverter.ControllerDef); SimpleController controller = new(controlConverter.ControllerDef);
// Unknown. // Unknown.

View File

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.IO;
using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -21,9 +20,9 @@ namespace BizHawk.Client.Common.movie.import
private SimpleController _controller; private SimpleController _controller;
private SimpleController _emptyController; private SimpleController _emptyController;
private readonly string[][] _lsnesGamepadButtons = Enumerable.Range(1, 8) private readonly (string, AxisSpec?)[][] _lsnesGamepadButtons = Enumerable.Range(1, 8)
.Select(player => new[] { "B", "Y", "Select", "Start", "Up", "Down", "Left", "Right", "A", "X", "L", "R" } .Select(player => new[] { "B", "Y", "Select", "Start", "Up", "Down", "Left", "Right", "A", "X", "L", "R" }
.Select(button => $"P{player} {button}").ToArray()) .Select(button => ($"P{player} {button}", (AxisSpec?)null)).ToArray())
.ToArray(); .ToArray();
protected override void RunImport() protected override void RunImport()
@ -85,6 +84,7 @@ namespace BizHawk.Client.Common.movie.import
} }
ControllerDefinition controllerDefinition = new BsnesControllers(ss, true).Definition; ControllerDefinition controllerDefinition = new BsnesControllers(ss, true).Definition;
controllerDefinition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(VSystemID.Raw.SNES));
_emptyController = new SimpleController(controllerDefinition); _emptyController = new SimpleController(controllerDefinition);
_controller = new SimpleController(controllerDefinition); _controller = new SimpleController(controllerDefinition);
_playerCount = controllerDefinition.PlayerCount; _playerCount = controllerDefinition.PlayerCount;
@ -312,8 +312,8 @@ namespace BizHawk.Client.Common.movie.import
{ {
if (player > _playerCount) break; if (player > _playerCount) break;
IReadOnlyList<string> buttons = _controller.Definition.ControlsOrdered[player]; var buttons = _controller.Definition.ControlsOrdered[player];
if (buttons[0].EndsWithOrdinal("Up")) // hack to identify gamepad / multitap which have a different button order in bizhawk compared to lsnes if (buttons[0].Name.EndsWithOrdinal("Up")) // hack to identify gamepad / multitap which have a different button order in bizhawk compared to lsnes
{ {
buttons = _lsnesGamepadButtons[player - 1]; buttons = _lsnesGamepadButtons[player - 1];
} }
@ -323,7 +323,7 @@ namespace BizHawk.Client.Common.movie.import
for (int button = 0; button < buttons.Count; button++) for (int button = 0; button < buttons.Count; button++)
{ {
// Consider the button pressed so long as its spot is not occupied by a ".". // Consider the button pressed so long as its spot is not occupied by a ".".
_controller[buttons[button]] = sections[player][button] != '.'; _controller[buttons[button].Name] = sections[player][button] != '.';
} }
} }
} }

View File

@ -146,6 +146,7 @@ namespace BizHawk.Client.Common.movie.import
{ {
var buttons = new[] { "Up", "Down", "Left", "Right", "B1", "B2", "Run", "Select" }; var buttons = new[] { "Up", "Down", "Left", "Right", "B1", "B2", "Run", "Select" };
_deck.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
SimpleController controllers = new(_deck.Definition); SimpleController controllers = new(_deck.Definition);
// Split up the sections of the frame. // Split up the sections of the frame.

View File

@ -95,6 +95,7 @@ namespace BizHawk.Client.Common.movie.import
var ss = new SMS.SmsSyncSettings(); var ss = new SMS.SmsSyncSettings();
var cd = new SMSControllerDeck(ss.Port1, ss.Port2, isGameGear, ss.UseKeyboard); var cd = new SMSControllerDeck(ss.Port1, ss.Port2, isGameGear, ss.UseKeyboard);
cd.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
SimpleController controllers = new(cd.Definition); SimpleController controllers = new(cd.Definition);
/* /*

View File

@ -181,6 +181,7 @@ namespace BizHawk.Client.Common
OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None
}; };
SimpleController controllers = new(Octoshock.CreateControllerDefinition(settings)); SimpleController controllers = new(Octoshock.CreateControllerDefinition(settings));
controllers.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
string[] buttons = string[] buttons =
{ {
@ -291,6 +292,7 @@ namespace BizHawk.Client.Common
OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None, OctoshockDll.ePeripheralType.None
}; };
SimpleController controllers = new(Octoshock.CreateControllerDefinition(settings)); SimpleController controllers = new(Octoshock.CreateControllerDefinition(settings));
controllers.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
string[] buttons = string[] buttons =
{ {

View File

@ -27,7 +27,7 @@ namespace BizHawk.Client.Common.movie.import
return; return;
} }
Result.Movie.HeaderEntries[HeaderKeys.Platform] = VSystemID.Raw.SNES; Result.Movie.SystemID = VSystemID.Raw.SNES;
// 004 4-byte little-endian unsigned int: version number // 004 4-byte little-endian unsigned int: version number
uint versionNumber = r.ReadUInt32(); uint versionNumber = r.ReadUInt32();
@ -189,6 +189,7 @@ namespace BizHawk.Client.Common.movie.import
} }
ControllerDefinition definition = new Snes9xControllers(ss).ControllerDefinition; ControllerDefinition definition = new Snes9xControllers(ss).ControllerDefinition;
definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
SimpleController controllers = new(definition); SimpleController controllers = new(definition);
Result.Movie.LogKey = Bk2LogEntryGenerator.GenerateLogKey(definition); Result.Movie.LogKey = Bk2LogEntryGenerator.GenerateLogKey(definition);

View File

@ -203,6 +203,7 @@ namespace BizHawk.Client.Common.movie.import
SimpleController controllers = isGBA SimpleController controllers = isGBA
? GbaController() ? GbaController()
: GbController(); : GbController();
controllers.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(isGBA ? VSystemID.Raw.GBA : VSystemID.Raw.GB));
/* /*
* 01 00 A * 01 00 A

View File

@ -106,6 +106,7 @@ namespace BizHawk.Client.Common.movie.import
"Reset", "Power", "Previous Disk", "Next Disk", "P1 Left", "P1 Right", "P1 Up", "P1 Down", "P1 Start", "P1 A", "P1 B", "P1 C", "P1 X", "P1 Y", "P1 Z", "P1 L", "P1 R" "Reset", "Power", "Previous Disk", "Next Disk", "P1 Left", "P1 Right", "P1 Up", "P1 Down", "P1 Start", "P1 A", "P1 B", "P1 C", "P1 X", "P1 Y", "P1 Z", "P1 L", "P1 R"
} }
}.MakeImmutable()); }.MakeImmutable());
controllers.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(Result.Movie.SystemID));
// Split up the sections of the frame. // Split up the sections of the frame.
var sections = line.Split(new[] { "|" }, StringSplitOptions.RemoveEmptyEntries); var sections = line.Split(new[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
@ -128,7 +129,7 @@ namespace BizHawk.Client.Common.movie.import
for (int button = 0; button < buttonNames.Count; button++) for (int button = 0; button < buttonNames.Count; button++)
{ {
// Consider the button pressed so long as its spot is not occupied by a ".". // Consider the button pressed so long as its spot is not occupied by a ".".
controllers[buttonNames[button]] = sections[1][button] != '.'; controllers[buttonNames[button].Name] = sections[1][button] != '.';
} }
} }

View File

@ -7,8 +7,6 @@ namespace BizHawk.Client.Common
{ {
internal class BkmControllerAdapter : IController internal class BkmControllerAdapter : IController
{ {
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public BkmControllerAdapter(ControllerDefinition definition, string systemId) public BkmControllerAdapter(ControllerDefinition definition, string systemId)
{ {
// We do need to map the definition name to the legacy // We do need to map the definition name to the legacy
@ -34,6 +32,7 @@ namespace BizHawk.Client.Common
_ => "Null Controller", _ => "Null Controller",
}; };
Definition = new(copyFrom: definition, withName: name); Definition = new(copyFrom: definition, withName: name);
Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(systemId));
} }
public ControllerDefinition Definition { get; set; } public ControllerDefinition Definition { get; set; }

View File

@ -74,11 +74,6 @@ namespace BizHawk.Client.Common
/// </summary> /// </summary>
void SaveBackup(); void SaveBackup();
/// <summary>
/// Creates a log generator using the given input source
/// </summary>
Bk2LogEntryGenerator LogGeneratorInstance(IController source);
/// <summary> /// <summary>
/// Instructs the movie to save the current contents to Filename /// Instructs the movie to save the current contents to Filename
/// </summary> /// </summary>

View File

@ -48,11 +48,10 @@ namespace BizHawk.Client.Common
/// <summary> /// <summary>
/// Creates a <see cref="IMovieController" /> instance based on the /// Creates a <see cref="IMovieController" /> instance based on the
/// given button definition if provided else the /// given button definition if provided else the current
/// current <see cref="MovieController" /> button definition /// <see cref="MovieController"/>s button definition will be used
/// will be used
/// </summary> /// </summary>
IMovieController GenerateMovieController(ControllerDefinition definition = null); IMovieController GenerateMovieController(ControllerDefinition definition = null, string logKey = null);
void HandleFrameBefore(); void HandleFrameBefore();
void HandleFrameAfter(); void HandleFrameAfter();

View File

@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common namespace BizHawk.Client.Common

View File

@ -20,8 +20,7 @@ namespace BizHawk.Client.Common
ChangeLog.AddGeneralUndo(frame - 1, frame - 1, $"Record Frame: {frame}"); ChangeLog.AddGeneralUndo(frame - 1, frame - 1, $"Record Frame: {frame}");
} }
var lg = LogGeneratorInstance(source); SetFrameAt(frame, Bk2LogEntryGenerator.GenerateLogEntry(source));
SetFrameAt(frame, lg.GenerateLogEntry());
Changes = true; Changes = true;
@ -86,8 +85,7 @@ namespace BizHawk.Client.Common
{ {
ChangeLog.AddGeneralUndo(frame, frame, $"Clear Frame: {frame}"); ChangeLog.AddGeneralUndo(frame, frame, $"Clear Frame: {frame}");
var lg = LogGeneratorInstance(Session.MovieController); SetFrameAt(frame, Bk2LogEntryGenerator.EmptyEntry(Session.MovieController));
SetFrameAt(frame, lg.EmptyEntry);
Changes = true; Changes = true;
InvalidateAfter(frame); InvalidateAfter(frame);
@ -210,8 +208,7 @@ namespace BizHawk.Client.Common
foreach (var input in inputStates) foreach (var input in inputStates)
{ {
var lg = LogGeneratorInstance(input); inputLog.Add(Bk2LogEntryGenerator.GenerateLogEntry(input));
inputLog.Add(lg.GenerateLogEntry());
} }
InsertInput(frame, inputLog); // Sets the ChangeLog InsertInput(frame, inputLog); // Sets the ChangeLog
@ -238,8 +235,7 @@ namespace BizHawk.Client.Common
break; break;
} }
var lg = LogGeneratorInstance(states[i]); var entry = Bk2LogEntryGenerator.GenerateLogEntry(states[i]);
var entry = lg.GenerateLogEntry();
if (firstChangedFrame == -1 && Log[frame + i] != entry) if (firstChangedFrame == -1 && Log[frame + i] != entry)
{ {
firstChangedFrame = frame + i; firstChangedFrame = frame + i;
@ -260,8 +256,7 @@ namespace BizHawk.Client.Common
{ {
frame = Math.Min(frame, Log.Count); frame = Math.Min(frame, Log.Count);
var lg = LogGeneratorInstance(Session.MovieController); Log.InsertRange(frame, Enumerable.Repeat(Bk2LogEntryGenerator.EmptyEntry(Session.MovieController), count));
Log.InsertRange(frame, Enumerable.Repeat(lg.EmptyEntry, count).ToList());
ShiftBindedMarkers(frame, count); ShiftBindedMarkers(frame, count);
@ -280,11 +275,9 @@ namespace BizHawk.Client.Common
Session.MovieController.SetFromSticky(Session.StickySource); Session.MovieController.SetFromSticky(Session.StickySource);
// account for autohold. needs autohold pattern to be already recorded in the current frame // account for autohold. needs autohold pattern to be already recorded in the current frame
var lg = LogGeneratorInstance(Session.MovieController);
for (int i = 0; i < numFrames; i++) for (int i = 0; i < numFrames; i++)
{ {
Log.Add(lg.GenerateLogEntry()); Log.Add(Bk2LogEntryGenerator.GenerateLogEntry(Session.MovieController));
} }
Changes = true; Changes = true;
@ -306,8 +299,7 @@ namespace BizHawk.Client.Common
var adapter = GetInputState(frame); var adapter = GetInputState(frame);
adapter.SetBool(buttonName, !adapter.IsPressed(buttonName)); adapter.SetBool(buttonName, !adapter.IsPressed(buttonName));
var lg = LogGeneratorInstance(adapter); Log[frame] = Bk2LogEntryGenerator.GenerateLogEntry(adapter);
Log[frame] = lg.GenerateLogEntry();
Changes = true; Changes = true;
InvalidateAfter(frame); InvalidateAfter(frame);
@ -325,8 +317,7 @@ namespace BizHawk.Client.Common
var old = adapter.IsPressed(buttonName); var old = adapter.IsPressed(buttonName);
adapter.SetBool(buttonName, val); adapter.SetBool(buttonName, val);
var lg = LogGeneratorInstance(adapter); Log[frame] = Bk2LogEntryGenerator.GenerateLogEntry(adapter);
Log[frame] = lg.GenerateLogEntry();
if (old != val) if (old != val)
{ {
@ -352,8 +343,7 @@ namespace BizHawk.Client.Common
bool old = adapter.IsPressed(buttonName); bool old = adapter.IsPressed(buttonName);
adapter.SetBool(buttonName, val); adapter.SetBool(buttonName, val);
var lg = LogGeneratorInstance(adapter); Log[frame + i] = Bk2LogEntryGenerator.GenerateLogEntry(adapter);
Log[frame + i] = lg.GenerateLogEntry();
if (changed == -1 && old != val) if (changed == -1 && old != val)
{ {
@ -381,8 +371,7 @@ namespace BizHawk.Client.Common
var old = adapter.AxisValue(buttonName); var old = adapter.AxisValue(buttonName);
adapter.SetAxis(buttonName, val); adapter.SetAxis(buttonName, val);
var lg = LogGeneratorInstance(adapter); Log[frame] = Bk2LogEntryGenerator.GenerateLogEntry(adapter);
Log[frame] = lg.GenerateLogEntry();
if (old != val) if (old != val)
{ {
@ -408,8 +397,7 @@ namespace BizHawk.Client.Common
var old = adapter.AxisValue(buttonName); var old = adapter.AxisValue(buttonName);
adapter.SetAxis(buttonName, val); adapter.SetAxis(buttonName, val);
var lg = LogGeneratorInstance(adapter); Log[frame + i] = Bk2LogEntryGenerator.GenerateLogEntry(adapter);
Log[frame + i] = lg.GenerateLogEntry();
if (changed == -1 && old != val) if (changed == -1 && old != val)
{ {

View File

@ -56,15 +56,8 @@ namespace BizHawk.Client.Common
} }
base.Attach(emulator); base.Attach(emulator);
foreach (var button in emulator.ControllerDefinition.BoolButtons)
{
_mnemonicCache[button] = Bk2MnemonicLookup.Lookup(button, emulator.SystemId);
}
} }
private readonly Dictionary<string, char> _mnemonicCache = new Dictionary<string, char>();
public override bool StartsFromSavestate public override bool StartsFromSavestate
{ {
get => base.StartsFromSavestate; get => base.StartsFromSavestate;
@ -146,7 +139,7 @@ namespace BizHawk.Client.Common
public void InvalidateEntireGreenzone() public void InvalidateEntireGreenzone()
=> InvalidateAfter(0); => InvalidateAfter(0);
private (int Frame, IMovieController Controller) _displayCache = (-1, new Bk2Controller("", NullController.Instance.Definition)); private (int Frame, IMovieController Controller) _displayCache = (-1, new Bk2Controller(NullController.Instance.Definition));
/// <summary> /// <summary>
/// Returns the mnemonic value for boolean buttons, and actual value for axes, /// Returns the mnemonic value for boolean buttons, and actual value for axes,
@ -162,12 +155,14 @@ namespace BizHawk.Client.Common
return CreateDisplayValueForButton(_displayCache.Controller, buttonName); return CreateDisplayValueForButton(_displayCache.Controller, buttonName);
} }
private string CreateDisplayValueForButton(IController adapter, string buttonName) private static string CreateDisplayValueForButton(IController adapter, string buttonName)
{ {
// those Contains checks could be avoided by passing in the button type
// this should be considered if this becomes a significant performance issue
if (adapter.Definition.BoolButtons.Contains(buttonName)) if (adapter.Definition.BoolButtons.Contains(buttonName))
{ {
return adapter.IsPressed(buttonName) return adapter.IsPressed(buttonName)
? _mnemonicCache[buttonName].ToString() ? adapter.Definition.MnemonicsCache![buttonName].ToString()
: ""; : "";
} }

View File

@ -58,7 +58,6 @@ namespace BizHawk.Client.EmuHawk
private int _dataSize; private int _dataSize;
private Dictionary<string, double> _cachedControlProbabilities; private Dictionary<string, double> _cachedControlProbabilities;
private Bk2LogEntryGenerator _logGenerator;
private bool _previousDisplayMessage; private bool _previousDisplayMessage;
private bool _previousInvisibleEmulation; private bool _previousInvisibleEmulation;
@ -1001,7 +1000,7 @@ namespace BizHawk.Client.EmuHawk
InputManager.SyncControls(Emulator, MovieSession, Config); InputManager.SyncControls(Emulator, MovieSession, Config);
if (clear_log) { _currentBotAttempt.Log.Clear(); } if (clear_log) { _currentBotAttempt.Log.Clear(); }
_currentBotAttempt.Log.Add(_logGenerator.GenerateLogEntry()); _currentBotAttempt.Log.Add(Bk2LogEntryGenerator.GenerateLogEntry(InputManager.ClickyVirtualPadController));
} }
private void StartBot() private void StartBot()
@ -1027,7 +1026,6 @@ namespace BizHawk.Client.EmuHawk
MovieSession.Movie.IsCountingRerecords = false; MovieSession.Movie.IsCountingRerecords = false;
} }
_logGenerator = MovieSession.Movie.LogGeneratorInstance(InputManager.ClickyVirtualPadController);
_cachedControlProbabilities = ControlProbabilities; _cachedControlProbabilities = ControlProbabilities;
_doNotUpdateValues = true; _doNotUpdateValues = true;

View File

@ -34,8 +34,6 @@ namespace BizHawk.Client.EmuHawk
// Get a IController that only contains buttons in key. // Get a IController that only contains buttons in key.
InitController(_inputKey); InitController(_inputKey);
var logGenerator = movieSession.Movie.LogGeneratorInstance(_controller);
string movieKey = Bk2LogEntryGenerator.GenerateLogKey(_controller.Definition).Replace("#", ""); string movieKey = Bk2LogEntryGenerator.GenerateLogKey(_controller.Definition).Replace("#", "");
movieKey = movieKey.Substring(startIndex: 0, length: movieKey.Length - 1); // drop last char movieKey = movieKey.Substring(startIndex: 0, length: movieKey.Length - 1); // drop last char
if (key == movieKey) if (key == movieKey)
@ -50,7 +48,7 @@ namespace BizHawk.Client.EmuHawk
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
_controller.SetFrom(movieSession.Movie.GetInputState(i + start)); _controller.SetFrom(movieSession.Movie.GetInputState(i + start));
_log[i] = logGenerator.GenerateLogEntry(); _log[i] = Bk2LogEntryGenerator.GenerateLogEntry(_controller);
} }
} }
} }
@ -115,18 +113,15 @@ namespace BizHawk.Client.EmuHawk
} }
var newController = _movieSession.GenerateMovieController(d.MakeImmutable()); var newController = _movieSession.GenerateMovieController(d.MakeImmutable());
var logGenerator = _movieSession.Movie.LogGeneratorInstance(newController);
logGenerator.GenerateLogEntry(); // Reference and create all buttons.
// Reset all buttons in targetController (it may still have buttons that aren't being set here set true) // Reset all buttons in targetController (it may still have buttons that aren't being set here set true)
var tC = _movieSession.Movie.LogGeneratorInstance(_targetController); _targetController.SetFromMnemonic(Bk2LogEntryGenerator.EmptyEntry(_targetController));
_targetController.SetFromMnemonic(tC.EmptyEntry);
for (int i = 0; i < Length; i++) for (int i = 0; i < Length; i++)
{ {
_controller.SetFromMnemonic(_log[i]); _controller.SetFromMnemonic(_log[i]);
LatchFromSourceButtons(_targetController, _controller); LatchFromSourceButtons(_targetController, _controller);
newController.SetFrom(_targetController); newController.SetFrom(_targetController);
_log[i] = logGenerator.GenerateLogEntry(); _log[i] = Bk2LogEntryGenerator.GenerateLogEntry(newController);
} }
_controller = newController; _controller = newController;

View File

@ -418,7 +418,7 @@ namespace BizHawk.Client.EmuHawk
} }
_tasClipboard.Add(new TasClipboardEntry(index, input)); _tasClipboard.Add(new TasClipboardEntry(index, input));
var logEntry = CurrentTasMovie.LogGeneratorInstance(input).GenerateLogEntry(); var logEntry = Bk2LogEntryGenerator.GenerateLogEntry(input);
sb.AppendLine(Settings.CopyIncludesFrameNo ? $"{FrameToStringPadded(index)} {logEntry}" : logEntry); sb.AppendLine(Settings.CopyIncludesFrameNo ? $"{FrameToStringPadded(index)} {logEntry}" : logEntry);
} }
@ -537,8 +537,7 @@ namespace BizHawk.Client.EmuHawk
} }
_tasClipboard.Add(new TasClipboardEntry(index, input)); _tasClipboard.Add(new TasClipboardEntry(index, input));
var lg = CurrentTasMovie.LogGeneratorInstance(input); sb.AppendLine(Bk2LogEntryGenerator.GenerateLogEntry(input));
sb.AppendLine(lg.GenerateLogEntry());
} }
Clipboard.SetDataObject(sb.ToString()); Clipboard.SetDataObject(sb.ToString());

View File

@ -7,7 +7,6 @@ using System.ComponentModel;
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Client.EmuHawk.ToolExtensions; using BizHawk.Client.EmuHawk.ToolExtensions;
using BizHawk.Client.EmuHawk.Properties; using BizHawk.Client.EmuHawk.Properties;
using BizHawk.Common;
using BizHawk.Common.StringExtensions; using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
@ -351,31 +350,17 @@ namespace BizHawk.Client.EmuHawk
Rotatable = true, Rotatable = true,
}); });
var columnNames = MovieSession.Movie foreach ((string name, string mnemonic0, int maxLength) in MnemonicMap())
.LogGeneratorInstance(MovieSession.MovieController)
.Map();
foreach (var (name, mnemonic0) in columnNames)
{ {
var mnemonic = Emulator.SystemId is VSystemID.Raw.N64 && N64CButtonSuffixes.Any(name.EndsWithOrdinal) var mnemonic = Emulator.SystemId is VSystemID.Raw.N64 && N64CButtonSuffixes.Any(name.EndsWithOrdinal)
? $"c{mnemonic0.ToUpperInvariant()}" // prepend 'c' to differentiate from L/R buttons -- this only affects the column headers ? $"c{mnemonic0.ToUpperInvariant()}" // prepend 'c' to differentiate from L/R buttons -- this only affects the column headers
: mnemonic0; : mnemonic0;
ColumnType type;
int digits; var type = ControllerType.Axes.ContainsKey(name) ? ColumnType.Axis : ColumnType.Boolean;
if (ControllerType.Axes.TryGetValue(name, out var range))
{
type = ColumnType.Axis;
digits = Math.Max(mnemonic.Length, range.MaxDigits);
}
else
{
type = ColumnType.Boolean;
digits = mnemonic.Length;
}
TasView.AllColumns.Add(new( TasView.AllColumns.Add(new(
name: name, name: name,
widthUnscaled: (digits * 6) + 14, // magic numbers reused in EditBranchTextPopUp() --feos // not since eb63fa5a9 (before 2.3.3) --yoshi widthUnscaled: (maxLength * 6) + 14, // magic numbers reused in EditBranchTextPopUp() --feos // not since eb63fa5a9 (before 2.3.3) --yoshi
type: type, type: type,
text: mnemonic)); text: mnemonic));
} }
@ -497,8 +482,7 @@ namespace BizHawk.Client.EmuHawk
{ {
get get
{ {
var lg = CurrentTasMovie.LogGeneratorInstance(MovieSession.MovieController); var empty = Bk2LogEntryGenerator.EmptyEntry(MovieSession.MovieController);
var empty = lg.EmptyEntry;
foreach (var row in TasView.SelectedRows) foreach (var row in TasView.SelectedRows)
{ {
if (CurrentTasMovie[row].LogEntry != empty) if (CurrentTasMovie[row].LogEntry != empty)
@ -1258,5 +1242,27 @@ namespace BizHawk.Client.EmuHawk
return null; return null;
} }
} }
private IEnumerable<(string Name, string Mnemonic, int MaxLength)> MnemonicMap()
{
if (MovieSession.MovieController.Definition.MnemonicsCache is null)
throw new InvalidOperationException("Can't build mnemonic map with empty mnemonics cache");
foreach (var playerControls in MovieSession.MovieController.Definition.ControlsOrdered)
{
foreach ((string name, AxisSpec? axisSpec) in playerControls)
{
if (axisSpec.HasValue)
{
string mnemonic = Bk2MnemonicLookup.LookupAxis(name, MovieSession.Movie.SystemID);
yield return (name, mnemonic, Math.Max(mnemonic.Length, axisSpec.Value.MaxDigits));
}
else
{
yield return (name, MovieSession.MovieController.Definition.MnemonicsCache[name].ToString(), 1);
}
}
}
}
} }
} }

View File

@ -1,6 +1,4 @@
#nullable disable using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -21,38 +19,48 @@ namespace BizHawk.Emulation.Common
private bool _mutable = true; private bool _mutable = true;
private IReadOnlyList<IReadOnlyList<string>> _orderedControls = null; private IReadOnlyList<IReadOnlyList<(string, AxisSpec?)>>? _orderedControls;
private IReadOnlyList<string> _orderedControlsFlat = null;
/// <summary>starts with console buttons, then each player's buttons individually</summary> /// <summary>starts with console buttons, then each player's buttons individually</summary>
public IReadOnlyList<IReadOnlyList<string>> ControlsOrdered public IReadOnlyList<IReadOnlyList<(string Name, AxisSpec? AxisSpec)>> ControlsOrdered
{ {
get get
{ {
if (_orderedControls is not null) return _orderedControls; if (_orderedControls is not null) return _orderedControls;
if (!_mutable) return _orderedControls = GenOrderedControls(); if (!_mutable) return _orderedControls = GenOrderedControls();
const string ERR_MSG = "this " + nameof(ControllerDefinition) + " has not yet been built and sealed, so it is not safe to enumerate this while it could still be mutated"; const string ERR_MSG = $"this {nameof(ControllerDefinition)} has not yet been built and sealed, so it is not safe to enumerate this while it could still be mutated";
throw new InvalidOperationException(ERR_MSG); throw new InvalidOperationException(ERR_MSG);
} }
} }
public readonly string Name; public readonly string Name;
public IReadOnlyList<string> OrderedControlsFlat => _orderedControlsFlat ??= ControlsOrdered.SelectMany(static s => s).ToList(); private Dictionary<string, char>? _mnemonicsCache;
public IReadOnlyDictionary<string, char>? MnemonicsCache => _mnemonicsCache;
/// <remarks>
/// TODO: this should probably be called in <see cref="MakeImmutable"/>,
/// but the needed Bk2MnemonicsLookup is in Client.Common
/// </remarks>
public void BuildMnemonicsCache(Func<string, char> mnemonicFunc)
{
if (_mutable)
throw new InvalidOperationException($"this {nameof(ControllerDefinition)} has not yet been built and sealed; can't build mnemonics cache");
_mnemonicsCache ??= BoolButtons.ToDictionary(buttonName => buttonName, mnemonicFunc);
}
public ControllerDefinition(string name) public ControllerDefinition(string name)
=> Name = name; => Name = name;
public ControllerDefinition(ControllerDefinition copyFrom, string withName = null) public ControllerDefinition(ControllerDefinition copyFrom, string? withName = null)
: this(withName ?? copyFrom.Name) : this(withName ?? copyFrom.Name)
{ {
BoolButtons.AddRange(copyFrom.BoolButtons); BoolButtons.AddRange(copyFrom.BoolButtons);
foreach (var kvp in copyFrom.Axes) Axes.Add(kvp); foreach (var kvp in copyFrom.Axes) Axes.Add(kvp);
HapticsChannels.AddRange(copyFrom.HapticsChannels); HapticsChannels.AddRange(copyFrom.HapticsChannels);
CategoryLabels = copyFrom.CategoryLabels; CategoryLabels = copyFrom.CategoryLabels;
// Do not clone _orderedControls, as GenOrderedControls may be overridden by the derived class _mnemonicsCache = copyFrom._mnemonicsCache;
// _orderedControls = copyFrom._orderedControls;
MakeImmutable(); MakeImmutable();
} }
@ -105,11 +113,12 @@ namespace BizHawk.Emulation.Common
if (!_mutable) throw new InvalidOperationException(ERR_MSG); if (!_mutable) throw new InvalidOperationException(ERR_MSG);
} }
protected virtual IReadOnlyList<IReadOnlyList<string>> GenOrderedControls() protected virtual IReadOnlyList<IReadOnlyList<(string Name, AxisSpec? AxisSpec)>> GenOrderedControls()
{ {
var ret = new List<string>[PlayerCount + 1]; var ret = new List<(string, AxisSpec?)>[PlayerCount + 1];
for (var i = 0; i < ret.Length; i++) ret[i] = new(); for (var i = 0; i < ret.Length; i++) ret[i] = new();
foreach (var btn in Axes.Keys.Concat(BoolButtons)) ret[PlayerNumber(btn)].Add(btn); foreach ((string buttonName, var axisSpec) in Axes) ret[PlayerNumber(buttonName)].Add((buttonName, axisSpec));
foreach (var btn in BoolButtons) ret[PlayerNumber(btn)].Add((btn, null));
return ret; return ret;
} }

View File

@ -13,8 +13,6 @@ namespace BizHawk.Emulation.Common
{ {
public ControllerDefinition Definition { get; } = new ControllerDefinition("Null Controller").MakeImmutable(); public ControllerDefinition Definition { get; } = new ControllerDefinition("Null Controller").MakeImmutable();
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public bool IsPressed(string button) => false; public bool IsPressed(string button) => false;
public int AxisValue(string name) => 0; public int AxisValue(string name) => 0;
@ -23,6 +21,7 @@ namespace BizHawk.Emulation.Common
public void SetHapticChannelStrength(string name, int strength) {} public void SetHapticChannelStrength(string name, int strength) {}
public static readonly NullController Instance = new NullController(); public static readonly NullController Instance = new();
private NullController() {}
} }
} }

View File

@ -69,8 +69,6 @@ namespace BizHawk.Emulation.Common
private readonly IController _src; private readonly IController _src;
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public DummyController( public DummyController(
IController src, IController src,
IReadOnlyDictionary<string, string> buttonAxisRemaps) IReadOnlyDictionary<string, string> buttonAxisRemaps)

View File

@ -11,9 +11,6 @@ namespace BizHawk.Emulation.Common
/// </summary> /// </summary>
ControllerDefinition Definition { get; } ControllerDefinition Definition { get; }
/// <summary>used as cache by frontend; implement as autoprop w/ initial value <see langword="null"/></summary>
IInputDisplayGenerator InputDisplayGenerator { get; set; }
/// <seealso cref="SetHapticChannelStrength"/> /// <seealso cref="SetHapticChannelStrength"/>
IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot(); IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot();

View File

@ -1,10 +0,0 @@
namespace BizHawk.Emulation.Common
{
public interface IInputDisplayGenerator
{
/// <summary>
/// Generates a display friendly version of the input log entry
/// </summary>
string Generate();
}
}

View File

@ -15,8 +15,6 @@ namespace BizHawk.Emulation.Common
{ {
private readonly Dictionary<string, int> _buttons = new(); private readonly Dictionary<string, int> _buttons = new();
public IInputDisplayGenerator InputDisplayGenerator { get; set; } = null;
public SaveController() public SaveController()
{ {
Definition = null; Definition = null;

View File

@ -112,18 +112,18 @@ namespace BizHawk.Emulation.Cores.Libretro
MakeImmutable(); MakeImmutable();
} }
protected override IReadOnlyList<IReadOnlyList<string>> GenOrderedControls() protected override IReadOnlyList<IReadOnlyList<(string Name, AxisSpec? AxisSpec)>> GenOrderedControls()
{ {
// all this is to remove the keyboard buttons from P0 and put them in P3 so they appear at the end of the input display // all this is to remove the keyboard buttons from P0 and put them in P3 so they appear at the end of the input display
var players = base.GenOrderedControls().ToList(); var players = base.GenOrderedControls().ToList();
List<string> retroKeyboard = new(); List<(string, AxisSpec?)> retroKeyboard = new();
var p0 = (List<string>) players[0]; var p0 = (List<(string, AxisSpec?)>) players[0];
for (var i = 0; i < p0.Count; /* incremented in body */) for (var i = 0; i < p0.Count; /* incremented in body */)
{ {
var buttonName = p0[i]; (string ButtonName, AxisSpec?) button = p0[i];
if (CategoryLabels.TryGetValue(buttonName, out var v) && v is CAT_KEYBOARD) if (CategoryLabels.TryGetValue(button.ButtonName, out var v) && v is CAT_KEYBOARD)
{ {
retroKeyboard.Add(buttonName); retroKeyboard.Add(button);
p0.RemoveAt(i); p0.RemoveAt(i);
} }
else else

View File

@ -15,18 +15,19 @@ namespace BizHawk.Tests.Client.Common.Display
public void Initializer() public void Initializer()
{ {
_boolController = new(new ControllerDefinition("Dummy Gamepad") { BoolButtons = { "A" } }.MakeImmutable()); _boolController = new(new ControllerDefinition("Dummy Gamepad") { BoolButtons = { "A" } }.MakeImmutable());
_boolController.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(VSystemID.Raw.NULL));
_axisController = new( _axisController = new(
new ControllerDefinition("Dummy Gamepad") new ControllerDefinition("Dummy Gamepad")
.AddXYPair("Stick{0}", AxisPairOrientation.RightAndUp, 0.RangeTo(200), MidValue) .AddXYPair("Stick{0}", AxisPairOrientation.RightAndUp, 0.RangeTo(200), MidValue)
.MakeImmutable()); .MakeImmutable());
_axisController.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(VSystemID.Raw.NULL));
} }
[TestMethod] [TestMethod]
public void Generate_BoolPressed_GeneratesMnemonic() public void Generate_BoolPressed_GeneratesMnemonic()
{ {
_boolController["A"] = true; _boolController["A"] = true;
var displayGenerator = new Bk2InputDisplayGenerator("NES", _boolController); var actual = Bk2InputDisplayGenerator.Generate(_boolController);
var actual = displayGenerator.Generate();
Assert.AreEqual("A", actual); Assert.AreEqual("A", actual);
} }
@ -34,16 +35,14 @@ namespace BizHawk.Tests.Client.Common.Display
public void Generate_BoolUnPressed_GeneratesSpace() public void Generate_BoolUnPressed_GeneratesSpace()
{ {
_boolController["A"] = false; _boolController["A"] = false;
var displayGenerator = new Bk2InputDisplayGenerator("NES", _boolController); var actual = Bk2InputDisplayGenerator.Generate(_boolController);
var actual = displayGenerator.Generate();
Assert.AreEqual(" ", actual); Assert.AreEqual(" ", actual);
} }
[TestMethod] [TestMethod]
public void Generate_Floats() public void Generate_Floats()
{ {
var displayGenerator = new Bk2InputDisplayGenerator("NES", _axisController); var actual = Bk2InputDisplayGenerator.Generate(_axisController);
var actual = displayGenerator.Generate();
Assert.AreEqual(" 0, 0,", actual); Assert.AreEqual(" 0, 0,", actual);
} }
@ -51,8 +50,7 @@ namespace BizHawk.Tests.Client.Common.Display
public void Generate_MidRangeDisplaysEmpty() public void Generate_MidRangeDisplaysEmpty()
{ {
_axisController.AcceptNewAxis("StickX", MidValue); _axisController.AcceptNewAxis("StickX", MidValue);
var displayGenerator = new Bk2InputDisplayGenerator("NES", _axisController); var actual = Bk2InputDisplayGenerator.Generate(_axisController);
var actual = displayGenerator.Generate();
Assert.AreEqual(" 0,", actual); Assert.AreEqual(" 0,", actual);
} }
} }

View File

@ -14,19 +14,21 @@ namespace BizHawk.Tests.Client.Common.Movie
public void Initializer() public void Initializer()
{ {
_boolController = new(new ControllerDefinition("Dummy Gamepad") { BoolButtons = { "A" } }.MakeImmutable()); _boolController = new(new ControllerDefinition("Dummy Gamepad") { BoolButtons = { "A" } }.MakeImmutable());
_boolController.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(VSystemID.Raw.NES));
_axisController = new( _axisController = new(
new ControllerDefinition("Dummy Gamepad") new ControllerDefinition("Dummy Gamepad")
.AddXYPair("Stick{0}", AxisPairOrientation.RightAndUp, 0.RangeTo(200), 100) .AddXYPair("Stick{0}", AxisPairOrientation.RightAndUp, 0.RangeTo(200), 100)
.MakeImmutable()); .MakeImmutable());
_axisController.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(VSystemID.Raw.NES));
} }
[TestMethod] [TestMethod]
public void GenerateLogEntry_ExclamationForUnknownButtons() public void GenerateLogEntry_ExclamationForUnknownButtons()
{ {
SimpleController controller = new(new ControllerDefinition("Dummy Gamepad") { BoolButtons = { "Unknown Button" } }.MakeImmutable()); SimpleController controller = new(new ControllerDefinition("Dummy Gamepad") { BoolButtons = { "Unknown Button" } }.MakeImmutable());
var lg = new Bk2LogEntryGenerator("NES", controller); controller.Definition.BuildMnemonicsCache(Bk2MnemonicLookup.MnemonicFunc(VSystemID.Raw.NES));
controller["Unknown Button"] = true; controller["Unknown Button"] = true;
var actual = lg.GenerateLogEntry(); var actual = Bk2LogEntryGenerator.GenerateLogEntry(controller);
Assert.AreEqual("|!|", actual); Assert.AreEqual("|!|", actual);
} }
@ -34,8 +36,7 @@ namespace BizHawk.Tests.Client.Common.Movie
public void GenerateLogEntry_BoolPressed_GeneratesMnemonic() public void GenerateLogEntry_BoolPressed_GeneratesMnemonic()
{ {
_boolController["A"] = true; _boolController["A"] = true;
var lg = new Bk2LogEntryGenerator("NES", _boolController); var actual = Bk2LogEntryGenerator.GenerateLogEntry(_boolController);
var actual = lg.GenerateLogEntry();
Assert.AreEqual("|A|", actual); Assert.AreEqual("|A|", actual);
} }
@ -43,17 +44,15 @@ namespace BizHawk.Tests.Client.Common.Movie
public void GenerateLogEntry_BoolUnPressed_GeneratesPeriod() public void GenerateLogEntry_BoolUnPressed_GeneratesPeriod()
{ {
_boolController["A"] = false; _boolController["A"] = false;
var lg = new Bk2LogEntryGenerator("NES", _boolController); var actual = Bk2LogEntryGenerator.GenerateLogEntry(_boolController);
var actual = lg.GenerateLogEntry();
Assert.AreEqual("|.|", actual); Assert.AreEqual("|.|", actual);
} }
[TestMethod] [TestMethod]
public void GenerateLogEntry_Floats() public void GenerateLogEntry_Floats()
{ {
var lg = new Bk2LogEntryGenerator("NES", _axisController); var actual = Bk2LogEntryGenerator.GenerateLogEntry(_axisController);
var actual = lg.GenerateLogEntry();
Assert.AreEqual("| 0, 0,|", actual); Assert.AreEqual("| 0, 0,|", actual);
} }
} }
} }