2013-11-01 18:52:26 +00:00
using System ;
2014-06-14 14:09:55 +00:00
using System.Collections.Generic ;
using System.Linq ;
2013-11-01 18:52:26 +00:00
using System.IO ;
2013-11-04 01:39:19 +00:00
using BizHawk.Emulation.Common ;
2013-11-01 18:52:26 +00:00
namespace BizHawk.Client.Common
2013-10-25 00:59:34 +00:00
{
public class MovieSession
{
2013-12-05 00:44:56 +00:00
private readonly MultitrackRecording _multiTrack = new MultitrackRecording ( ) ;
private readonly MovieControllerAdapter _movieControllerAdapter = new MovieControllerAdapter ( ) ;
2013-11-01 18:52:26 +00:00
2013-12-05 00:44:56 +00:00
public MovieSession ( )
{
ReadOnly = true ;
}
public MultitrackRecording MultiTrack { get { return _multiTrack ; } }
public MovieControllerAdapter MovieControllerAdapter { get { return _movieControllerAdapter ; } }
public IMovie Movie { get ; set ; }
public bool ReadOnly { get ; set ; }
public Action < string > MessageCallback { get ; set ; }
public Func < string , string , bool > AskYesNoCallback { get ; set ; }
2013-12-03 18:08:45 +00:00
2013-11-01 18:52:26 +00:00
private void Output ( string message )
{
if ( MessageCallback ! = null )
{
MessageCallback ( message ) ;
}
}
2013-12-05 00:44:56 +00:00
public void LatchMultitrackPlayerInput ( IController playerSource , MultitrackRewiringControllerAdapter rewiredSource )
2013-11-01 18:52:26 +00:00
{
2013-12-05 00:44:56 +00:00
if ( _multiTrack . IsActive )
2013-11-01 18:52:26 +00:00
{
2013-12-05 00:44:56 +00:00
rewiredSource . PlayerSource = 1 ;
rewiredSource . PlayerTargetMask = 1 < < _multiTrack . CurrentPlayer ;
if ( _multiTrack . RecordAll )
{
rewiredSource . PlayerTargetMask = unchecked ( ( int ) 0xFFFFFFFF ) ;
}
2013-11-01 18:52:26 +00:00
}
else
{
2013-12-05 00:44:56 +00:00
rewiredSource . PlayerSource = - 1 ;
2013-10-25 00:59:34 +00:00
}
2013-12-05 00:44:56 +00:00
_movieControllerAdapter . LatchPlayerFromSource ( rewiredSource , _multiTrack . CurrentPlayer ) ;
2013-10-25 00:59:34 +00:00
}
public void LatchInputFromPlayer ( IController source )
{
2013-12-05 00:44:56 +00:00
_movieControllerAdapter . LatchFromSource ( source ) ;
2013-10-25 00:59:34 +00:00
}
/// <summary>
2013-11-01 18:52:26 +00:00
/// Latch input from the input log, if available
2013-10-25 00:59:34 +00:00
/// </summary>
public void LatchInputFromLog ( )
{
2013-11-29 19:55:05 +00:00
var input = Movie . GetInput ( Global . Emulator . Frame ) ;
// Attempting to get a frame past the end of a movie changes the mode to finished
if ( ! Movie . IsFinished )
{
2013-12-05 00:44:56 +00:00
_movieControllerAdapter . SetControllersAsMnemonic ( input ) ;
2013-11-29 19:55:05 +00:00
}
2013-11-01 18:52:26 +00:00
}
2013-11-23 18:18:58 +00:00
public void StopMovie ( bool saveChanges = true )
2013-11-01 18:52:26 +00:00
{
2013-12-05 00:44:56 +00:00
var message = "Movie " ;
2013-11-01 18:52:26 +00:00
if ( Movie . IsRecording )
{
message + = "recording " ;
}
else if ( Movie . IsPlaying )
{
message + = "playback " ;
}
message + = "stopped." ;
if ( Movie . IsActive )
{
2013-11-23 18:18:58 +00:00
Movie . Stop ( saveChanges ) ;
if ( saveChanges )
2013-11-01 18:52:26 +00:00
{
Output ( Path . GetFileName ( Movie . Filename ) + " written to disk." ) ;
}
2013-12-05 00:44:56 +00:00
2013-11-01 18:52:26 +00:00
Output ( message ) ;
2013-12-03 18:08:45 +00:00
ReadOnly = true ;
2013-11-01 18:52:26 +00:00
}
}
2013-12-17 21:26:15 +00:00
public void HandleMovieSaveState ( TextWriter writer )
2013-11-01 18:52:26 +00:00
{
if ( Movie . IsActive )
{
2013-11-30 02:50:54 +00:00
writer . Write ( Movie . GetInputLog ( ) ) ;
2013-11-01 18:52:26 +00:00
}
}
public void ClearFrame ( )
{
if ( Movie . IsPlaying )
{
Movie . ClearFrame ( Global . Emulator . Frame ) ;
2013-12-03 18:08:45 +00:00
Output ( "Scrubbed input at frame " + Global . Emulator . Frame ) ;
2013-11-01 18:52:26 +00:00
}
}
2013-11-03 16:53:05 +00:00
public void HandleMovieOnFrameLoop ( )
2013-11-01 18:52:26 +00:00
{
if ( ! Movie . IsActive )
{
LatchInputFromPlayer ( Global . MovieInputSourceAdapter ) ;
}
else if ( Movie . IsFinished )
{
2013-12-05 00:44:56 +00:00
if ( Global . Emulator . Frame < Movie . FrameCount ) // This scenario can happen from rewinding (suddenly we are back in the movie, so hook back up to the movie
2013-11-01 18:52:26 +00:00
{
Movie . SwitchToPlay ( ) ;
LatchInputFromLog ( ) ;
}
else
{
LatchInputFromPlayer ( Global . MovieInputSourceAdapter ) ;
}
}
else if ( Movie . IsPlaying )
{
2013-11-29 19:55:05 +00:00
LatchInputFromLog ( ) ;
2013-12-05 00:44:56 +00:00
// Movie may go into finished mode as a result from latching
2013-11-29 19:55:05 +00:00
if ( ! Movie . IsFinished )
2013-11-01 18:52:26 +00:00
{
2013-11-03 16:53:05 +00:00
if ( Global . ClientControls [ "Scrub Input" ] )
2013-11-01 18:52:26 +00:00
{
LatchInputFromPlayer ( Global . MovieInputSourceAdapter ) ;
ClearFrame ( ) ;
}
2013-11-29 19:55:05 +00:00
else if ( Global . Config . MoviePlaybackPokeMode )
2013-11-01 18:52:26 +00:00
{
LatchInputFromPlayer ( Global . MovieInputSourceAdapter ) ;
var mg = new MnemonicsGenerator ( ) ;
mg . SetSource ( Global . MovieOutputHardpoint ) ;
if ( ! mg . IsEmpty )
{
LatchInputFromPlayer ( Global . MovieInputSourceAdapter ) ;
2013-12-07 16:31:04 +00:00
Movie . PokeFrame ( Global . Emulator . Frame , Global . MovieOutputHardpoint ) ;
2013-11-01 18:52:26 +00:00
}
else
{
LatchInputFromLog ( ) ;
}
}
}
}
else if ( Movie . IsRecording )
{
2013-12-05 00:44:56 +00:00
if ( _multiTrack . IsActive )
2013-11-01 18:52:26 +00:00
{
LatchMultitrackPlayerInput ( Global . MovieInputSourceAdapter , Global . MultitrackRewiringControllerAdapter ) ;
}
else
{
LatchInputFromPlayer ( Global . MovieInputSourceAdapter ) ;
}
2013-12-03 18:08:45 +00:00
// the movie session makes sure that the correct input has been read and merged to its MovieControllerAdapter;
// this has been wired to Global.MovieOutputHardpoint in RewireInputChain
2013-12-07 16:31:04 +00:00
Movie . RecordFrame ( Global . Emulator . Frame , Global . MovieOutputHardpoint ) ;
2013-11-01 18:52:26 +00:00
}
}
2013-11-01 20:53:47 +00:00
public bool HandleMovieLoadState ( string path )
{
using ( var sr = new StreamReader ( path ) )
{
2013-12-03 18:08:45 +00:00
return HandleMovieLoadState ( sr ) ;
2013-11-01 20:53:47 +00:00
}
}
2014-04-18 17:41:14 +00:00
//TODO: maybe someone who understands more about what's going on here could rename these step1 and step2 into something more descriptive
public bool HandleMovieLoadState_HackyStep2 ( TextReader reader )
{
if ( ! Movie . IsActive )
{
return true ;
}
if ( ReadOnly )
{
}
else
{
string errorMsg ;
//// fixme: this is evil (it causes crashes in binary states because InflaterInputStream can't have its position set, even to zero.
//((StreamReader)reader).BaseStream.Position = 0;
//((StreamReader)reader).DiscardBufferedData();
//edit: zero 18-apr-2014 - this was solved by HackyStep1 and HackyStep2, so that the zip stream can be re-acquired instead of needing its position reset
var result = Movie . ExtractInputLog ( reader , out errorMsg ) ;
if ( ! result )
{
Output ( errorMsg ) ;
return false ;
}
}
return true ;
}
2013-12-17 21:26:15 +00:00
public bool HandleMovieLoadState ( TextReader reader )
2014-04-18 17:41:14 +00:00
{
if ( ! HandleMovieLoadState_HackyStep1 ( reader ) )
return false ;
return HandleMovieLoadState_HackyStep2 ( reader ) ;
}
public bool HandleMovieLoadState_HackyStep1 ( TextReader reader )
2013-11-01 18:52:26 +00:00
{
if ( ! Movie . IsActive )
{
return true ;
}
2013-12-05 00:44:56 +00:00
string errorMsg ;
2013-12-04 14:42:24 +00:00
if ( ReadOnly )
2013-11-01 18:52:26 +00:00
{
2013-12-05 00:20:21 +00:00
var result = Movie . CheckTimeLines ( reader , out errorMsg ) ;
2013-12-04 14:42:24 +00:00
if ( ! result )
2013-11-01 18:52:26 +00:00
{
2013-12-05 00:20:21 +00:00
Output ( errorMsg ) ;
2013-12-04 14:42:24 +00:00
return false ;
2013-11-01 18:52:26 +00:00
}
2013-12-04 14:42:24 +00:00
if ( Movie . IsRecording )
2013-11-01 18:52:26 +00:00
{
2013-12-04 14:42:24 +00:00
Movie . SwitchToPlay ( ) ;
2013-11-01 18:52:26 +00:00
}
2013-12-04 14:42:24 +00:00
else if ( Movie . IsFinished )
2013-11-01 18:52:26 +00:00
{
2013-12-05 00:20:21 +00:00
LatchInputFromPlayer ( Global . MovieInputSourceAdapter ) ;
2013-11-01 18:52:26 +00:00
}
}
2013-12-04 14:42:24 +00:00
else
2013-11-01 18:52:26 +00:00
{
2013-12-05 00:20:21 +00:00
if ( Movie . IsFinished )
2013-11-01 18:52:26 +00:00
{
2013-12-05 00:20:21 +00:00
Movie . StartNewRecording ( ) ;
2013-12-04 14:42:24 +00:00
}
2013-12-05 00:20:21 +00:00
else if ( Movie . IsPlaying )
2013-12-04 14:42:24 +00:00
{
Movie . SwitchToRecord ( ) ;
}
2013-12-05 00:20:21 +00:00
2013-11-01 18:52:26 +00:00
}
return true ;
2013-10-25 00:59:34 +00:00
}
}
}