diff --git a/InputSystems/ANX.InputSystems.Windows.Recording/RecordableDevice.cs b/InputSystems/ANX.InputSystems.Windows.Recording/RecordableDevice.cs index eff1a9fc..34f16fae 100644 --- a/InputSystems/ANX.InputSystems.Windows.Recording/RecordableDevice.cs +++ b/InputSystems/ANX.InputSystems.Windows.Recording/RecordableDevice.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.IO; #endregion @@ -58,16 +59,44 @@ namespace ANX.InputSystem.Windows.Recording /// /// Abstract Class. Classes derived from this class allow recording and Playback of Inputs. /// - abstract class RecordableDevice + public abstract class RecordableDevice { + protected Stream recordStream; //The stream where the input is written to + protected int nullStateCounter; //Used to sum up frames with no input. + public RecordingState RecordingState { get; protected set; } + public event EventHandler EndOfPlaybackReached; + + /// + /// How many bytes this Instance requires per Frame. Must never change! + /// + public int FramePacketLenght { get; protected set; } + public RecordableDevice() { RecordingState = RecordingState.None; } - - public void StartRecording() + + /// + /// Initializes the Device using a new + /// MemoryStream for input-buffering. + /// + public virtual void Initialize() + { + Initialize(new MemoryStream()); + } + + /// + /// Initializes the Device using the specified stream + /// for input-buffering. + /// + public virtual void Initialize(Stream bufferStream) + { + recordStream = bufferStream; + } + + public virtual void StartRecording() { if (RecordingState == RecordingState.Recording) return; @@ -75,7 +104,7 @@ namespace ANX.InputSystem.Windows.Recording RecordingState = RecordingState.Recording; } - public void StopRecording() + public virtual void StopRecording() { if (RecordingState != RecordingState.Recording) throw new InvalidOperationException("Recording wasn't started for this device!"); @@ -83,7 +112,7 @@ namespace ANX.InputSystem.Windows.Recording RecordingState = RecordingState.None; } - public void StartPlayback() + public virtual void StartPlayback() { if (RecordingState == RecordingState.Recording) throw new InvalidOperationException("Recording is currently running for this device."); @@ -91,9 +120,78 @@ namespace ANX.InputSystem.Windows.Recording RecordingState = RecordingState.Playback; } - public void StopPlayback() + public virtual void StopPlayback() { RecordingState = RecordingState.None; } + + /// + /// Writes the current input state to the buffering stream. Pass null + /// for state, if no input is done (no keys down etc.). + /// Must be called once per Frame! + /// + protected virtual void WriteState(byte[] state) + { + if (state == null) + { + nullStateCounter++; + return; + } + + if (state.Length != FramePacketLenght) + throw new InvalidOperationException("The passed state's lenght does not match the speficed FramePaketLenght."); + + if (nullStateCounter > 0) //Note how many packets we had nothing + { + recordStream.WriteByte((byte)PacketType.NullFrameCounter); + recordStream.Write(BitConverter.GetBytes(nullStateCounter), 0, 4); + } + + recordStream.WriteByte((byte)PacketType.InputData); + recordStream.Write(state, 0, state.Length); + } + + /// + /// Reads the next input-state from the buffering stream. Might + /// return null, if no input was made in this frame. + /// Must be called once per Frame! + /// + protected virtual byte[] ReadState() + { + if (nullStateCounter > 0) //we have null-states pending + { + nullStateCounter--; + return null; + } + + if (recordStream.Position == recordStream.Length) + { + OnEndOfPlaybackReached(); + return null; //TODO: Better switch to RecordingState.None here? + } + + PacketType type = (PacketType)recordStream.ReadByte(); + switch (type) + { + case PacketType.NullFrameCounter: + byte[] buffer = new byte[4]; + recordStream.Read(buffer, 0, 4); + nullStateCounter = BitConverter.ToInt32(buffer, 0) - 1; + return null; + case PacketType.InputData: + byte[] buffer2 = new byte[FramePacketLenght]; + recordStream.Read(buffer2, 0, FramePacketLenght); + return buffer2; + default: + throw new NotImplementedException("The PaketType " + Enum.GetName(typeof(PacketType), type) + "is not supported."); + } + + } + + protected virtual void OnEndOfPlaybackReached() + { + if (EndOfPlaybackReached != null) + EndOfPlaybackReached(this, EventArgs.Empty); + } } } diff --git a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingGamePad.cs b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingGamePad.cs index 77b651bc..3e7ee303 100644 --- a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingGamePad.cs +++ b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingGamePad.cs @@ -61,7 +61,7 @@ namespace ANX.InputSystem.Windows.Recording /// /// Wrapper arround another IGamePad, will record all inputs and allows playback. /// - class RecordingGamePad : RecordableDevice,IGamePad + public class RecordingGamePad : RecordableDevice, IGamePad { public GamePadCapabilities GetCapabilities(PlayerIndex playerIndex) { diff --git a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingHelper.cs b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingHelper.cs index 68f5c07b..130202f6 100644 --- a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingHelper.cs +++ b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingHelper.cs @@ -55,7 +55,7 @@ using System.Text; namespace ANX.InputSystem.Windows.Recording { - enum RecordingState + public enum RecordingState { /// /// This device is recording input. @@ -70,11 +70,18 @@ namespace ANX.InputSystem.Windows.Recording /// None } + + enum PacketType : byte + { + NullFrameCounter = 0, + InputData = 1 + } /// /// Static Helper-class containing some recording related stuff. /// static class RecordingHelper { + } } diff --git a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingKeyboard.cs b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingKeyboard.cs index 282d117c..a52a2e8f 100644 --- a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingKeyboard.cs +++ b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingKeyboard.cs @@ -60,7 +60,7 @@ namespace ANX.InputSystem.Windows.Recording /// /// Wrapper arround another IKeyboard, will record all inputs and allows playback. /// - class RecordingKeyboard : RecordableDevice, IKeyboard + public class RecordingKeyboard : RecordableDevice, IKeyboard { public IntPtr WindowHandle { get; set; } diff --git a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingMotionSensingDevice.cs b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingMotionSensingDevice.cs index 42510a66..8982f7e2 100644 --- a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingMotionSensingDevice.cs +++ b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingMotionSensingDevice.cs @@ -61,7 +61,7 @@ namespace ANX.InputSystem.Windows.Recording /// /// Wrapper aroung another IMotionSensingDevice, will record all inputs and allows playback. /// - class RecordingMotionSensingDevice : RecordableDevice,IMotionSensingDevice + public class RecordingMotionSensingDevice : RecordableDevice, IMotionSensingDevice { public GraphicsDevice GraphicsDevice { diff --git a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingMouse.cs b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingMouse.cs index a1c8aec9..070d2784 100644 --- a/InputSystems/ANX.InputSystems.Windows.Recording/RecordingMouse.cs +++ b/InputSystems/ANX.InputSystems.Windows.Recording/RecordingMouse.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using ANX.Framework.NonXNA; using ANX.Framework.Input; +using System.IO; #endregion @@ -57,10 +58,28 @@ using ANX.Framework.Input; namespace ANX.InputSystem.Windows.Recording { + [Flags] + public enum MouseRecordInfo : byte + { + LeftButton = 1, + RightButton = 2, + MiddleButton = 4, + X1Button = 8, + X2Button = 16, + ScrollWheel = 32, + XPosition = 64, + YPosition = 128, + LRMButtons = LeftButton | RightButton | MiddleButton, + XButtons = X1Button | X2Button, + AllButtons = LRMButtons | XButtons, + Position = XPosition | YPosition, + All = AllButtons | Position | ScrollWheel + } + /// /// Wrapper arround another IGamePad, will record all inputs and allows playback. /// - class RecordingMouse : RecordableDevice,IMouse + public class RecordingMouse : RecordableDevice, IMouse { public IntPtr WindowHandle { get; set; } @@ -73,5 +92,39 @@ namespace ANX.InputSystem.Windows.Recording { throw new NotImplementedException(); } + + public void Initialize(MouseRecordInfo info) + { + base.Initialize(); + } + + public void Initialize(MouseRecordInfo info, Stream bufferStream) + { + base.Initialize(bufferStream); + } + + private int GetPaketSize(MouseRecordInfo info) + { + int ret = 0; //TODO: Pack the bools in one byte to save space sizeof(bool) == sizeof(byte)! + if (info.HasFlag(MouseRecordInfo.LeftButton)) + ret += sizeof(bool); + if (info.HasFlag(MouseRecordInfo.RightButton)) + ret += sizeof(bool); + if (info.HasFlag(MouseRecordInfo.MiddleButton)) + ret += sizeof(bool); + if (info.HasFlag(MouseRecordInfo.X1Button)) + ret += sizeof(bool); + if (info.HasFlag(MouseRecordInfo.X2Button)) + ret += sizeof(bool); + + if (info.HasFlag(MouseRecordInfo.XPosition)) + ret += sizeof(int); + if (info.HasFlag(MouseRecordInfo.YPosition)) + ret += sizeof(int); + if (info.HasFlag(MouseRecordInfo.ScrollWheel)) + ret += sizeof(int); + + return ret; + } } }