#region Using Statements using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; #endregion // This file is part of the ANX.Framework created by the // "ANX.Framework developer group" and released under the Ms-PL license. // For details see: http://anxframework.codeplex.com/license namespace ANX.InputSystem.Recording { /// /// Abstract Class. Classes derived from this class allow recording and Playback of Inputs. /// 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. protected bool isInitialized = false; public RecordingState RecordingState { get; protected set; } public event EventHandler EndOfPlaybackReached; /// /// How many bytes this Instance requires per Packet. Must never change! /// public int PacketLenght { get; protected set; } public RecordableDevice() { RecordingState = RecordingState.None; } /// /// Initializes the Device using the specified stream /// for input-buffering. /// protected virtual void Initialize(Stream bufferStream) { if (!bufferStream.CanRead || !bufferStream.CanWrite) throw new ArgumentException("The stream must support read and write opearions!", "bufferStream"); recordStream = bufferStream; isInitialized = true; } public virtual void StartRecording() { if (!isInitialized) throw new InvalidOperationException("This instance is not initialized!"); if (RecordingState == RecordingState.Recording) return; //Reset the stream if we can seek if(recordStream.CanSeek) recordStream.Position = 0; RecordingState = RecordingState.Recording; } public virtual void StopRecording() { if (RecordingState != RecordingState.Recording) throw new InvalidOperationException("Recording wasn't started for this device!"); nullStateCounter = 0; RecordingState = RecordingState.None; } public virtual void StartPlayback() { if (!isInitialized) throw new InvalidOperationException("This instance is not initialized!"); if (RecordingState == RecordingState.Recording) throw new InvalidOperationException("Recording is currently running for this device."); //Reset the stream if we can seek if (recordStream.CanSeek) recordStream.Position = 0; RecordingState = RecordingState.Playback; } 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.). /// protected virtual void WriteState(byte[] state) { if (state == null) { nullStateCounter++; return; } if (state.Length != PacketLenght) throw new InvalidOperationException("The passed state's lenght does not match the speficed FramePacketLenght."); TryWriteNullStates(); recordStream.WriteByte((byte)PacketType.InputData); recordStream.Write(state, 0, state.Length); } /// /// Writes a custom packet to the stream. When this packet is found during ReadState() /// HandleUserPacket is called to handle the packet. Please note that the packetTypes 0 and /// 1 are reseved. /// protected virtual void WriteUserState(byte packetType, byte[] packetData) { TryWriteNullStates(); recordStream.WriteByte(packetType); // TODO: cast to byte correct? created build error cause of WriteByte and int as value recordStream.WriteByte((byte)packetData.Length); } private void TryWriteNullStates() { if (nullStateCounter > 0) //Note how many packets we had nothing { recordStream.WriteByte((byte)PacketType.NullFrameCounter); recordStream.Write(BitConverter.GetBytes(nullStateCounter), 0, 4); nullStateCounter = 0; } } /// /// Reads the next input-state from the buffering stream. Might /// return null, if no input was made at this Position. /// protected virtual byte[] ReadState() { if (nullStateCounter > 0) //we have null-states pending { nullStateCounter--; return null; } if (recordStream.Position == recordStream.Length) { OnEndOfPlaybackReached(); return null; } 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[PacketLenght]; recordStream.Read(buffer2, 0, PacketLenght); return buffer2; default: //Custom Packet Type byte packetLenght = (byte)recordStream.ReadByte(); byte[] buffer3 = new byte[packetLenght]; recordStream.Read(buffer3, 0, packetLenght); HandleUserPacket((byte)type, buffer3); return ReadState(); //We read another packet until we find a "valid" one. } } /// /// Fires the EndOfPlaybackReaced event and Calls StopPlayback(). /// Overwrite this method to change this behavoir. /// protected virtual void OnEndOfPlaybackReached() { StopPlayback(); if (EndOfPlaybackReached != null) EndOfPlaybackReached(this, EventArgs.Empty); } /// /// Overwrite this method to handle custom packet types written by WriteUserState(). When /// ReadState() encounters an unknown packet type this method is called. /// protected virtual void HandleUserPacket(byte packetType, byte[] packetData) { } } }