2013-11-01 18:52:26 +00:00
|
|
|
|
using System;
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
public MultitrackRecording MultiTrack = new MultitrackRecording();
|
2013-11-23 17:26:33 +00:00
|
|
|
|
public IMovie Movie;
|
2013-10-25 00:59:34 +00:00
|
|
|
|
public MovieControllerAdapter MovieControllerAdapter = new MovieControllerAdapter();
|
2013-11-01 18:52:26 +00:00
|
|
|
|
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>
|
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)
|
|
|
|
|
{
|
|
|
|
|
MovieControllerAdapter.SetControllersAsMnemonic(
|
|
|
|
|
Movie.GetInput(Global.Emulator.Frame)
|
|
|
|
|
);
|
|
|
|
|
}
|
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
|
|
|
|
{
|
|
|
|
|
string message = "Movie ";
|
|
|
|
|
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.");
|
|
|
|
|
}
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2013-11-29 19:55:05 +00:00
|
|
|
|
|
|
|
|
|
LatchInputFromLog();
|
|
|
|
|
|
|
|
|
|
//Movie may go into finished mode as a result from latching
|
|
|
|
|
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);
|
|
|
|
|
Movie.PokeFrame(Global.Emulator.Frame, mg.GetControllersAsMnemonic());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LatchInputFromLog();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (Movie.IsRecording)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-01 20:53:47 +00:00
|
|
|
|
public bool HandleMovieLoadState(string path)
|
|
|
|
|
{
|
|
|
|
|
using (var sr = new StreamReader(path))
|
|
|
|
|
{
|
|
|
|
|
return Global.MovieSession.HandleMovieLoadState(sr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-01 18:52:26 +00:00
|
|
|
|
//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)
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var result = Movie.CheckTimeLines(reader, onlyGuid: false, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
2013-11-23 18:18:58 +00:00
|
|
|
|
Movie.Save();
|
2013-11-01 18:52:26 +00:00
|
|
|
|
Movie.SwitchToPlay();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.GuidMismatch)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (HandleGuidError())
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var newresult = Movie.CheckTimeLines(reader, onlyGuid: false, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (newresult == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
2013-11-23 18:18:58 +00:00
|
|
|
|
Movie.Save();
|
2013-11-01 18:52:26 +00:00
|
|
|
|
Movie.SwitchToPlay();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Output(ErrorMSG);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Output(ErrorMSG);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var result = Movie.CheckTimeLines(reader, onlyGuid: true, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
reader.BaseStream.Position = 0;
|
|
|
|
|
reader.DiscardBufferedData();
|
|
|
|
|
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.GuidMismatch)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (HandleGuidError())
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var newresult = Movie.CheckTimeLines(reader, onlyGuid: false, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (newresult == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var result = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
//Frame loop automatically handles the rewinding effect based on Global.Emulator.Frame so nothing else is needed here
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.GuidMismatch)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (HandleGuidError())
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var newresult = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (newresult == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Output(ErrorMSG);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Output(ErrorMSG);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var result = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
Movie.SwitchToRecord();
|
|
|
|
|
reader.BaseStream.Position = 0;
|
|
|
|
|
reader.DiscardBufferedData();
|
|
|
|
|
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.GuidMismatch)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (HandleGuidError())
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var newresult = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (newresult == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result != LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.GuidMismatch)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (HandleGuidError())
|
|
|
|
|
{
|
2013-11-23 00:13:36 +00:00
|
|
|
|
var newresult = Movie.CheckTimeLines(reader, onlyGuid: true, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (newresult == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var result = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: false, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
2013-11-03 02:51:21 +00:00
|
|
|
|
Global.Emulator.ClearSaveRam();
|
2013-11-29 19:55:05 +00:00
|
|
|
|
Movie.StartNewRecording();
|
2013-11-01 18:52:26 +00:00
|
|
|
|
reader.BaseStream.Position = 0;
|
|
|
|
|
reader.DiscardBufferedData();
|
|
|
|
|
Movie.LoadLogFromSavestateText(reader, MultiTrack.IsActive);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (result == LoadStateResult.GuidMismatch)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (HandleGuidError())
|
|
|
|
|
{
|
2013-11-16 21:05:59 +00:00
|
|
|
|
var newresult = Movie.CheckTimeLines(reader, onlyGuid: !Global.ReadOnly, ignoreGuidMismatch: true, errorMessage: out ErrorMSG);
|
2013-11-23 17:26:33 +00:00
|
|
|
|
if (newresult == LoadStateResult.Pass)
|
2013-11-01 18:52:26 +00:00
|
|
|
|
{
|
2013-11-03 02:51:21 +00:00
|
|
|
|
Global.Emulator.ClearSaveRam();
|
2013-11-29 19:55:05 +00:00
|
|
|
|
Movie.StartNewRecording();
|
2013-11-01 18:52:26 +00:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|