diff --git a/BizHawk.Client.Common/movie/import/Fm2Import.cs b/BizHawk.Client.Common/movie/import/Fm2Import.cs
index d4cb3f9bee..3e6dde2d59 100644
--- a/BizHawk.Client.Common/movie/import/Fm2Import.cs
+++ b/BizHawk.Client.Common/movie/import/Fm2Import.cs
@@ -6,6 +6,7 @@ using BizHawk.Emulation.Cores.Nintendo.NES;
namespace BizHawk.Client.Common
{
+ // FM2 file format: http://www.fceux.com/web/FM2.html
// ReSharper disable once UnusedMember.Global
[ImportExtension(".fm2")]
public class Fm2Import : MovieImporter
@@ -17,11 +18,19 @@ namespace BizHawk.Client.Common
var syncSettings = new NES.NESSyncSettings();
+ var controllerSettings = new NESControlSettings
+ {
+ NesLeftPort = nameof(UnpluggedNES),
+ NesRightPort = nameof(UnpluggedNES)
+ };
+
+ _deck = controllerSettings.Instantiate((x, y) => true);
+ AddDeckControlButtons();
+
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = platform;
-
+
using var sr = SourceFile.OpenText();
string line;
-
while ((line = sr.ReadLine()) != null)
{
if (line == "")
@@ -31,8 +40,7 @@ namespace BizHawk.Client.Common
if (line[0] == '|')
{
- // TODO: import a frame of input
- // TODO: report any errors importing this frame and bail out if so
+ ImportInputFrame(line);
}
else if (line.ToLower().StartsWith("sub"))
{
@@ -107,18 +115,43 @@ namespace BizHawk.Client.Common
{
Result.Movie.HeaderEntries[HeaderKeys.PAL] = ParseHeader(line, "palFlag");
}
+ else if (line.ToLower().StartsWith("port0"))
+ {
+ if (ParseHeader(line, "port0") == "1")
+ {
+ controllerSettings.NesLeftPort = nameof(ControllerNES);
+ _deck = controllerSettings.Instantiate((x, y) => false);
+ AddDeckControlButtons();
+ }
+ }
+ else if (line.ToLower().StartsWith("port1"))
+ {
+ if (ParseHeader(line, "port1") == "1")
+ {
+ controllerSettings.NesRightPort = nameof(ControllerNES);
+ _deck = controllerSettings.Instantiate((x, y) => false);
+ AddDeckControlButtons();
+ }
+ }
+ else if (line.ToLower().StartsWith("port2"))
+ {
+ if (ParseHeader(line, "port2") == "1")
+ {
+ Result.Errors.Add("Famicom port not yet supported");
+ break;
+ }
+ }
else if (line.ToLower().StartsWith("fourscore"))
{
bool fourscore = ParseHeader(line, "fourscore") == "1";
if (fourscore)
{
// TODO: set controller config sync settings
- syncSettings.Controls = new NESControlSettings
- {
- NesLeftPort = nameof(FourScore),
- NesRightPort = nameof(FourScore)
- };
+ controllerSettings.NesLeftPort = nameof(FourScore);
+ controllerSettings.NesRightPort = nameof(FourScore);
}
+
+ _deck = controllerSettings.Instantiate((x, y) => false);
}
else
{
@@ -126,9 +159,74 @@ namespace BizHawk.Client.Common
}
}
+ syncSettings.Controls = controllerSettings;
Result.Movie.SyncSettingsJson = ToJson(syncSettings);
}
+ private IControllerDeck _deck;
+
+ private readonly string[] _buttons = { "Right", "Left", "Down", "Up", "Start", "Select", "B", "A" };
+ private void ImportInputFrame(string line)
+ {
+ var controllers = new SimpleController
+ {
+ Definition = _deck.GetDefinition()
+ };
+
+ string[] sections = line.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries);
+ controllers["Reset"] = sections[1][0] == '1';
+ switch (sections[0][0])
+ {
+ case '0':
+ break;
+ case '1':
+ controllers["Reset"] = true;
+ break;
+ case '2':
+ controllers["Power"] = true;
+ break;
+ case '4':
+ controllers["FDS Insert 0"] = true;
+ break;
+ case '8':
+ controllers["FDS Insert 1"] = true;
+ break;
+ // TODO: insert coin?
+ default:
+ Result.Warnings.Add($"Unknown command: {sections[0][0]}");
+ break;
+ }
+
+ for (int player = 1; player < sections.Length; player++)
+ {
+ string prefix = $"P{player} ";
+ // Only count lines with that have the right number of buttons and are for valid players.
+ if (sections[player].Length == _buttons.Length)
+ {
+ for (int button = 0; button < _buttons.Length; button++)
+ {
+ // Consider the button pressed so long as its spot is not occupied by a ".".
+ controllers[prefix + _buttons[button]] = sections[player][button] != '.';
+ }
+ }
+ }
+
+ Result.Movie.AppendFrame(controllers);
+ }
+
+ private void AddDeckControlButtons()
+ {
+ var controllers = new SimpleController
+ {
+ Definition = _deck.GetDefinition()
+ };
+
+ // TODO: FDS
+ // Yes, this adds them to the deck definition too
+ controllers.Definition.BoolButtons.Add("Reset");
+ controllers.Definition.BoolButtons.Add("Power");
+ }
+
private static string ImportTextSubtitle(string line)
{
line = SingleSpaces(line);
diff --git a/BizHawk.Client.Common/movie/import/MovieImport.cs b/BizHawk.Client.Common/movie/import/MovieImport.cs
index 3c6dfd6b4a..2b59be39b4 100644
--- a/BizHawk.Client.Common/movie/import/MovieImport.cs
+++ b/BizHawk.Client.Common/movie/import/MovieImport.cs
@@ -230,7 +230,7 @@ namespace BizHawk.Client.Common
{
string[] extensions =
{
- "BKM", "FCM", "FM2", "FMV", "GMV", "MCM", "MC2", "MMV", "NMV", "LSMV", "SMV", "VBM", "VMV", "YMV", "ZMV"
+ "BKM", "FCM", "FMV", "GMV", "MCM", "MC2", "MMV", "NMV", "LSMV", "SMV", "VBM", "VMV", "YMV", "ZMV"
};
return extensions.Any(ext => extension.ToUpper() == $".{ext}");
}
diff --git a/BizHawk.sln.DotSettings b/BizHawk.sln.DotSettings
index abf2e58730..fc3b174d2b 100644
--- a/BizHawk.sln.DotSettings
+++ b/BizHawk.sln.DotSettings
@@ -209,6 +209,7 @@
True
True
True
+ True
True
True
True