using System; using System.Collections.Generic; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx { public class GPGXControlConverter { // this isn't all done struct CName { public readonly string Name; public readonly LibGPGX.INPUT_KEYS Key; public CName(string name, LibGPGX.INPUT_KEYS key) { Name = name; Key = key; } } private static readonly CName[] Genesis3 = { new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP), new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN), new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT), new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT), new CName("A", LibGPGX.INPUT_KEYS.INPUT_A), new CName("B", LibGPGX.INPUT_KEYS.INPUT_B), new CName("C", LibGPGX.INPUT_KEYS.INPUT_C), new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START), }; private static readonly CName[] Genesis6 = { new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP), new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN), new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT), new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT), new CName("A", LibGPGX.INPUT_KEYS.INPUT_A), new CName("B", LibGPGX.INPUT_KEYS.INPUT_B), new CName("C", LibGPGX.INPUT_KEYS.INPUT_C), new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START), new CName("X", LibGPGX.INPUT_KEYS.INPUT_X), new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y), new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z), new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE), }; private static readonly CName[] Mouse = { new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT), new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER), new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT), new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START), }; private static readonly CName[] Lightgun = { new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER), new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START), }; private static readonly CName[] Activator = { new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L), new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U), new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L), new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U), new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L), new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U), new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L), new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U), new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L), new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U), new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L), new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U), new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L), new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U), new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L), new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U), }; private static readonly CName[] Xea1P = { new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A), new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B), new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C), new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D), new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START), new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT), new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1), new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2), }; private static readonly ControllerDefinition.AxisRange MouseRange = new ControllerDefinition.AxisRange(-256, 0, 255); // lightgun needs to be transformed to match the current screen resolution private static readonly ControllerDefinition.AxisRange LightgunRange = new ControllerDefinition.AxisRange(0, 5000, 10000); private static readonly ControllerDefinition.AxisRange Xea1PRange = new ControllerDefinition.AxisRange(-128, 0, 127); private LibGPGX.InputData _target; private IController _source; private readonly List _converts = new List(); public ControllerDefinition ControllerDef { get; } void AddToController(int idx, int player, IEnumerable buttons) { foreach (var button in buttons) { string name = $"P{player} {button.Name}"; ControllerDef.BoolButtons.Add(name); var buttonFlag = button.Key; _converts.Add(delegate { if (_source.IsPressed(name)) { _target.pad[idx] |= buttonFlag; } }); } } private void DoMouseAnalog(int idx, int player) { string nx = $"P{player} Mouse X"; string ny = $"P{player} Mouse Y"; ControllerDef.AxisControls.Add(nx); ControllerDef.AxisControls.Add(ny); ControllerDef.AxisRanges.Add(MouseRange); ControllerDef.AxisRanges.Add(MouseRange); _converts.Add(delegate { _target.analog[(2 * idx) + 0] = (short)_source.AxisValue(nx); _target.analog[(2 * idx) + 1] = (short)_source.AxisValue(ny); }); } private void DoLightgunAnalog(int idx, int player) { string nx = $"P{player} Lightgun X"; string ny = $"P{player} Lightgun Y"; ControllerDef.AxisControls.Add(nx); ControllerDef.AxisControls.Add(ny); ControllerDef.AxisRanges.Add(LightgunRange); ControllerDef.AxisRanges.Add(LightgunRange); _converts.Add(delegate { _target.analog[(2 * idx) + 0] = (short)(_source.AxisValue(nx) / 10000.0f * (ScreenWidth - 1)); _target.analog[(2 * idx) + 1] = (short)(_source.AxisValue(ny) / 10000.0f * (ScreenHeight - 1)); }); } private void DoXea1PAnalog(int idx, int player) { string nx = $"P{player} Stick X"; string ny = $"P{player} Stick Y"; string nz = $"P{player} Stick Z"; ControllerDef.AxisControls.Add(nx); ControllerDef.AxisControls.Add(ny); ControllerDef.AxisControls.Add(nz); ControllerDef.AxisRanges.Add(Xea1PRange); ControllerDef.AxisRanges.Add(Xea1PRange); ControllerDef.AxisRanges.Add(Xea1PRange); _converts.Add(delegate { _target.analog[(2 * idx) + 0] = (short)_source.AxisValue(nx); _target.analog[(2 * idx) + 1] = (short)_source.AxisValue(ny); // +2 is correct in how gpgx internally does this _target.analog[(2 * idx) + 2] = (short)(_source.AxisValue(nz)); }); } public GPGXControlConverter(LibGPGX.InputData input, bool cdButtons) { Console.WriteLine("Genesis Controller report:"); foreach (var e in input.system) Console.WriteLine("S:{0}", e); foreach (var e in input.dev) Console.WriteLine("D:{0}", e); int player = 1; ControllerDef = new ControllerDefinition(); ControllerDef.BoolButtons.Add("Power"); ControllerDef.BoolButtons.Add("Reset"); if (cdButtons) { ControllerDef.BoolButtons.Add("Previous Disk"); ControllerDef.BoolButtons.Add("Next Disk"); } for (int i = 0; i < LibGPGX.MAX_DEVICES; i++) { switch (input.dev[i]) { case LibGPGX.INPUT_DEVICE.DEVICE_PAD3B: AddToController(i, player, Genesis3); player++; break; case LibGPGX.INPUT_DEVICE.DEVICE_PAD6B: AddToController(i, player, Genesis6); player++; break; case LibGPGX.INPUT_DEVICE.DEVICE_MOUSE: AddToController(i, player, Mouse); DoMouseAnalog(i, player); player++; break; case LibGPGX.INPUT_DEVICE.DEVICE_NONE: break; case LibGPGX.INPUT_DEVICE.DEVICE_LIGHTGUN: // supports menacers and justifiers AddToController(i, player, Lightgun); DoLightgunAnalog(i, player); player++; break; case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B: case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE: case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD: case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI: throw new Exception("Master System only device? Something went wrong."); case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR: AddToController(i, player, Activator); player++; break; case LibGPGX.INPUT_DEVICE.DEVICE_XE_A1P: AddToController(i, player, Xea1P); DoXea1PAnalog(i, player); player++; break; case LibGPGX.INPUT_DEVICE.DEVICE_PICO: // PICO isn't finished on the unmanaged side either throw new Exception("Sega PICO not implemented yet!"); default: throw new Exception("Unknown Genesis control device! Something went wrong."); } } ControllerDef.Name = "GPGX Genesis Controller"; } public void Convert(IController source, LibGPGX.InputData target) { _source = source; _target = target; target.ClearAllBools(); foreach (var f in _converts) { f(); } _source = null; _target = null; } /// /// must be set for proper lightgun operation /// public int ScreenWidth { get; set; } /// /// must be set for proper lightgun operation /// public int ScreenHeight { get; set; } } }