BizHawk/EMU7800/Core/InputState.cs

389 lines
15 KiB
C#

/*
* InputState.cs
*
* Class containing the input state of the console and its controllers,
* mapping emulator input devices to external input.
*
* Copyright © 2003-2010 Mike Murphy
*
*/
using System;
namespace EMU7800.Core
{
public class InputState
{
#region Fields
const int
PaddleOhmMin = 100000,
PaddleOhmMax = 800000;
const int
LeftControllerJackIndex = 0,
RightControllerJackIndex = 1,
ConsoleSwitchIndex = 2,
ControllerActionStateIndex = 3,
OhmsIndex = ControllerActionStateIndex + 4,
LightgunPositionIndex = ControllerActionStateIndex + 4,
InputStateSize = ControllerActionStateIndex + 8 + 1;
// For driving controllers
readonly byte[] _rotGrayCodes = new byte[] { 0x0f, 0x0d, 0x0c, 0x0e };
readonly int[] _rotState = new int[2];
readonly int[] _nextInputState = new int[InputStateSize];
readonly int[] _inputState = new int[InputStateSize];
bool _lagged = true;
#endregion
#region Public Members
/// <summary>
/// Enables the incoming input state buffer to be populated prior to the start of the frame.
/// Useful for input playback senarios.
/// </summary>
/// <return>Return value is ignored.</return>
public Func<int[], object> InputAdvancing { get; set; }
/// <summary>
/// Enables access to the input state buffer.
/// Useful for input recording senarios.
/// </summary>
/// <return>Return value is ignored.</return>
public Func<int[], object> InputAdvanced { get; set; }
public void CaptureInputState()
{
if (InputAdvancing != null)
InputAdvancing(_nextInputState);
Buffer.BlockCopy(_nextInputState, 0, _inputState, 0, InputStateSize * sizeof(int));
if (InputAdvanced != null)
InputAdvanced(_inputState);
_lagged = true;
}
public Controller LeftControllerJack
{
get { return (Controller)_nextInputState[LeftControllerJackIndex]; }
set { _nextInputState[LeftControllerJackIndex] = (int)value; }
}
public Controller RightControllerJack
{
get { return (Controller)_nextInputState[RightControllerJackIndex]; }
set { _nextInputState[RightControllerJackIndex] = (int)value; }
}
public bool IsGameBWConsoleSwitchSet
{
get { return (_nextInputState[ConsoleSwitchIndex] & (1 << (int) ConsoleSwitch.GameBW)) != 0; }
}
public bool IsLeftDifficultyAConsoleSwitchSet
{
get { return (_nextInputState[ConsoleSwitchIndex] & (1 << (int)ConsoleSwitch.LeftDifficultyA)) != 0; }
}
public bool IsRightDifficultyAConsoleSwitchSet
{
get { return (_nextInputState[ConsoleSwitchIndex] & (1 << (int)ConsoleSwitch.RightDifficultyA)) != 0; }
}
public bool Lagged
{
get { return _lagged; }
}
public Action InputPollCallback { get; set; }
public void RaiseInput(int playerNo, MachineInput input, bool down)
{
if (InputPollCallback != null)
{
InputPollCallback();
}
switch (input)
{
case MachineInput.Fire:
SetControllerActionState(playerNo, ControllerAction.Trigger, down);
break;
case MachineInput.Fire2:
SetControllerActionState(playerNo, ControllerAction.Trigger2, down);
break;
case MachineInput.Left:
SetControllerActionState(playerNo, ControllerAction.Left, down);
if (down) SetControllerActionState(playerNo, ControllerAction.Right, false);
break;
case MachineInput.Up:
SetControllerActionState(playerNo, ControllerAction.Up, down);
if (down) SetControllerActionState(playerNo, ControllerAction.Down, false);
break;
case MachineInput.Right:
SetControllerActionState(playerNo, ControllerAction.Right, down);
if (down) SetControllerActionState(playerNo, ControllerAction.Left, false);
break;
case MachineInput.Down:
SetControllerActionState(playerNo, ControllerAction.Down, down);
if (down) SetControllerActionState(playerNo, ControllerAction.Up, false);
break;
case MachineInput.NumPad7:
SetControllerActionState(playerNo, ControllerAction.Keypad7, down);
break;
case MachineInput.NumPad8:
SetControllerActionState(playerNo, ControllerAction.Keypad8, down);
break;
case MachineInput.NumPad9:
SetControllerActionState(playerNo, ControllerAction.Keypad9, down);
break;
case MachineInput.NumPad4:
SetControllerActionState(playerNo, ControllerAction.Keypad4, down);
break;
case MachineInput.NumPad5:
SetControllerActionState(playerNo, ControllerAction.Keypad5, down);
break;
case MachineInput.NumPad6:
SetControllerActionState(playerNo, ControllerAction.Keypad6, down);
break;
case MachineInput.NumPad1:
SetControllerActionState(playerNo, ControllerAction.Keypad1, down);
break;
case MachineInput.NumPad2:
SetControllerActionState(playerNo, ControllerAction.Keypad2, down);
break;
case MachineInput.NumPad3:
SetControllerActionState(playerNo, ControllerAction.Keypad3, down);
break;
case MachineInput.NumPadMult:
SetControllerActionState(playerNo, ControllerAction.KeypadA, down);
break;
case MachineInput.NumPad0:
SetControllerActionState(playerNo, ControllerAction.Keypad0, down);
break;
case MachineInput.NumPadHash:
SetControllerActionState(playerNo, ControllerAction.KeypadP, down);
break;
case MachineInput.Driving0:
SetControllerActionState(playerNo, ControllerAction.Driving0, true);
SetControllerActionState(playerNo, ControllerAction.Driving1, false);
SetControllerActionState(playerNo, ControllerAction.Driving2, false);
SetControllerActionState(playerNo, ControllerAction.Driving3, false);
break;
case MachineInput.Driving1:
SetControllerActionState(playerNo, ControllerAction.Driving0, false);
SetControllerActionState(playerNo, ControllerAction.Driving1, true);
SetControllerActionState(playerNo, ControllerAction.Driving2, false);
SetControllerActionState(playerNo, ControllerAction.Driving3, false);
break;
case MachineInput.Driving2:
SetControllerActionState(playerNo, ControllerAction.Driving0, false);
SetControllerActionState(playerNo, ControllerAction.Driving1, false);
SetControllerActionState(playerNo, ControllerAction.Driving2, true);
SetControllerActionState(playerNo, ControllerAction.Driving3, false);
break;
case MachineInput.Driving3:
SetControllerActionState(playerNo, ControllerAction.Driving0, false);
SetControllerActionState(playerNo, ControllerAction.Driving1, false);
SetControllerActionState(playerNo, ControllerAction.Driving2, false);
SetControllerActionState(playerNo, ControllerAction.Driving3, true);
break;
case MachineInput.Reset:
SetConsoleSwitchState(ConsoleSwitch.GameReset, down);
break;
case MachineInput.Select:
SetConsoleSwitchState(ConsoleSwitch.GameSelect, down);
break;
case MachineInput.Color:
if (down) ToggleConsoleSwitchState(ConsoleSwitch.GameBW);
break;
case MachineInput.LeftDifficulty:
if (down) ToggleConsoleSwitchState(ConsoleSwitch.LeftDifficultyA);
break;
case MachineInput.RightDifficulty:
if (down) ToggleConsoleSwitchState(ConsoleSwitch.RightDifficultyA);
break;
}
}
public void RaisePaddleInput(int playerNo, int valMax, int val)
{
var ohms = PaddleOhmMax - (PaddleOhmMax - PaddleOhmMin) / valMax * val;
_nextInputState[OhmsIndex + (playerNo & 3)] = ohms;
}
public void RaiseLightgunPos(int playerNo, int scanline, int hpos)
{
var i = LightgunPositionIndex + ((playerNo & 1) << 1);
_nextInputState[i++] = scanline;
_nextInputState[i] = hpos;
}
public void ClearAllInput()
{
_nextInputState[ConsoleSwitchIndex] = 0;
ClearLeftJackInput();
ClearRightJackInput();
}
public void ClearInputByPlayer(int playerNo)
{
_nextInputState[OhmsIndex + (playerNo & 3)] = 0;
_nextInputState[ControllerActionStateIndex + (playerNo & 3)] = 0;
_nextInputState[LightgunPositionIndex + ((playerNo & 1) << 1)] = _nextInputState[LightgunPositionIndex + ((playerNo & 1) << 1) + 1] = 0;
}
public void ClearLeftJackInput()
{
_nextInputState[OhmsIndex] = _nextInputState[OhmsIndex + 1] = 0;
_nextInputState[ControllerActionStateIndex] = 0;
switch (LeftControllerJack)
{
case Controller.Paddles:
_nextInputState[ControllerActionStateIndex] = _nextInputState[ControllerActionStateIndex + 1] = 0;
break;
default:
_nextInputState[ControllerActionStateIndex] = 0;
break;
}
_nextInputState[LightgunPositionIndex] = _nextInputState[LightgunPositionIndex + 1] = 0;
}
public void ClearRightJackInput()
{
_nextInputState[OhmsIndex + 2] = _nextInputState[OhmsIndex + 3] = 0;
switch (RightControllerJack)
{
case Controller.Paddles:
_nextInputState[ControllerActionStateIndex + 2] = _nextInputState[ControllerActionStateIndex + 3] = 0;
break;
default:
_nextInputState[ControllerActionStateIndex + 1] = 0;
break;
}
_nextInputState[LightgunPositionIndex + 2] = _nextInputState[LightgunPositionIndex + 3] = 0;
}
#endregion
#region Serialization Members
public InputState()
{
}
public InputState(DeserializationContext input)
{
if (input == null)
throw new ArgumentNullException("input");
input.CheckVersion(2);
_rotState = input.ReadIntegers(2);
_nextInputState = input.ReadIntegers(InputStateSize);
_inputState = input.ReadIntegers(InputStateSize);
_lagged = input.ReadBoolean();
}
public void GetObjectData(SerializationContext output)
{
if (output == null)
throw new ArgumentNullException("output");
output.WriteVersion(2);
output.Write(_rotState);
output.Write(_nextInputState);
output.Write(_inputState);
output.Write(_lagged);
}
#endregion
#region Internal Members
internal bool SampleCapturedConsoleSwitchState(ConsoleSwitch consoleSwitch)
{
_lagged = false;
return (_inputState[ConsoleSwitchIndex] & (1 << (int)consoleSwitch)) != 0;
}
internal bool SampleCapturedControllerActionState(int playerno, ControllerAction action)
{
_lagged = false;
return (_inputState[ControllerActionStateIndex + (playerno & 3)] & (1 << (int)action)) != 0;
}
internal int SampleCapturedOhmState(int playerNo)
{
_lagged = false;
return _inputState[OhmsIndex + (playerNo & 3)];
}
internal void SampleCapturedLightGunPosition(int playerNo, out int scanline, out int hpos)
{
_lagged = false;
var i = LightgunPositionIndex + ((playerNo & 1) << 1);
scanline = _inputState[i++];
hpos = _inputState[i];
}
internal byte SampleCapturedDrivingState(int playerNo)
{
_lagged = false;
if (SampleCapturedControllerActionState(playerNo, ControllerAction.Driving0))
_rotState[playerNo] = 0;
else if (SampleCapturedControllerActionState(playerNo, ControllerAction.Driving1))
_rotState[playerNo] = 1;
else if (SampleCapturedControllerActionState(playerNo, ControllerAction.Driving2))
_rotState[playerNo] = 2;
else if (SampleCapturedControllerActionState(playerNo, ControllerAction.Driving3))
_rotState[playerNo] = 3;
return _rotGrayCodes[_rotState[playerNo]];
}
#endregion
#region Object Overrides
public override string ToString()
{
return GetType().Name;
}
#endregion
#region Helpers
void SetControllerActionState(int playerNo, ControllerAction action, bool value)
{
if (value)
{
_nextInputState[ControllerActionStateIndex + (playerNo & 3)] |= (1 << (int)action);
}
else
{
_nextInputState[ControllerActionStateIndex + (playerNo & 3)] &= ~(1 << (int)action);
}
}
void SetConsoleSwitchState(ConsoleSwitch consoleSwitch, bool value)
{
if (value)
{
_nextInputState[ConsoleSwitchIndex] |= (byte)(1 << (byte)consoleSwitch);
}
else
{
_nextInputState[ConsoleSwitchIndex] &= (byte)~(1 << (byte)consoleSwitch);
}
}
void ToggleConsoleSwitchState(ConsoleSwitch consoleSwitch)
{
var consoleSwitchState = (_nextInputState[ConsoleSwitchIndex] & (1 << (int) consoleSwitch)) != 0;
SetConsoleSwitchState(consoleSwitch, !consoleSwitchState);
}
#endregion
}
}