From 9131c8ab2e560e74d6360dbf4e4c3dead08bb3e9 Mon Sep 17 00:00:00 2001 From: Morilli <35152647+Morilli@users.noreply.github.com> Date: Sun, 13 Aug 2023 19:59:37 +0200 Subject: [PATCH] Change SmvImport to import to Snes9x core; various fixes the importer is still far from perfect, but it should handle some things better than before - closes #3177 --- .../movie/import/SmvImport.cs | 82 ++++----- .../Consoles/Nintendo/SNES9X/LibSnes9x.cs | 7 +- .../Consoles/Nintendo/SNES9X/Snes9x.cs | 162 +----------------- .../Nintendo/SNES9X/Snes9xControllers.cs | 158 +++++++++++++++++ 4 files changed, 205 insertions(+), 204 deletions(-) create mode 100644 src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9xControllers.cs diff --git a/src/BizHawk.Client.Common/movie/import/SmvImport.cs b/src/BizHawk.Client.Common/movie/import/SmvImport.cs index 3f12f295bd..ef7d3cf94e 100644 --- a/src/BizHawk.Client.Common/movie/import/SmvImport.cs +++ b/src/BizHawk.Client.Common/movie/import/SmvImport.cs @@ -1,10 +1,11 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using System.Text; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores; -using BizHawk.Emulation.Cores.Nintendo.SNES; +using BizHawk.Emulation.Cores.Nintendo.SNES9X; namespace BizHawk.Client.Common.movie.import { @@ -13,11 +14,9 @@ namespace BizHawk.Client.Common.movie.import [ImporterFor("Snes9x", ".smv")] internal class SmvImport : MovieImporter { - private LibsnesControllerDeck _deck; - protected override void RunImport() { - Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.Bsnes; //TODO set to Snes9x instead? or give the user the choice? + Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.Snes9X; using var fs = SourceFile.Open(FileMode.Open, FileAccess.Read); using var r = new BinaryReader(fs); @@ -74,32 +73,23 @@ namespace BizHawk.Client.Common.movie.import controllersUsed[controller - 1] = ((controllerFlags >> (controller - 1)) & 0x1) != 0; } - var controllerCount = controllersUsed.Count(c => c); + int highestControllerIndex = 1 + Array.LastIndexOf(controllersUsed, true); + var ss = new Snes9x.SyncSettings(); - var ss = new LibsnesCore.SnesSyncSettings + switch (highestControllerIndex) { - LeftPort = LibsnesControllerDeck.ControllerType.Gamepad, - RightPort = LibsnesControllerDeck.ControllerType.Gamepad - }; - - if (controllerCount == 1) - { - ss.RightPort = LibsnesControllerDeck.ControllerType.Unplugged; + case 1: + ss.RightPort = LibSnes9x.RightPortDevice.None; + break; + case 3: + case 4: + ss.LeftPort = LibSnes9x.LeftPortDevice.Multitap; + ss.RightPort = LibSnes9x.RightPortDevice.None; + break; + case 5: + ss.LeftPort = LibSnes9x.LeftPortDevice.Multitap; + break; } - else if (controllerCount > 2) - { - // More than 2 controllers means a multi-tap on the first port - // Snes9x only supported up to 5 controllers, so right port would never be multitap - ss.LeftPort = LibsnesControllerDeck.ControllerType.Multitap; - - // Unless there are exactly 5, right port is unplugged, as the multitap will handle 4 controllers - if (controllerCount < 5) - { - ss.RightPort = LibsnesControllerDeck.ControllerType.Unplugged; - } - } - - _deck = new LibsnesControllerDeck(ss); // 015 1-byte flags "movie options" byte movieFlags = r.ReadByte(); @@ -200,7 +190,9 @@ namespace BizHawk.Client.Common.movie.import Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName; } - SimpleController controllers = new(_deck.Definition); + var _controllers = new Snes9xControllers(ss); + Result.Movie.LogKey = new Bk2LogEntryGenerator("SNES", new Bk2Controller(_controllers.ControllerDefinition)).GenerateLogKey(); + SimpleController controllers = new(_controllers.ControllerDefinition); r.BaseStream.Position = firstFrameOffset; /* @@ -252,15 +244,25 @@ namespace BizHawk.Client.Common.movie.import controllers["Reset"] = false; } - /* - While the meaning of controller data (for 1.51 and up) for a single standard SNES controller pad - remains the same, each frame of controller data can contain additional bytes if input for peripherals - is being recorded. - */ - if (version != "1.43" && player <= controllerTypes.Length) + ushort controllerState = (ushort)(((controllerState1 << 4) & 0x0F00) | controllerState2); + for (int button = 0; button < buttons.Length; button++) + { + controllers[$"P{player} {buttons[button]}"] = + ((controllerState >> button) & 0x1) != 0; + } + } + + /* + While the meaning of controller data (for 1.51 and up) for a single standard SNES controller pad + remains the same, each frame of controller data can contain additional bytes if input for peripherals + is being recorded. + */ + if (version != "1.43") + { + for (int i = 0; i < 2; i++) { var peripheral = ""; - switch (controllerTypes[player - 1]) + switch (controllerTypes[i]) { case 0: // NONE continue; @@ -292,13 +294,6 @@ namespace BizHawk.Client.Common.movie.import Result.Warnings.Add($"Unable to import {peripheral}. Not supported yet"); } } - - ushort controllerState = (ushort)(((controllerState1 << 4) & 0x0F00) | controllerState2); - for (int button = 0; button < buttons.Length; button++) - { - controllers[$"P{player} {buttons[button]}"] = - ((controllerState >> button) & 0x1) != 0; - } } // The controller data contains frames. @@ -311,7 +306,6 @@ namespace BizHawk.Client.Common.movie.import } Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(ss); - MaybeSetCorePreference(VSystemID.Raw.SNES, CoreNames.Bsnes, fileExt: ".smv"); } } } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/LibSnes9x.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/LibSnes9x.cs index 673c94b6c5..9529dd83df 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/LibSnes9x.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/LibSnes9x.cs @@ -7,13 +7,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X { public enum LeftPortDevice : uint { - //None = 0, // something in the libretro spaghetti input goes wonky with None - Joypad = 1 + None = 0, + Joypad = 1, + Multitap = 2 } public enum RightPortDevice : uint { - //None = 0, // something in the libretro spaghetti input goes wonky with None + None = 0, Joypad = 1, Multitap = 2, Mouse = 3, diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs index 125ea29955..eeedeb9db2 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9x.cs @@ -71,168 +71,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X PutSettings(settings); } - private readonly short[] _inputState = new short[16 * 8]; - private List _cdums; - private readonly List _controllers = new List(); - private void InitControllers() { _core.biz_set_port_devices(_syncSettings.LeftPort, _syncSettings.RightPort); - switch (_syncSettings.LeftPort) - { - case LibSnes9x.LeftPortDevice.Joypad: - _controllers.Add(new Joypad()); - break; - } - switch (_syncSettings.RightPort) - { - case LibSnes9x.RightPortDevice.Joypad: - _controllers.Add(new Joypad()); - break; - case LibSnes9x.RightPortDevice.Multitap: - _controllers.Add(new Joypad()); - _controllers.Add(new Joypad()); - _controllers.Add(new Joypad()); - _controllers.Add(new Joypad()); - break; - case LibSnes9x.RightPortDevice.Mouse: - _controllers.Add(new Mouse()); - break; - case LibSnes9x.RightPortDevice.SuperScope: - _controllers.Add(new SuperScope()); - break; - case LibSnes9x.RightPortDevice.Justifier: - _controllers.Add(new Justifier()); - break; - } - - _controllerDefinition = ControllerDefinitionMerger.GetMerged( - "SNES Controller", - _controllers.Select(c => c.Definition), - out _cdums); - - // add buttons that the core itself will handle - _controllerDefinition.BoolButtons.Add("Reset"); - _controllerDefinition.BoolButtons.Add("Power"); - - _controllerDefinition.MakeImmutable(); + _controllers = new Snes9xControllers(_syncSettings); } - private void UpdateControls(IController c) - { - Array.Clear(_inputState, 0, 16 * 8); - for (int i = 0, offset = 0; i < _controllers.Count; i++, offset += 16) - { - _controllers[i].ApplyState(_cdums[i].UnMerge(c), _inputState, offset); - } - } + private Snes9xControllers _controllers; - private interface IControlDevice - { - ControllerDefinition Definition { get; } - void ApplyState(IController controller, short[] input, int offset); - } - - private class Joypad : IControlDevice - { - private static readonly string[] Buttons = - { - "0B", - "0Y", - "0Select", - "0Start", - "0Up", - "0Down", - "0Left", - "0Right", - "0A", - "0X", - "0L", - "0R" - }; - - private static int ButtonOrder(string btn) - { - var order = new Dictionary - { - ["0Up"] = 0, - ["0Down"] = 1, - ["0Left"] = 2, - ["0Right"] = 3, - - ["0Select"] = 4, - ["0Start"] = 5, - - ["0Y"] = 6, - ["0B"] = 7, - - ["0X"] = 8, - ["0A"] = 9, - - ["0L"] = 10, - ["0R"] = 11 - }; - - return order[btn]; - } - - private static readonly ControllerDefinition _definition = new("(SNES Controller fragment)") - { - BoolButtons = Buttons.OrderBy(ButtonOrder).ToList() - }; - - public ControllerDefinition Definition { get; } = _definition; - - public void ApplyState(IController controller, short[] input, int offset) - { - for (int i = 0; i < Buttons.Length; i++) - input[offset + i] = (short)(controller.IsPressed(Buttons[i]) ? 1 : 0); - } - } - - private abstract class Analog : IControlDevice - { - public abstract ControllerDefinition Definition { get; } - - public void ApplyState(IController controller, short[] input, int offset) - { - foreach (var s in Definition.Axes.Keys) - input[offset++] = (short)controller.AxisValue(s); - foreach (var s in Definition.BoolButtons) - input[offset++] = (short)(controller.IsPressed(s) ? 1 : 0); - } - } - - private class Mouse : Analog - { - private static readonly ControllerDefinition _definition - = new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Mouse Left", "0Mouse Right" } } - .AddXYPair("0Mouse {0}", AxisPairOrientation.RightAndDown, (-127).RangeTo(127), 0); //TODO verify direction against hardware - - public override ControllerDefinition Definition => _definition; - } - - private class SuperScope : Analog - { - private static readonly ControllerDefinition _definition - = new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Cursor", "0Turbo", "0Pause", "0Offscreen" } } - .AddLightGun("0Scope {0}"); - - public override ControllerDefinition Definition => _definition; - } - - private class Justifier : Analog - { - private static readonly ControllerDefinition _definition - = new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Start", "0Offscreen" } } - .AddLightGun("0Justifier {0}"); - - public override ControllerDefinition Definition => _definition; - } - - private ControllerDefinition _controllerDefinition; - public override ControllerDefinition ControllerDefinition => _controllerDefinition; + public override ControllerDefinition ControllerDefinition => _controllers.ControllerDefinition; public DisplayType Region { get; } @@ -242,8 +90,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X _core.biz_hard_reset(); else if (controller.IsPressed("Reset")) _core.biz_soft_reset(); - UpdateControls(controller); - _core.SetButtons(_inputState); + _controllers.UpdateControls(controller); + _core.SetButtons(_controllers.inputState); return new LibWaterboxCore.FrameInfo(); } diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9xControllers.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9xControllers.cs new file mode 100644 index 0000000000..23a7bc1445 --- /dev/null +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES9X/Snes9xControllers.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Nintendo.SNES; + +namespace BizHawk.Emulation.Cores.Nintendo.SNES9X +{ + public class Snes9xControllers + { + private readonly List _cdums; + private readonly List _controllers = new(); + + internal readonly short[] inputState = new short[16 * 8]; + + public ControllerDefinition ControllerDefinition { get; } + + public Snes9xControllers(Snes9x.SyncSettings ss) + { + switch (ss.LeftPort) + { + case LibSnes9x.LeftPortDevice.Joypad: + _controllers.Add(new Joypad()); + break; + case LibSnes9x.LeftPortDevice.Multitap: + _controllers.Add(new Joypad()); + _controllers.Add(new Joypad()); + _controllers.Add(new Joypad()); + _controllers.Add(new Joypad()); + break; + } + + switch (ss.RightPort) + { + case LibSnes9x.RightPortDevice.Joypad: + _controllers.Add(new Joypad()); + break; + case LibSnes9x.RightPortDevice.Multitap: + _controllers.Add(new Joypad()); + _controllers.Add(new Joypad()); + _controllers.Add(new Joypad()); + _controllers.Add(new Joypad()); + break; + case LibSnes9x.RightPortDevice.Mouse: + _controllers.Add(new Mouse()); + break; + case LibSnes9x.RightPortDevice.SuperScope: + _controllers.Add(new SuperScope()); + break; + case LibSnes9x.RightPortDevice.Justifier: + _controllers.Add(new Justifier()); + break; + } + + ControllerDefinition = ControllerDefinitionMerger.GetMerged( + "SNES Controller", + _controllers.Select(c => c.Definition), + out _cdums); + + // add buttons that the core itself will handle + ControllerDefinition.BoolButtons.Add("Reset"); + ControllerDefinition.BoolButtons.Add("Power"); + + ControllerDefinition.MakeImmutable(); + } + + internal void UpdateControls(IController c) + { + Array.Clear(inputState, 0, 16 * 8); + for (int i = 0, offset = 0; i < _controllers.Count; i++, offset += 16) + { + _controllers[i] + .ApplyState(_cdums[i] + .UnMerge(c), inputState, offset); + } + } + + private interface IControlDevice + { + ControllerDefinition Definition { get; } + void ApplyState(IController controller, short[] input, int offset); + } + + private class Joypad : IControlDevice + { + private static readonly string[] Buttons = + { + "0B", "0Y", "0Select", "0Start", "0Up", "0Down", "0Left", "0Right", "0A", "0X", "0L", "0R" + }; + + private static int ButtonOrder(string btn) + { + var order = new Dictionary + { + ["0Up"] = 0, ["0Down"] = 1, ["0Left"] = 2, ["0Right"] = 3, ["0Select"] = 4, ["0Start"] = 5, ["0Y"] = 6, ["0B"] = 7, ["0X"] = 8, ["0A"] = 9 + , ["0L"] = 10, ["0R"] = 11 + }; + + return order[btn]; + } + + private static readonly ControllerDefinition _definition = new("(SNES Controller fragment)") + { + BoolButtons = Buttons.OrderBy(ButtonOrder) + .ToList() + }; + + public ControllerDefinition Definition { get; } = _definition; + + public void ApplyState(IController controller, short[] input, int offset) + { + for (int i = 0; i < Buttons.Length; i++) + input[offset + i] = (short)(controller.IsPressed(Buttons[i]) ? 1 : 0); + } + } + + private abstract class Analog : IControlDevice + { + public abstract ControllerDefinition Definition { get; } + + public void ApplyState(IController controller, short[] input, int offset) + { + foreach (var s in Definition.Axes.Keys) + input[offset++] = (short)controller.AxisValue(s); + foreach (var s in Definition.BoolButtons) + input[offset++] = (short)(controller.IsPressed(s) ? 1 : 0); + } + } + + private class Mouse : Analog + { + private static readonly ControllerDefinition _definition + = new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Mouse Left", "0Mouse Right" } } + .AddXYPair("0Mouse {0}", AxisPairOrientation.RightAndDown, (-127).RangeTo(127), 0); //TODO verify direction against hardware + + public override ControllerDefinition Definition => _definition; + } + + private class SuperScope : Analog + { + private static readonly ControllerDefinition _definition + = new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Cursor", "0Turbo", "0Pause", "0Offscreen" } } + .AddLightGun("0Scope {0}"); + + public override ControllerDefinition Definition => _definition; + } + + private class Justifier : Analog + { + private static readonly ControllerDefinition _definition + = new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Start", "0Offscreen" } } + .AddLightGun("0Justifier {0}"); + + public override ControllerDefinition Definition => _definition; + } + } +}