fix .bkm import

- closes #3909

Effectively best-effort for now, may or may not work for some movies.
This commit is contained in:
Morilli 2025-02-18 01:07:11 +01:00
parent d3362f5e0c
commit 85e2d18780
5 changed files with 281 additions and 734 deletions

View File

@ -1,8 +1,9 @@
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores;
namespace BizHawk.Client.Common.movie.import
{
// ReSharper disable once UnusedMember.Global
[ImporterFor("BizHawk", ".bkm")]
internal class BkmImport : MovieImporter
{
@ -13,15 +14,20 @@ namespace BizHawk.Client.Common.movie.import
for (var i = 0; i < bkm.InputLogLength; i++)
{
// TODO: this is currently broken because Result.Movie.Emulator is no longer getting set,
// however using that was sketchy anyway because it relied on the currently loaded core for import
var input = bkm.GetInputState(i, Result.Movie.Emulator.ControllerDefinition, bkm.Header[HeaderKeys.Platform]);
var input = bkm.GetInputState(i, bkm.Header[HeaderKeys.Platform]);
Result.Movie.AppendFrame(input);
}
Result.Movie.LogKey = bkm.GenerateLogKey;
Result.Movie.HeaderEntries.Clear();
foreach (var (k, v) in bkm.Header) Result.Movie.HeaderEntries[k] = v;
// migrate some stuff, probably incomplete
if (Result.Movie.HeaderEntries[HeaderKeys.Core] is "QuickNes") Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.QuickNes;
if (Result.Movie.HeaderEntries[HeaderKeys.Core] is "EMU7800") Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.A7800Hawk;
if (Result.Movie.HeaderEntries[HeaderKeys.Platform] is "DGB") Result.Movie.HeaderEntries[HeaderKeys.Platform] = VSystemID.Raw.GBL;
Result.Movie.SyncSettingsJson = bkm.SyncSettingsJson;
Result.Movie.Comments.Clear();

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Emulation.Common;
@ -7,32 +8,10 @@ namespace BizHawk.Client.Common
{
internal class BkmControllerAdapter : IController
{
public BkmControllerAdapter(ControllerDefinition definition, string systemId)
public BkmControllerAdapter(string systemId)
{
// We do need to map the definition name to the legacy
// controller names that were used back in the bkm days
var name = systemId switch
{
"Lynx" => "Lynx Controller",
"SNES" => "SNES Controller",
"C64" => "Commodore 64 Controller",
"GBA" => "GBA Controller",
"A78" => "Atari 7800 ProLine Joystick Controller",
"DGB" => "Dual Gameboy Controller",
"WSWAN" => "WonderSwan Controller",
"N64" => "Nintendo 64 Controller",
"SAT" => "Saturn Controller",
"GEN" => "GPGX Genesis Controller",
"NES" => "NES Controller",
"GB" => "Gameboy Controller",
"A26" => "Atari 2600 Basic Controller",
"TI83" => "TI83 Controller",
"Coleco" => "ColecoVision Basic Controller",
"SMS Controller" => "SMS",
_ => "Null Controller",
};
Definition = new(copyFrom: definition, withName: name);
Definition.BuildMnemonicsCache(systemId); //TODO these aren't the same...
Definition = BkmMnemonicConstants.ControllerDefinitions[systemId];
Definition.BuildMnemonicsCache(systemId);
}
public ControllerDefinition Definition { get; set; }
@ -43,167 +22,126 @@ namespace BizHawk.Client.Common
public int AxisValue(string name)
=> _myAxisControls.GetValueOrDefault(name);
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => throw new NotImplementedException(); // no idea --yoshi
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => throw new NotSupportedException();
public void SetHapticChannelStrength(string name, int strength) => throw new NotImplementedException(); // no idea --yoshi
public void SetHapticChannelStrength(string name, int strength) => throw new NotSupportedException();
private void SetFromMnemonic(ReadOnlySpan<char> mnemonic)
{
if (mnemonic.IsEmpty) return;
var iterator = 0;
foreach ((string buttonName, AxisSpec? axisSpec) in Definition.ControlsOrdered.Skip(1).SelectMany(static x => x))
{
while (mnemonic[iterator] == '|') iterator++;
if (axisSpec.HasValue)
{
var separatorIndex = iterator + mnemonic[iterator..].IndexOfAny(',', '|');
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
var val = int.Parse(mnemonic[iterator..separatorIndex]);
#else
var axisValueString = mnemonic[iterator..separatorIndex].ToString();
var val = int.Parse(axisValueString);
#endif
_myAxisControls[buttonName] = val;
iterator = separatorIndex + 1;
}
else
{
_myBoolButtons[buttonName] = mnemonic[iterator] != '.';
iterator++;
}
}
}
/// <summary>
/// latches all buttons from the supplied mnemonic string
/// </summary>
public void SetControllersAsMnemonic(string mnemonic)
{
switch (ControlType)
// _myBoolButtons.Clear();
switch (Definition.Name)
{
case "Null Controller":
return;
case "Lynx Controller":
SetLynxControllersAsMnemonic(mnemonic);
return;
case "SNES Controller":
SetSNESControllersAsMnemonic(mnemonic);
return;
case "Commodore 64 Controller":
SetC64ControllersAsMnemonic(mnemonic);
return;
case "Gameboy Controller":
Force("Power", mnemonic[1] == 'P');
SetFromMnemonic(mnemonic.AsSpan(3));
break;
case "GBA Controller":
SetGBAControllersAsMnemonic(mnemonic);
return;
case "Atari 7800 ProLine Joystick Controller":
SetAtari7800AsMnemonic(mnemonic);
return;
case "Dual Gameboy Controller":
SetDualGameBoyControllerAsMnemonic(mnemonic);
return;
case "WonderSwan Controller":
SetWonderSwanControllerAsMnemonic(mnemonic);
return;
case "Nintendo 64 Controller":
SetN64ControllersAsMnemonic(mnemonic);
return;
case "Saturn Controller":
SetSaturnControllersAsMnemonic(mnemonic);
return;
Force("Power", mnemonic[1] == 'P');
SetFromMnemonic(mnemonic.AsSpan(3));
break;
case "GPGX Genesis Controller":
{
if (IsGenesis6Button())
{
SetGenesis6ControllersAsMnemonic(mnemonic);
}
else
{
SetGenesis3ControllersAsMnemonic(mnemonic);
}
return;
}
}
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
int start = 3;
if (ControlType == "NES Controller")
{
if (mnemonic.Length < 2)
{
return;
}
switch (mnemonic[1])
{
case 'P':
Force("Power", true);
break;
case 'E':
Force("FDS Eject", true);
break;
case '0':
Force("FDS Insert 0", true);
break;
case '1':
Force("FDS Insert 1", true);
break;
case '2':
Force("FDS Insert 2", true);
break;
case '3':
Force("FDS Insert 3", true);
break;
case 'c':
Force("VS Coin 1", true);
break;
case 'C':
Force("VS Coin 2", true);
break;
default:
{
if (mnemonic[1] != '.')
{
Force("Reset", true);
}
break;
}
}
}
if (ControlType == "Gameboy Controller")
{
if (mnemonic.Length < 2)
{
return;
}
Force("Power", mnemonic[1] != '.');
}
if (ControlType == "SMS Controller" || ControlType == "TI83 Controller" || ControlType == "ColecoVision Basic Controller")
{
start = 1;
}
if (ControlType == "Atari 2600 Basic Controller")
{
if (mnemonic.Length < 2)
{
return;
}
Force("Reset", mnemonic[1] != '.' && mnemonic[1] != '0');
Force("Select", mnemonic[2] != '.' && mnemonic[2] != '0');
start = 4;
}
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int ctr = start;
if (mnemonic.Length < srcIndex + ctr + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
string prefix = "";
if (ControlType != "Gameboy Controller" && ControlType != "TI83 Controller")
{
prefix = $"P{player} ";
}
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force(prefix + button, c[srcIndex + ctr++]);
}
}
if (ControlType == "SMS Controller")
{
int srcIndex = BkmMnemonicConstants.Players[ControlType] * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int ctr = start;
foreach (var command in BkmMnemonicConstants.Commands[ControlType].Keys)
{
Force(command, c[srcIndex + ctr++]);
}
Force("Power", mnemonic[1] == 'P');
Force("Reset", mnemonic[1] == 'r');
SetFromMnemonic(mnemonic.AsSpan(3));
break;
case "NES Controller":
Force("Power", mnemonic[1] == 'P');
Force("Reset", mnemonic[1] == 'r');
Force("FDS Eject", mnemonic[1] == 'E');
Force("FDS Insert 0", mnemonic[1] == '0');
Force("FDS Insert 1", mnemonic[1] == '1');
Force("FDS Insert 2", mnemonic[1] == '2');
Force("FDS Insert 3", mnemonic[1] == '3');
Force("VS Coin 1", mnemonic[1] == 'c');
Force("VS Coin 2", mnemonic[1] == 'C');
SetFromMnemonic(mnemonic.AsSpan(3));
break;
case "SNES Controller":
Force("Power", mnemonic[1] == 'P');
Force("Reset", mnemonic[1] == 'r');
SetFromMnemonic(mnemonic.AsSpan(3));
break;
case "PC Engine Controller":
SetFromMnemonic(mnemonic.AsSpan(3));
break;
case "SMS Controller":
SetFromMnemonic(mnemonic.AsSpan(1));
Force("Power", mnemonic[^3] == 'P');
Force("Reset", mnemonic[^2] == 'r');
break;
case "TI83 Controller":
SetFromMnemonic(mnemonic.AsSpan(1));
break;
case "Atari 2600 Basic Controller":
Force("Reset", mnemonic[1] == 'r');
Force("Select", mnemonic[2] == 's');
SetFromMnemonic(mnemonic.AsSpan(4));
break;
case "Atari 7800 ProLine Joystick Controller":
Force("Power", mnemonic[1] == 'P');
Force("Reset", mnemonic[2] == 'r');
Force("Select", mnemonic[3] == 's');
Force("Pause", mnemonic[4] == 'p');
SetFromMnemonic(mnemonic.AsSpan(6));
break;
case "Commodore 64 Controller":
SetFromMnemonic(mnemonic.AsSpan(1));
break;
case "ColecoVision Basic Controller":
SetFromMnemonic(mnemonic.AsSpan(1));
break;
case "Nintento 64 Controller":
Force("Power", mnemonic[1] == 'P');
Force("Reset", mnemonic[1] == 'r');
SetFromMnemonic(mnemonic.AsSpan(3));
break;
case "Saturn Controller":
Force("Power", mnemonic[1] == 'P');
Force("Reset", mnemonic[1] == 'r');
SetFromMnemonic(mnemonic.AsSpan(3));
break;
case "Dual Gameboy Controller":
SetFromMnemonic(mnemonic.AsSpan());
break;
case "WonderSwan Controller":
SetFromMnemonic(mnemonic.AsSpan(1));
Force("Power", mnemonic[^3] == 'P');
Force("Rotate", mnemonic[^2] == 'R');
break;
}
}
@ -211,381 +149,9 @@ namespace BizHawk.Client.Common
private readonly Dictionary<string, bool> _myBoolButtons = new();
private bool IsGenesis6Button() => Definition.BoolButtons.Contains("P1 X");
private void Force(string button, bool state)
{
_myBoolButtons[button] = state;
}
private void Force(string name, int state)
{
_myAxisControls[name] = state;
}
private string ControlType => Definition.Name;
private void SetGBAControllersAsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
{
return;
}
if (mnemonic[1] == 'P')
{
Force("Power", true);
}
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force(button, c[start++]);
}
}
private void SetGenesis6ControllersAsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
{
return;
}
if (mnemonic[1] == 'P')
{
Force("Power", true);
}
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
{
Force("Reset", true);
}
if (mnemonic.Length < 9)
{
return;
}
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
private void SetGenesis3ControllersAsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
{
return;
}
if (mnemonic[1] == 'P')
{
Force("Power", true);
}
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
{
Force("Reset", true);
}
if (mnemonic.Length < 9)
{
return;
}
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Count + 1);
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Count - 1)
{
return;
}
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Keys)
{
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
private void SetSNESControllersAsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
{
return;
}
if (mnemonic[1] == 'P')
{
Force("Power", true);
}
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
{
Force("Reset", true);
}
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
int start = 3;
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
private void SetLynxControllersAsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
{
return;
}
if (mnemonic[1] == 'P')
{
Force("Power", true);
}
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
int start = 3;
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force(button, c[srcIndex + start++]);
}
}
}
private void SetN64ControllersAsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
{
return;
}
if (mnemonic[1] == 'P')
{
Force("Power", true);
}
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
{
Force("Reset", true);
}
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + (BkmMnemonicConstants.Analogs[ControlType].Count * 4) + 1 + 1);
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcIndex + start++]);
}
foreach (string name in BkmMnemonicConstants.Analogs[ControlType].Keys)
{
Force($"P{player} {name}", int.Parse(mnemonic.Substring(srcIndex + start, 4)));
start += 5;
}
}
}
private void SetSaturnControllersAsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
{
return;
}
if (mnemonic[1] == 'P')
{
Force("Power", true);
}
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
{
Force("Reset", true);
}
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
private void SetAtari7800AsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 5)
{
return;
}
if (mnemonic[1] == 'P')
{
Force("Power", true);
}
if (mnemonic[2] == 'r')
{
Force("Reset", true);
}
if (mnemonic[3] == 's')
{
Force("Select", true);
}
if (mnemonic[4] == 'p')
{
Force("Pause", true);
}
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int start = 6;
if (mnemonic.Length < srcIndex + start + BkmMnemonicConstants.Buttons[ControlType].Count)
{
return;
}
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
private void SetDualGameBoyControllerAsMnemonic(string mnemonic)
{
var checker = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
for (int i = 0; i < BkmMnemonicConstants.DgbMnemonic.Length; i++)
{
var t = BkmMnemonicConstants.DgbMnemonic[i];
if (t.Item1 != null)
{
Force(t.Item1, checker[i]);
}
}
}
private void SetWonderSwanControllerAsMnemonic(string mnemonic)
{
var checker = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
for (int i = 0; i < BkmMnemonicConstants.WsMnemonic.Length; i++)
{
var t = BkmMnemonicConstants.WsMnemonic[i];
if (t.Item1 != null)
{
Force(t.Item1, checker[i]);
}
}
}
private void SetC64ControllersAsMnemonic(string mnemonic)
{
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcIndex + 1 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
int start = 1;
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
int startKey = 13;
foreach (string button in BkmMnemonicConstants.Buttons["Commodore 64 Keyboard"].Keys)
{
Force(button, c[startKey++]);
}
}
private sealed class MnemonicChecker
{
private readonly string _mnemonic;
public MnemonicChecker(string mnemonic)
{
_mnemonic = mnemonic;
}
public bool this[int c] => !string.IsNullOrEmpty(_mnemonic) && _mnemonic[c] != '.';
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
internal class BkmControllerDefinition(string name) : ControllerDefinition(name)
{
// same as ControllerDefinition.GenOrderedControls, just with Axes after BoolButtons
protected override IReadOnlyList<IReadOnlyList<(string Name, AxisSpec? AxisSpec)>> GenOrderedControls()
{
var ret = new List<(string, AxisSpec?)>[PlayerCount + 1];
for (var i = 0; i < ret.Length; i++) ret[i] = new();
foreach (var btn in BoolButtons) ret[PlayerNumber(btn)].Add((btn, null));
foreach ((string buttonName, var axisSpec) in Axes) ret[PlayerNumber(buttonName)].Add((buttonName, axisSpec));
return ret;
}
}
}

