diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 07fb3f1ede..75343412ce 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1005,6 +1005,7 @@ + PCEngine.cs diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IEmulator.cs index ce4d1af49a..5fa7f01d7e 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IEmulator.cs @@ -6,7 +6,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { public IEmulatorServiceProvider ServiceProvider { get; private set; } - public ControllerDefinition ControllerDefinition => PCEngineController; + public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; public void FrameAdvance(IController controller, bool render, bool rendersound) { diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISettable.cs index 28694b69d5..39e6d2dcaf 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.ISettable.cs @@ -1,4 +1,7 @@ -using BizHawk.Emulation.Common; +using System.ComponentModel; + +using BizHawk.Common; +using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.PCEngine { @@ -62,6 +65,36 @@ namespace BizHawk.Emulation.Cores.PCEngine public class PCESyncSettings { + [DefaultValue(PceControllerType.GamePad)] + [DisplayName("Port 1 Device")] + [Description("The type of controller plugged into the first controller port")] + [TypeConverter(typeof(DescribableEnumConverter))] + public PceControllerType Port1 { get; set; } = PceControllerType.GamePad; + + [DefaultValue(PceControllerType.Unplugged)] + [DisplayName("Port 2 Device")] + [Description("The type of controller plugged into the second controller port")] + [TypeConverter(typeof(DescribableEnumConverter))] + public PceControllerType Port2 { get; set; } = PceControllerType.Unplugged; + + [DefaultValue(PceControllerType.Unplugged)] + [DisplayName("Port 3 Device")] + [Description("The type of controller plugged into the third controller port")] + [TypeConverter(typeof(DescribableEnumConverter))] + public PceControllerType Port3 { get; set; } = PceControllerType.Unplugged; + + [DefaultValue(PceControllerType.Unplugged)] + [DisplayName("Port 4 Device")] + [Description("The type of controller plugged into the fourth controller port")] + [TypeConverter(typeof(DescribableEnumConverter))] + public PceControllerType Port4 { get; set; } = PceControllerType.Unplugged; + + [DefaultValue(PceControllerType.Unplugged)] + [DisplayName("Port 5 Device")] + [Description("The type of controller plugged into the fifth controller port")] + [TypeConverter(typeof(DescribableEnumConverter))] + public PceControllerType Port5 { get; set; } = PceControllerType.Unplugged; + public ControllerSetting[] Controllers = { new ControllerSetting { IsConnected = true }, diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.Input.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.Input.cs index c40788c08a..52b5c7e444 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.Input.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.Input.cs @@ -63,6 +63,8 @@ namespace BizHawk.Emulation.Cores.PCEngine } } + private readonly PceControllerDeck _controllerDeck; + private byte ReadInput() { InputCallbacks.Call(); @@ -72,21 +74,7 @@ namespace BizHawk.Emulation.Cores.PCEngine if (player < 6) { _lagged = false; - if (SEL == false) // return buttons - { - if (_controller.IsPressed("P" + player + " B1")) value &= 0xFE; - if (_controller.IsPressed("P" + player + " B2")) value &= 0xFD; - if (_controller.IsPressed("P" + player + " Select")) value &= 0xFB; - if (_controller.IsPressed("P" + player + " Run")) value &= 0xF7; - } - else - { - //return directions - if (_controller.IsPressed("P" + player + " Up")) value &= 0xFE; - if (_controller.IsPressed("P" + player + " Right")) value &= 0xFD; - if (_controller.IsPressed("P" + player + " Down")) value &= 0xFB; - if (_controller.IsPressed("P" + player + " Left")) value &= 0xF7; - } + value &= _controllerDeck.Read(player, _controller, SEL); } if (Region == "Japan") diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs index 32ca565b65..d3d43f1464 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs @@ -41,7 +41,14 @@ namespace BizHawk.Emulation.Cores.PCEngine Settings = (PCESettings)settings ?? new PCESettings(); _syncSettings = (PCESyncSettings)syncSettings ?? new PCESyncSettings(); Init(game, rom); - SetControllerButtons(); + + _controllerDeck = new PceControllerDeck( + _syncSettings.Port1, + _syncSettings.Port2, + _syncSettings.Port3, + _syncSettings.Port4, + _syncSettings.Port5); + //SetControllerButtons(); // TODO: get rid of this method } public PCEngine(CoreComm comm, GameInfo game, Disc disc, object Settings, object syncSettings) @@ -96,7 +103,14 @@ namespace BizHawk.Emulation.Cores.PCEngine // the default RomStatusDetails don't do anything with Disc CoreComm.RomStatusDetails = string.Format("{0}\r\nDisk partial hash:{1}", game.Name, new DiscSystem.DiscHasher(disc).OldHash()); - SetControllerButtons(); + + _controllerDeck = new PceControllerDeck( + _syncSettings.Port1, + _syncSettings.Port2, + _syncSettings.Port3, + _syncSettings.Port4, + _syncSettings.Port5); + //SetControllerButtons(); } // ROM diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PceControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PceControllerDeck.cs new file mode 100644 index 0000000000..ac5a9efd24 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PceControllerDeck.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.PCEngine +{ + public enum PceControllerType + { + Unplugged, + GamePad + } + + public class PceControllerDeck + { + private static readonly Type[] Implementors = + { + typeof(UnpluggedController), // Order must match PceControllerType enum values + typeof(StandardController) + }; + + public PceControllerDeck( + PceControllerType controller1, + PceControllerType controller2, + PceControllerType controller3, + PceControllerType controller4, + PceControllerType controller5) + { + Port1 = (IPort)Activator.CreateInstance(Implementors[(int)controller1], 1); + Port2 = (IPort)Activator.CreateInstance(Implementors[(int)controller2], 2); + Port3 = (IPort)Activator.CreateInstance(Implementors[(int)controller3], 3); + Port4 = (IPort)Activator.CreateInstance(Implementors[(int)controller4], 4); + Port5 = (IPort)Activator.CreateInstance(Implementors[(int)controller5], 5); + + Definition = new ControllerDefinition + { + Name = "PC Engine Controller", + BoolButtons = Port1.Definition.BoolButtons + .Concat(Port2.Definition.BoolButtons) + .Concat(Port3.Definition.BoolButtons) + .Concat(Port4.Definition.BoolButtons) + .Concat(Port5.Definition.BoolButtons) + .ToList() + }; + + Definition.FloatControls.AddRange(Port1.Definition.FloatControls); + Definition.FloatControls.AddRange(Port2.Definition.FloatControls); + Definition.FloatControls.AddRange(Port3.Definition.FloatControls); + Definition.FloatControls.AddRange(Port4.Definition.FloatControls); + Definition.FloatControls.AddRange(Port5.Definition.FloatControls); + + Definition.FloatRanges.AddRange(Port1.Definition.FloatRanges); + Definition.FloatRanges.AddRange(Port2.Definition.FloatRanges); + Definition.FloatRanges.AddRange(Port3.Definition.FloatRanges); + Definition.FloatRanges.AddRange(Port4.Definition.FloatRanges); + Definition.FloatRanges.AddRange(Port5.Definition.FloatRanges); + } + + private readonly IPort Port1; + private readonly IPort Port2; + private readonly IPort Port3; + private readonly IPort Port4; + private readonly IPort Port5; + + public byte Read(int portNum, IController c, bool sel) + { + switch (portNum) + { + default: + throw new ArgumentException($"Invalid {nameof(portNum)}: {portNum}"); + case 1: + return Port1.Read(c, sel); + case 2: + return Port2.Read(c, sel); + case 3: + return Port3.Read(c, sel); + case 4: + return Port4.Read(c, sel); + case 5: + return Port5.Read(c, sel); + } + } + + public ControllerDefinition Definition { get; } + } + + public interface IPort + { + byte Read(IController c, bool sel); + + ControllerDefinition Definition { get; } + + int PortNum { get; } + } + + public class UnpluggedController : IPort + { + public UnpluggedController(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + BoolButtons = new List() + }; + } + + public byte Read(IController c, bool sel) + { + return 0x3F; + } + + public ControllerDefinition Definition { get; } + + public int PortNum { get; } + } + + public class StandardController : IPort + { + public StandardController(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + BoolButtons = BaseDefinition + .Select(b => $"P{PortNum} " + b) + .ToList() + }; + } + + public ControllerDefinition Definition { get; } + + public int PortNum { get; } + + public byte Read(IController c, bool sel) + { + byte result = 0x3F; + + if (sel == false) + { + if (c.IsPressed($"P{PortNum} B1")) result &= 0xFE; + if (c.IsPressed($"P{PortNum} B2")) result &= 0xFD; + if (c.IsPressed($"P{PortNum} Select")) result &= 0xFB; + if (c.IsPressed($"P{PortNum} Run")) result &= 0xF7; + } + else + { + if (c.IsPressed($"P{PortNum} Up")) { result &= 0xFE; } + if (c.IsPressed($"P{PortNum} Right")) { result &= 0xFD; } + if (c.IsPressed($"P{PortNum} Down")) { result &= 0xFB; } + if (c.IsPressed($"P{PortNum} Left")) { result &= 0xF7; } + } + + return result; + } + + private static readonly string[] BaseDefinition = + { + "Up", "Down", "Left", "Right", "Select", "Run", "B2", "B1" + }; + } +}