BizHawk/BizHawk.Client.Common/movie/MovieSession.cs

479 lines
12 KiB
C#
Raw Normal View History

using System;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
2013-10-25 00:59:34 +00:00
{
public class MovieSession
{
public MultitrackRecording MultiTrack = new MultitrackRecording();
public IMovie Movie;
2013-10-25 00:59:34 +00:00
public MovieControllerAdapter MovieControllerAdapter = new MovieControllerAdapter();
public bool EditorMode { get; set; }
public Action<string> MessageCallback; //Not Required
public Func<string, string, bool> AskYesNoCallback; //Not Required
private void Output(string message)
{
if (MessageCallback != null)
{
MessageCallback(message);
}
}
private bool AskYesNo(string title, string message)
{
if (AskYesNoCallback != null)
{
return AskYesNoCallback(title, message);
}
else
{
return true;
}
}
private bool HandleGuidError()
{
return AskYesNo(
"GUID Mismatch error",
"The savestate GUID does not match the current movie. Proceed anyway?"
);
}
2013-10-25 00:59:34 +00:00
public void LatchMultitrackPlayerInput(IController playerSource, MultitrackRewiringControllerAdapter rewiredSource)
{
if (MultiTrack.IsActive)
{
rewiredSource.PlayerSource = 1;
rewiredSource.PlayerTargetMask = 1 << (MultiTrack.CurrentPlayer);
if (MultiTrack.RecordAll) rewiredSource.PlayerTargetMask = unchecked((int)0xFFFFFFFF);
}
else rewiredSource.PlayerSource = -1;
MovieControllerAdapter.LatchPlayerFromSource(rewiredSource, MultiTrack.CurrentPlayer);
}
public void LatchInputFromPlayer(IController source)
{
MovieControllerAdapter.LatchFromSource(source);
}
/// <summary>
/// Latch input from the input log, if available
2013-10-25 00:59:34 +00:00
/// </summary>
public void LatchInputFromLog()
{
MovieControllerAdapter.SetControllersAsMnemonic(
Movie.GetInput(Global.Emulator.Frame)
);
}
public void StopMovie(bool abortchanges = false)
{
string message = "Movie ";
if (Movie.IsRecording)
{
message += "recording ";
}
else if (Movie.IsPlaying)
{
message += "playback ";
}
message += "stopped.";
if (Movie.IsActive)
{
Movie.Stop(abortchanges);
if (!abortchanges)
{
Output(Path.GetFileName(Movie.Filename) + " written to disk.");
}
Output(message);
Global.ReadOnly = true;
}
}
//State handling
public void HandleMovieSaveState(StreamWriter writer)
{
if (Movie.IsActive)
{
Movie.DumpLogIntoSavestateText(writer);
}
}
public void ClearFrame()
{
if (Movie.IsPlaying)
{
Movie.ClearFrame(Global.Emulator.Frame);
Output("Scrubbed input at frame " + Global.Emulator.Frame.ToString());
}
}
public void HandleMovieOnFrameLoop()
{
if (!Movie.IsActive)
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
}
else if (Movie.IsFinished)
{
if (Global.Emulator.Frame < Movie.Frames) //This scenario can happen from rewinding (suddenly we are back in the movie, so hook back up to the movie
{
Movie.SwitchToPlay();
LatchInputFromLog();
}
else
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
}
}
else if (Movie.IsPlaying)
{
if (Global.Emulator.Frame >= Movie.Frames)
{
if (EditorMode)
{
Movie.CaptureState();
LatchInputFromLog();
Movie.CommitFrame(Global.Emulator.Frame, Global.MovieOutputHardpoint);
}
else
{
Movie.Finish();
}
}
else
{
Movie.CaptureState();
LatchInputFromLog();
if (Global.ClientControls["Scrub Input"])
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
ClearFrame();
}
else if (EditorMode || Global.Config.MoviePlaybackPokeMode)
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
var mg = new MnemonicsGenerator();
mg.SetSource(Global.MovieOutputHardpoint);
if (!mg.IsEmpty)
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
Movie.PokeFrame(Global.Emulator.Frame, mg.GetControllersAsMnemonic());
}
else
{
LatchInputFromLog();
}
}
}
}
else if (Movie.IsRecording)
{
Movie.CaptureState();
if (MultiTrack.IsActive)
{
LatchMultitrackPlayerInput(Global.MovieInputSourceAdapter, Global.MultitrackRewiringControllerAdapter);
}
else
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
}
//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
Movie.CommitFrame(Global.Emulator.Frame, Global.MovieOutputHardpoint);
}
}
public bool HandleMovieLoadState(string path)
{
using (var sr = new StreamReader(path))
{
return Global.MovieSession.HandleMovieLoadState(sr);
}
}
//OMG this needs to be refactored!
public bool HandleMovieLoadState(StreamReader reader)
{
string ErrorMSG = String.Empty;
if (!Movie.IsActive)
{
return true;
}
else if (Movie.IsRecording)
{
if (Global.ReadOnly)
{
var result = Movie.CheckTimeLines(reader, onlyGuid: false, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
if (result == LoadStateResult.Pass)
{
Movie.WriteMovie();
Movie.SwitchToPlay();
return true;
}
else
{
if (result == LoadStateResult.GuidMismatch)
{
if (HandleGuidError())
{
var newresult = Movie.CheckTimeLines(reader, onlyGuid: false, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
if (newresult == LoadStateResult.Pass)
{
Movie.WriteMovie();
Movie.SwitchToPlay();
return true;
}
else
{
Output(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
Output(ErrorMSG);
return false;
}
}
}
else
{
var result = Movie.CheckTimeLines(reader, onlyGuid: true, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
if (result == LoadStateResult.Pass)
{
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
}
else
{
if (result == LoadStateResult.GuidMismatch)
{
if (HandleGuidError())
{
var newresult = Movie.CheckTimeLines(reader, onlyGuid: false, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
if (newresult == LoadStateResult.Pass)
{
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
return true;
}
else
{
Output(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
Output(ErrorMSG);
return false;
}
}
}
}
else if (Movie.IsPlaying && !Movie.IsFinished)
{
if (Global.ReadOnly)
{
var result = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
if (result == LoadStateResult.Pass)
{
//Frame loop automatically handles the rewinding effect based on Global.Emulator.Frame so nothing else is needed here
return true;
}
else
{
if (result == LoadStateResult.GuidMismatch)
{
if (HandleGuidError())
{
var newresult = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
if (newresult == LoadStateResult.Pass)
{
return true;
}
else
{
Output(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
Output(ErrorMSG);
return false;
}
}
}
else
{
var result = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
if (result == LoadStateResult.Pass)
{
Movie.SwitchToRecord();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
return true;
}
else
{
if (result == LoadStateResult.GuidMismatch)
{
if (HandleGuidError())
{
var newresult = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
if (newresult == LoadStateResult.Pass)
{
Movie.SwitchToRecord();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
return true;
}
else
{
Output(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
Output(ErrorMSG);
return false;
}
}
}
}
else if (Movie.IsFinished)
{
if (Global.ReadOnly)
{
2013-11-23 00:13:36 +00:00
var result = Movie.CheckTimeLines(reader, onlyGuid: false, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
if (result != LoadStateResult.Pass)
{
if (result == LoadStateResult.GuidMismatch)
{
if (HandleGuidError())
{
2013-11-23 00:13:36 +00:00
var newresult = Movie.CheckTimeLines(reader, onlyGuid: true, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
if (newresult == LoadStateResult.Pass)
{
Movie.SwitchToPlay();
return true;
}
else
{
Output(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
Output(ErrorMSG);
return false;
}
}
else if (Movie.IsFinished) //TimeLine check can change a movie to finished, hence the check here (not a good design)
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
}
else
{
Movie.SwitchToPlay();
}
}
else
{
var result = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
if (result == LoadStateResult.Pass)
{
Global.Emulator.ClearSaveRam();
Movie.StartRecording();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
return true;
}
else
{
if (result == LoadStateResult.GuidMismatch)
{
if (HandleGuidError())
{
var newresult = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
if (newresult == LoadStateResult.Pass)
{
Global.Emulator.ClearSaveRam();
Movie.StartRecording();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
return true;
}
else
{
Output(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
Output(ErrorMSG);
return false;
}
}
}
}
return true;
2013-10-25 00:59:34 +00:00
}
}
}