2013-12-01 20:55:52 +00:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2013-12-10 01:45:45 +00:00
using System.Text ;
2014-06-11 21:14:13 +00:00
using BizHawk.Common ;
2013-12-07 16:31:04 +00:00
using BizHawk.Emulation.Common ;
2014-08-23 18:02:02 +00:00
using System.ComponentModel ;
2013-12-07 16:31:04 +00:00
2013-12-01 20:55:52 +00:00
namespace BizHawk.Client.Common
{
2014-08-23 18:02:02 +00:00
public sealed partial class TasMovie : Bk2Movie , INotifyPropertyChanged
2013-12-01 20:55:52 +00:00
{
2014-08-29 02:40:45 +00:00
private readonly Bk2MnemonicConstants Mnemonics = new Bk2MnemonicConstants ( ) ;
2014-07-07 19:32:37 +00:00
private List < bool > LagLog = new List < bool > ( ) ;
2014-07-17 18:21:12 +00:00
private readonly TasStateManager StateManager ;
2014-08-24 14:41:56 +00:00
public TasMovieMarkerList Markers { get ; set ; }
2013-12-08 18:44:41 +00:00
2014-10-18 15:50:12 +00:00
public const string DefaultProjectName = "default" ;
2014-08-24 15:07:27 +00:00
public TasMovie ( string path ) : base ( path )
{
// TODO: how to call the default constructor AND the base(path) constructor? And is base(path) calling base() ?
StateManager = new TasStateManager ( this ) ;
Header [ HeaderKeys . MOVIEVERSION ] = "BizHawk v2.0 Tasproj v1.0" ;
Markers = new TasMovieMarkerList ( this ) ;
Markers . CollectionChanged + = Markers_CollectionChanged ;
Markers . Add ( 0 , StartsFromSavestate ? "Savestate" : "Power on" ) ;
}
2013-12-01 22:29:38 +00:00
2014-06-14 20:17:07 +00:00
public TasMovie ( )
2014-06-14 21:09:14 +00:00
: base ( )
2013-12-01 22:29:38 +00:00
{
2014-07-17 18:21:12 +00:00
StateManager = new TasStateManager ( this ) ;
2014-07-13 14:26:40 +00:00
Header [ HeaderKeys . MOVIEVERSION ] = "BizHawk v2.0 Tasproj v1.0" ;
2014-07-17 18:38:30 +00:00
Markers = new TasMovieMarkerList ( this ) ;
2014-08-24 14:41:56 +00:00
Markers . CollectionChanged + = Markers_CollectionChanged ;
2014-07-15 23:43:17 +00:00
Markers . Add ( 0 , StartsFromSavestate ? "Savestate" : "Power on" ) ;
2014-06-11 21:20:23 +00:00
}
2014-06-14 21:09:14 +00:00
public override string PreferredExtension
2014-06-11 21:20:23 +00:00
{
2014-07-07 18:00:25 +00:00
get { return Extension ; }
2014-06-11 21:20:23 +00:00
}
2014-10-14 13:31:14 +00:00
public TasStateManager TasStateManager
{
get { return StateManager ; }
}
public new const string Extension = "tasproj" ;
public TasMovieRecord this [ int index ]
{
get
{
return new TasMovieRecord
{
State = StateManager [ index ] ,
LogEntry = GetInputLogEntry ( index ) ,
Lagged = ( index < LagLog . Count ) ? LagLog [ index ] : ( bool? ) null
} ;
}
}
2014-09-27 23:56:23 +00:00
public override bool Stop ( bool saveChanges = true )
{
2014-10-18 15:17:49 +00:00
return base . Stop ( saveChanges ) ;
2014-09-27 23:56:23 +00:00
}
2014-08-24 14:41:56 +00:00
#region Events and Handlers
2014-08-23 18:02:02 +00:00
public event PropertyChangedEventHandler PropertyChanged ;
private bool _changes ;
public override bool Changes
{
get { return _changes ; }
protected set
{
if ( _changes ! = value )
{
_changes = value ;
OnPropertyChanged ( "Changes" ) ;
}
}
}
2014-10-14 13:31:14 +00:00
// This event is Raised ony when Changes is TOGGLED.
2014-08-23 18:02:02 +00:00
private void OnPropertyChanged ( string propertyName )
{
if ( PropertyChanged ! = null )
{
2014-10-14 13:31:14 +00:00
// Raising the event when FirstName or LastName property value changed
2014-08-23 18:02:02 +00:00
PropertyChanged . Invoke ( this , new PropertyChangedEventArgs ( propertyName ) ) ;
}
}
2014-08-24 14:41:56 +00:00
void Markers_CollectionChanged ( object sender , System . Collections . Specialized . NotifyCollectionChangedEventArgs e )
{
Changes = true ;
}
2013-12-01 20:55:52 +00:00
2014-08-24 14:41:56 +00:00
#endregion
2014-07-11 18:06:18 +00:00
public void ClearChanges ( )
{
Changes = false ;
}
2014-07-08 13:33:01 +00:00
public override void StartNewRecording ( )
{
LagLog . Clear ( ) ;
StateManager . Clear ( ) ;
2014-07-09 16:35:39 +00:00
Markers . Clear ( ) ;
2014-07-08 13:33:01 +00:00
base . StartNewRecording ( ) ;
2014-07-15 23:43:17 +00:00
Markers . Add ( 0 , StartsFromSavestate ? "Savestate" : "Power on" ) ;
2014-07-08 13:33:01 +00:00
}
2014-07-09 16:35:39 +00:00
2014-08-23 18:02:02 +00:00
public override void SwitchToPlay ( )
{
_mode = Moviemode . Play ;
}
2014-07-11 19:58:24 +00:00
/// <summary>
/// Removes lag log and greenzone after this frame
/// </summary>
2014-08-23 02:06:56 +00:00
/// <param name="frame">The last frame that can be valid.</param>
2014-07-11 19:58:24 +00:00
private void InvalidateAfter ( int frame )
{
if ( frame < LagLog . Count )
{
LagLog . RemoveRange ( frame + 1 , LagLog . Count - frame - 1 ) ;
}
StateManager . Invalidate ( frame + 1 ) ;
2014-08-22 17:04:31 +00:00
Changes = true ; // TODO check if this actually removed anything before flagging changes
2014-07-11 19:58:24 +00:00
}
2014-07-10 02:45:56 +00:00
/// <summary>
/// Returns the mnemonic value for boolean buttons, and actual value for floats,
2014-08-22 17:04:31 +00:00
/// for a given frame and button.
2014-07-10 02:45:56 +00:00
/// </summary>
public string DisplayValue ( int frame , string buttonName )
{
2014-08-29 02:58:52 +00:00
if ( UseInputCache & & InputStateCache . ContainsKey ( frame ) )
{
return CreateDisplayValueForButton ( InputStateCache [ frame ] , buttonName ) ;
}
2014-07-10 02:45:56 +00:00
var adapter = GetInputState ( frame ) ;
2014-08-29 02:58:52 +00:00
if ( UseInputCache )
{
InputStateCache . Add ( frame , adapter ) ;
}
2014-07-16 23:04:56 +00:00
return CreateDisplayValueForButton ( adapter , buttonName ) ;
}
2014-08-29 02:58:52 +00:00
private readonly Dictionary < int , IController > InputStateCache = new Dictionary < int , IController > ( ) ;
public bool UseInputCache { get ; set ; }
public void FlushInputCache ( )
{
InputStateCache . Clear ( ) ;
}
2014-08-29 01:59:08 +00:00
public string CreateDisplayValueForButton ( IController adapter , string buttonName )
2014-07-16 23:04:56 +00:00
{
2014-07-10 02:45:56 +00:00
if ( adapter . Type . BoolButtons . Contains ( buttonName ) )
{
return adapter . IsPressed ( buttonName ) ?
2014-08-29 02:40:45 +00:00
Mnemonics [ buttonName ] . ToString ( ) :
2014-07-10 02:45:56 +00:00
string . Empty ;
}
if ( adapter . Type . FloatControls . Contains ( buttonName ) )
{
2014-07-11 16:26:19 +00:00
return adapter . GetFloat ( buttonName ) . ToString ( ) ;
2014-07-10 02:45:56 +00:00
}
return "!" ;
}
2014-07-10 20:40:50 +00:00
public void ToggleBoolState ( int frame , string buttonName )
{
if ( frame < _log . Count )
{
var adapter = GetInputState ( frame ) as Bk2ControllerAdapter ;
adapter [ buttonName ] = ! adapter . IsPressed ( buttonName ) ;
var lg = LogGeneratorInstance ( ) ;
lg . SetSource ( adapter ) ;
_log [ frame ] = lg . GenerateLogEntry ( ) ;
Changes = true ;
2014-07-11 19:58:24 +00:00
InvalidateAfter ( frame ) ;
2014-07-10 20:40:50 +00:00
}
}
public void SetBoolState ( int frame , string buttonName , bool val )
{
if ( frame < _log . Count )
{
var adapter = GetInputState ( frame ) as Bk2ControllerAdapter ;
var old = adapter [ buttonName ] ;
adapter [ buttonName ] = val ;
var lg = LogGeneratorInstance ( ) ;
lg . SetSource ( adapter ) ;
_log [ frame ] = lg . GenerateLogEntry ( ) ;
if ( old ! = val )
{
2014-07-11 19:58:24 +00:00
InvalidateAfter ( frame ) ;
2014-07-10 20:40:50 +00:00
Changes = true ;
}
}
}
2014-07-11 16:26:19 +00:00
public void SetFloatState ( int frame , string buttonName , float val )
{
if ( frame < _log . Count )
{
var adapter = GetInputState ( frame ) as Bk2ControllerAdapter ;
var old = adapter . GetFloat ( buttonName ) ;
adapter . SetFloat ( buttonName , val ) ;
var lg = LogGeneratorInstance ( ) ;
lg . SetSource ( adapter ) ;
_log [ frame ] = lg . GenerateLogEntry ( ) ;
if ( old ! = val )
{
2014-07-11 19:58:24 +00:00
InvalidateAfter ( frame ) ;
2014-07-11 16:26:19 +00:00
Changes = true ;
}
}
}
2014-07-10 20:40:50 +00:00
public bool BoolIsPressed ( int frame , string buttonName )
{
var adapter = GetInputState ( frame ) as Bk2ControllerAdapter ;
return adapter . IsPressed ( buttonName ) ;
}
2014-07-10 20:48:43 +00:00
2014-07-11 16:26:19 +00:00
public float GetFloatValue ( int frame , string buttonName )
{
var adapter = GetInputState ( frame ) as Bk2ControllerAdapter ;
return adapter . GetFloat ( buttonName ) ;
}
2014-07-13 22:17:31 +00:00
// TODO: try not to need this, or at least use GetInputState and then a log entry generator
public string GetInputLogEntry ( int frame )
2014-07-10 20:48:43 +00:00
{
2014-07-13 22:17:31 +00:00
if ( frame < FrameCount & & frame > = 0 )
{
int getframe ;
if ( LoopOffset . HasValue )
{
if ( frame < _log . Count )
{
getframe = frame ;
}
else
{
getframe = ( ( frame - LoopOffset . Value ) % ( _log . Count - LoopOffset . Value ) ) + LoopOffset . Value ;
}
}
else
{
getframe = frame ;
}
return _log [ getframe ] ;
}
return string . Empty ;
2014-07-10 20:48:43 +00:00
}
2014-07-11 15:43:47 +00:00
2014-10-02 22:58:36 +00:00
public void ClearGreenzone ( )
{
2014-10-02 23:50:50 +00:00
if ( StateManager . Any ( ) )
2014-10-02 22:58:36 +00:00
{
2014-10-02 23:50:50 +00:00
StateManager . ClearGreenzone ( ) ;
2014-10-02 22:58:36 +00:00
Changes = true ;
}
}
2014-10-16 23:05:59 +00:00
public override IController GetInputState ( int frame )
{
// TODO: states and lag capture
if ( Global . Emulator . Frame = = frame ) // Take this opportunity to capture lag and state info if we do not have it
{
if ( frame = = LagLog . Count ) // I intentionally did not do >=, if it were >= we missed some entries somewhere, oops, maybe this shoudl be a dictionary<int, bool> with frame values?
{
LagLog . Add ( Global . Emulator . IsLagFrame ) ;
}
if ( ! StateManager . HasState ( frame ) )
{
StateManager . Capture ( ) ;
}
}
return base . GetInputState ( frame ) ;
}
2013-12-01 20:55:52 +00:00
}
}