View File

@ -1,203 +1,156 @@
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
internal static class BkmMnemonicConstants
{
public static readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> Buttons = new Dictionary<string, IReadOnlyDictionary<string, string>>
public static readonly IReadOnlyDictionary<string, ControllerDefinition> ControllerDefinitions = new Dictionary<string, ControllerDefinition>
{
["Gameboy Controller"] = new Dictionary<string, string>
[VSystemID.Raw.GB] = new BkmControllerDefinition("Gameboy Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A"
},
["Lynx Controller"] = new Dictionary<string, string>
BoolButtons = new[] { "Up", "Down", "Left", "Right", "Select", "Start", "B", "A" }.Select(b => $"P1 {b}")
.Append("Power")
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.GBA] = new BkmControllerDefinition("GBA Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A"
},
["GBA Controller"] = new Dictionary<string, string>
BoolButtons = new[] { "Up", "Down", "Left", "Right", "Select", "Start", "B", "A", "L", "R" }.Select(b => $"P1 {b}")
.Append("Power")
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.GEN] = new BkmControllerDefinition("GPGX Genesis Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A", ["L"] = "L", ["R"] = "R"
},
["Genesis 3-Button Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "A", "B", "C", "Start", "X", "Y", "Z", "Mode" }
.Select(b => $"P{i} {b}"))
.Concat([ "Reset", "Power" ])
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.NES] = new BkmControllerDefinition("NES Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Start"] = "S", ["A"] = "A", ["B"] = "B", ["C"] = "C"
},
["GPGX Genesis Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 4)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Select", "Start", "B", "A" }
.Select(b => $"P{i} {b}"))
.Concat([ "Reset", "Power", "FDS Eject", "FDS Insert 0", "FDS Insert 1", "FDS Insert 2", "FDS Insert 3", "VS Coin 1", "VS Coin 2" ])
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.SNES] = new BkmControllerDefinition("SNES Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["A"] = "A", ["B"] = "B", ["C"] = "C", ["Start"] = "S", ["X"] = "X", ["Y"] = "Y", ["Z"] = "Z", ["Mode"] = "M"
},
["GPGX 3-Button Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 4)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Select", "Start", "B", "A", "X", "Y", "L", "R" }
.Select(b => $"P{i} {b}"))
.Concat([ "Reset", "Power" ])
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.PCE] = new BkmControllerDefinition("PC Engine Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["A"] = "A", ["B"] = "B", ["C"] = "C", ["Start"] = "S"
},
["NES Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 5)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Select", "Run", "B2", "B1" }
.Select(b => $"P{i} {b}"))
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.SMS] = new BkmControllerDefinition("SMS Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A"
},
["SNES Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "B1", "B2" }
.Select(b => $"P{i} {b}"))
.Concat([ "Pause", "Reset" ])
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.TI83] = new BkmControllerDefinition("TI83 Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A", ["X"] = "X", ["Y"] = "Y", ["L"] = "L", ["R"] = "R"
},
["PC Engine Controller"] = new Dictionary<string, string>
BoolButtons = new[] {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "DOT", "ON", "ENTER",
"UP", "DOWN", "LEFT", "RIGHT", "PLUS", "MINUS", "MULTIPLY", "DIVIDE",
"CLEAR", "EXP", "DASH", "PARAOPEN", "PARACLOSE", "TAN", "VARS", "COS",
"PRGM", "STAT", "MATRIX", "X", "STO", "LN", "LOG", "SQUARED", "NEG1",
"MATH", "ALPHA", "GRAPH", "TRACE", "ZOOM", "WINDOW", "Y", "2ND", "MODE"
}.Select(b => $"P1 {b}")
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.A26] = new BkmControllerDefinition("Atari 2600 Basic Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Run"] = "r", ["B2"] = "2", ["B1"] = "1"
},
["SMS Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Button" }
.Select(b => $"P{i} {b}"))
.Concat([ "Reset", "Select" ])
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.A78] = new BkmControllerDefinition("Atari 7800 ProLine Joystick Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["B1"] = "1", ["B2"] = "2"
},
["TI83 Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Trigger", "Trigger 2" }
.Select(b => $"P{i} {b}"))
.Concat([ "Reset", "Power", "Select", "Pause" ])
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.C64] = new BkmControllerDefinition("Commodore 64 Controller")
{
["0"] = "0", ["1"] = "1", ["2"] = "2", ["3"] = "3", ["4"] = "4", ["5"] = "5", ["6"] = "6", ["7"] = "7", ["8"] = "8", ["9"] = "9",
["DOT"] = "`", ["ON"] = "O", ["ENTER"] = "=", ["UP"] = "U", ["DOWN"] = "D", ["LEFT"] = "L", ["RIGHT"] = "R", ["PLUS"] = "+", ["MINUS"] = "_", ["MULTIPLY"] = "*",
["DIVIDE"] = "/", ["CLEAR"] = "c", ["EXP"] = "^", ["DASH"] = "-", ["PARAOPEN"] = "(", ["PARACLOSE"] = ")", ["TAN"] = "T", ["VARS"] = "V", ["COS"] = "C", ["PRGM"] = "P",
["STAT"] = "s", ["MATRIX"] = "m", ["X"] = "X", ["STO"] = ">", ["LN"] = "n", ["LOG"] = "L", ["SQUARED"] = "2", ["NEG1"] = "1", ["MATH"] = "H", ["ALPHA"] = "A",
["GRAPH"] = "G", ["TRACE"] = "t", ["ZOOM"] = "Z", ["WINDOW"] = "W", ["Y"] = "Y", ["2ND"] = "&", ["MODE"] = "O"
},
["Atari 2600 Basic Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Button" }
.Select(b => $"P{i} {b}"))
.Concat([ "Key F1", "Key F3", "Key F5", "Key F7", "Key Left Arrow", "Key 1",
"Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Plus",
"Key Minus", "Key Pound", "Key Clear/Home", "Key Insert/Delete", "Key Control", "Key Q", "Key W", "Key E",
"Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", "Key At", "Key Asterisk", "Key Up Arrow",
"Key Restore", "Key Run/Stop", "Key Lck", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J",
"Key K", "Key L", "Key Colon", "Key Semicolon", "Key Equal", "Key Return", "Key Commodore", "Key Left Shift",
"Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Comma", "Key Period",
"Key Slash", "Key Right Shift", "Key Cursor Up/Down", "Key Cursor Left/Right", "Key Space"
])
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.Coleco] = new BkmControllerDefinition("ColecoVision Basic Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Button"] = "B"
},
["Atari 7800 ProLine Joystick Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "L", "R",
"Key1", "Key2", "Key3", "Key4", "Key5", "Key6",
"Key7", "Key8", "Key9", "Star", "Key0", "Pound"
}.Select(b => $"P{i} {b}"))
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.N64] = new BkmControllerDefinition("Nintento 64 Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Trigger"] = "1", ["Trigger 2"] = "2"
},
["Commodore 64 Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 4)
.SelectMany(i => new[] {
"DPad U", "DPad D", "DPad L", "DPad R",
"B", "A", "Z", "Start", "L", "R",
"C Up", "C Down", "C Left", "C Right"
}.Select(b => $"P{i} {b}"))
.Concat([ "Reset", "Power" ])
.ToArray()
}.AddXYPair("P1 {0} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0)
.AddXYPair("P2 {0} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0)
.AddXYPair("P3 {0} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0)
.AddXYPair("P4 {0} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0)
.MakeImmutable(),
[VSystemID.Raw.SAT] = new BkmControllerDefinition("Saturn Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Button"] = "B"
},
["Commodore 64 Keyboard"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Start", "X", "Y", "Z", "A", "B", "C", "L", "R" }
.Select(b => $"P{i} {b}"))
.Concat([ "Reset", "Power" ])
.ToArray()
}.MakeImmutable(),
["DGB"] = new BkmControllerDefinition("Dual Gameboy Controller")
{
["Key F1"] = "1", ["Key F3"] = "3", ["Key F5"] = "5", ["Key F7"] = "7", ["Key Left Arrow"] = "l", ["Key 1"] = "1",
["Key 2"] = "2", ["Key 3"] = "3", ["Key 4"] = "4", ["Key 5"] = "5", ["Key 6"] = "6", ["Key 7"] = "7", ["Key 8"] = "8", ["Key 9"] = "9", ["Key 0"] = "0", ["Key Plus"] = "+",
["Key Minus"] = "-", ["Key Pound"] = "l", ["Key Clear/Home"] = "c", ["Key Insert/Delete"] = "i", ["Key Control"] = "c", ["Key Q"] = "Q", ["Key W"] = "W", ["Key E"] = "E",
["Key R"] = "R", ["Key T"] = "T", ["Key Y"] = "Y", ["Key U"] = "U", ["Key I"] = "I", ["Key O"] = "O", ["Key P"] = "P", ["Key At"] = "@", ["Key Asterisk"] = "*", ["Key Up Arrow"] = "u",
["Key Restore"] = "r", ["Key Run/Stop"] = "s", ["Key Lck"] = "k", ["Key A"] = "A", ["Key S"] = "S", ["Key D"] = "D", ["Key F"] = "F", ["Key G"] = "G", ["Key H"] = "H", ["Key J"] = "J",
["Key K"] = "K", ["Key L"] = "L", ["Key Colon"] = ":", ["Key Semicolon"] = ";", ["Key Equal"] = "=", ["Key Return"] = "e", ["Key Commodore"] = "o", ["Key Left Shift"] = "s",
["Key Z"] = "Z", ["Key X"] = "X", ["Key C"] = "C", ["Key V"] = "V", ["Key B"] = "B", ["Key N"] = "N", ["Key M"] = "M", ["Key Comma"] = ",",
["Key Period"] = ">", ["Key Slash"] = "/", ["Key Right Shift"] = "s", ["Key Cursor Up/Down"] = "u", ["Key Cursor Left/Right"] = "l", ["Key Space"] = "_"
},
["ColecoVision Basic Controller"] = new Dictionary<string, string>
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "Power", "Up", "Down", "Left", "Right", "Select", "Start", "B", "A" }
.Select(b => $"P{i} {b}"))
.ToArray()
}.MakeImmutable(),
[VSystemID.Raw.WSWAN] = new BkmControllerDefinition("WonderSwan Controller")
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["L"] = "l", ["R"] = "r",
["Key1"] = "1", ["Key2"] = "2", ["Key3"] = "3", ["Key4"] = "4", ["Key5"] = "5", ["Key6"] = "6",
["Key7"] = "7", ["Key8"] = "8", ["Key9"] = "9", ["Star"] = "*", ["Key0"] = "0", ["Pound"] = "#"
},
["Nintendo 64 Controller"] = new Dictionary<string, string>
{
["DPad U"] = "U", ["DPad D"] = "D", ["DPad L"] = "L", ["DPad R"] = "R",
["B"] = "B", ["A"] = "A", ["Z"] = "Z", ["Start"] = "S", ["L"] = "L", ["R"] = "R",
["C Up"] = "u", ["C Down"] = "d", ["C Left"] = "l", ["C Right"] = "r"
},
["Saturn Controller"] = new Dictionary<string, string>
{
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Start"] = "S", ["X"] = "X", ["Y"] = "Y", ["Z"] = "Z", ["A"] = "A", ["B"] = "B", ["C"] = "C", ["L"] = "l", ["R"] = "r"
}
};
public static readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> Analogs = new Dictionary<string, IReadOnlyDictionary<string, string>>
{
["Nintendo 64 Controller"] = new Dictionary<string, string> { ["X Axis"] = "X" , ["Y Axis"] = "Y" }
};
public static readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> Commands = new Dictionary<string, IReadOnlyDictionary<string, string>>
{
["Atari 2600 Basic Controller"] = new Dictionary<string, string> { ["Reset"] = "r", ["Select"] = "s" },
["Atari 7800 ProLine Joystick Controller"] = new Dictionary<string, string> { ["Reset"] = "r", ["Select"] = "s" },
["Gameboy Controller"] = new Dictionary<string, string> { ["Power"] = "P" },
["GBA Controller"] = new Dictionary<string, string> { ["Power"] = "P" },
["Genesis 3-Button Controller"] = new Dictionary<string, string> { ["Reset"] = "r" },
["GPGX Genesis Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" },
["NES Controller"] = new Dictionary<string, string> { ["Reset"] = "r", ["Power"] = "P", ["FDS Eject"] = "E", ["FDS Insert 0"] = "0", ["FDS Insert 1"] = "1", ["VS Coin 1"] = "c", ["VS Coin 2"] = "C" },
["SNES Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" },
["PC Engine Controller"] = new Dictionary<string, string>(),
["SMS Controller"] = new Dictionary<string, string> { ["Pause"] = "p", ["Reset"] = "r" },
["TI83 Controller"] = new Dictionary<string, string>(),
["Nintendo 64 Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" },
["Saturn Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" },
["GPGX 3-Button Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" }
};
public static readonly IReadOnlyDictionary<string, int> Players = new Dictionary<string, int>
{
["Gameboy Controller"] = 1,
["GBA Controller"] = 1,
["Genesis 3-Button Controller"] = 2,
["GPGX Genesis Controller"] = 2,
["NES Controller"] = 4,
["SNES Controller"] = 4,
["PC Engine Controller"] = 5,
["SMS Controller"] = 2,
["TI83 Controller"] = 1,
["Atari 2600 Basic Controller"] = 2,
["Atari 7800 ProLine Joystick Controller"] = 2,
["ColecoVision Basic Controller"] = 2,
["Commodore 64 Controller"] = 2,
["Nintendo 64 Controller"] = 4,
["Saturn Controller"] = 2,
["GPGX 3-Button Controller"] = 2,
["Lynx Controller"] = 1
};
// just experimenting with different possibly more painful ways to handle mnemonics
// |P|UDLRsSBA|
public static readonly (string, char)[] DgbMnemonic =
{
(null, '|'),
("P1 Power", 'P'),
(null, '|'),
("P1 Up", 'U'),
("P1 Down", 'D'),
("P1 Left", 'L'),
("P1 Right", 'R'),
("P1 Select", 's'),
("P1 Start", 'S'),
("P1 B", 'B'),
("P1 A", 'A'),
(null, '|'),
("P2 Power", 'P'),
(null, '|'),
("P2 Up", 'U'),
("P2 Down", 'D'),
("P2 Left", 'L'),
("P2 Right", 'R'),
("P2 Select", 's'),
("P2 Start", 'S'),
("P2 B", 'B'),
("P2 A", 'A'),
(null, '|')
};
public static readonly (string, char)[] WsMnemonic =
{
(null, '|'),
("P1 X1", '1'),
("P1 X3", '3'),
("P1 X4", '4'),
("P1 X2", '2'),
("P1 Y1", '1'),
("P1 Y3", '3'),
("P1 Y4", '4'),
("P1 Y2", '2'),
("P1 Start", 'S'),
("P1 B", 'B'),
("P1 A", 'A'),
(null, '|'),
("P2 X1", '1'),
("P2 X3", '3'),
("P2 X4", '4'),
("P2 X2", '2'),
("P2 Y1", '1'),
("P2 Y3", '3'),
("P2 Y4", '4'),
("P2 Y2", '2'),
("P2 Start", 'S'),
("P2 B", 'B'),
("P2 A", 'A'),
(null, '|'),
("Power", 'P'),
("Rotate", 'R'),
(null, '|')
BoolButtons = Enumerable.Range(1, 2)
.SelectMany(i => new[] { "X1", "X3", "X4", "X2", "Y1", "Y3", "Y4", "Y2", "Start", "B", "A" }
.Select(b => $"P{i} {b}"))
.Concat([ "Power", "Rotate" ])
.ToArray()
}.MakeImmutable()
};
}
}

View File

@ -2,25 +2,26 @@ using System.Collections.Generic;
using System.IO;
using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
internal class BkmMovie
{
private BkmControllerAdapter _adapter;
private readonly List<string> _log = new List<string>();
public BkmHeader Header { get; } = new BkmHeader();
public string Filename { get; set; } = "";
public bool Loaded { get; private set; }
public int InputLogLength => Loaded ? _log.Count : 0;
public BkmControllerAdapter GetInputState(int frame, ControllerDefinition definition, string sytemId)
public BkmControllerAdapter GetInputState(int frame, string sytemId)
{
if (frame < InputLogLength && frame >= 0)
{
var adapter = new BkmControllerAdapter(definition, sytemId);
adapter.SetControllersAsMnemonic(_log[frame]);
return adapter;
_adapter ??= new BkmControllerAdapter(sytemId);
_adapter.SetControllersAsMnemonic(_log[frame]);
return _adapter;
}
return null;
@ -30,6 +31,8 @@ namespace BizHawk.Client.Common
public IList<string> Comments => Header.Comments;
public string GenerateLogKey => Bk2LogEntryGenerator.GenerateLogKey(_adapter.Definition);
public string SyncSettingsJson
{
get => Header[HeaderKeys.SyncSettings];