Refactor movie loadstate code to get the winform specific code out of Movie.cs and movie it to Mainform, this involves passing back error information rather than calling messageboxes itself. Movie.cs loadstate is slightly less crappy now, and the mainform HandleMovieLoadstate is unbelievably crappy, must refactor at some point. I tested and don't think I broke anything, but given the complexity of the logic and number of scenario, I wouldn't be surprised.

This commit is contained in:
adelikat 2013-10-23 23:45:15 +00:00
parent c1533d3259
commit de19802d25
2 changed files with 294 additions and 96 deletions

View File

@ -152,6 +152,14 @@ namespace BizHawk.MultiClient
} }
} }
private void ShowError(string error)
{
if (!String.IsNullOrWhiteSpace(error))
{
MessageBox.Show(error, "Loadstate Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private bool HandleMovieLoadState(string path) private bool HandleMovieLoadState(string path)
{ {
using (var sr = new StreamReader(path)) using (var sr = new StreamReader(path))
@ -160,8 +168,10 @@ namespace BizHawk.MultiClient
} }
} }
//OMG this needs to be refactored!
private bool HandleMovieLoadState(StreamReader reader) private bool HandleMovieLoadState(StreamReader reader)
{ {
string ErrorMSG = String.Empty;
//Note, some of the situations in these IF's may be identical and could be combined but I intentionally separated it out for clarity //Note, some of the situations in these IF's may be identical and could be combined but I intentionally separated it out for clarity
if (!GlobalWinF.MovieSession.Movie.IsActive) if (!GlobalWinF.MovieSession.Movie.IsActive)
{ {
@ -170,29 +180,94 @@ namespace BizHawk.MultiClient
else if (GlobalWinF.MovieSession.Movie.IsRecording) else if (GlobalWinF.MovieSession.Movie.IsRecording)
{ {
if (ReadOnly) if (ReadOnly)
{ {
if (!GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, false)) var result = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: false, IgnoreGuidMismatch: false, ErrorMessage: out ErrorMSG);
{ if (result == Movie.LoadStateResult.Pass)
return false; //Timeline/GUID error
}
else
{ {
GlobalWinF.MovieSession.Movie.WriteMovie(); GlobalWinF.MovieSession.Movie.WriteMovie();
GlobalWinF.MovieSession.Movie.SwitchToPlay(); GlobalWinF.MovieSession.Movie.SwitchToPlay();
SetMainformMovieInfo(); SetMainformMovieInfo();
return true;
}
else
{
if (result == Movie.LoadStateResult.GuidMismatch)
{
var dresult = MessageBox.Show("The savestate GUID does not match the current movie. Proceed anyway?",
"GUID Mismatch error",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dresult == DialogResult.Yes)
{
var newresult = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: false, IgnoreGuidMismatch: true, ErrorMessage: out ErrorMSG);
if (newresult == Movie.LoadStateResult.Pass)
{
GlobalWinF.MovieSession.Movie.WriteMovie();
GlobalWinF.MovieSession.Movie.SwitchToPlay();
SetMainformMovieInfo();
return true;
}
else
{
ShowError(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
ShowError(ErrorMSG);
return false;
}
} }
} }
else else
{ {
if (!GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, true)) var result = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: true, IgnoreGuidMismatch: false, ErrorMessage: out ErrorMSG);
if (result == Movie.LoadStateResult.Pass)
{ {
return false; //GUID Error reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
}
else
{
if (result == Movie.LoadStateResult.GuidMismatch)
{
var dresult = MessageBox.Show("The savestate GUID does not match the current movie. Proceed anyway?",
"GUID Mismatch error",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dresult == DialogResult.Yes)
{
var newresult = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: false, IgnoreGuidMismatch: true, ErrorMessage: out ErrorMSG);
if (newresult == Movie.LoadStateResult.Pass)
{
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
return true;
}
else
{
ShowError(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
ShowError(ErrorMSG);
return false;
}
} }
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
} }
} }
@ -200,63 +275,193 @@ namespace BizHawk.MultiClient
{ {
if (ReadOnly) if (ReadOnly)
{ {
if (!GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, false)) var result = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: !ReadOnly, IgnoreGuidMismatch: false, ErrorMessage: out ErrorMSG);
if (result == Movie.LoadStateResult.Pass)
{ {
return false; //Timeline/GUID error //Frame loop automatically handles the rewinding effect based on Global.Emulator.Frame so nothing else is needed here
return true;
}
else
{
if (result == Movie.LoadStateResult.GuidMismatch)
{
var dresult = MessageBox.Show("The savestate GUID does not match the current movie. Proceed anyway?",
"GUID Mismatch error",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dresult == DialogResult.Yes)
{
var newresult = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: !ReadOnly, IgnoreGuidMismatch: true, ErrorMessage: out ErrorMSG);
if (newresult == Movie.LoadStateResult.Pass)
{
return true;
}
else
{
ShowError(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
ShowError(ErrorMSG);
return false;
}
} }
//Frame loop automatically handles the rewinding effect based on Global.Emulator.Frame so nothing else is needed here
} }
else else
{ {
if (!GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, true)) var result = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: !ReadOnly, IgnoreGuidMismatch: false, ErrorMessage: out ErrorMSG);
if (result == Movie.LoadStateResult.Pass)
{ {
return false; //GUID Error GlobalWinF.MovieSession.Movie.SwitchToRecord();
SetMainformMovieInfo();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
return true;
}
else
{
if (result == Movie.LoadStateResult.GuidMismatch)
{
var dresult = MessageBox.Show("The savestate GUID does not match the current movie. Proceed anyway?",
"GUID Mismatch error",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dresult == DialogResult.Yes)
{
var newresult = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: !ReadOnly, IgnoreGuidMismatch: true, ErrorMessage: out ErrorMSG);
if (newresult == Movie.LoadStateResult.Pass)
{
GlobalWinF.MovieSession.Movie.SwitchToRecord();
SetMainformMovieInfo();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
return true;
}
else
{
ShowError(ErrorMSG);
return false;
}
}
else
{
return false;
}
}
else
{
ShowError(ErrorMSG);
return false;
}
} }
GlobalWinF.MovieSession.Movie.SwitchToRecord();
SetMainformMovieInfo();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
} }
} }
else if (GlobalWinF.MovieSession.Movie.IsFinished) else if (GlobalWinF.MovieSession.Movie.IsFinished)
{ {
if (ReadOnly) if (ReadOnly)
{ {
var result = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: !ReadOnly, IgnoreGuidMismatch: false, ErrorMessage: out ErrorMSG);
if (result != Movie.LoadStateResult.Pass)
{ {
if (!GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, false)) if (result == Movie.LoadStateResult.GuidMismatch)
{ {
return false; //Timeline/GUID error var dresult = MessageBox.Show("The savestate GUID does not match the current movie. Proceed anyway?",
} "GUID Mismatch error",
else if (GlobalWinF.MovieSession.Movie.IsFinished) //TimeLine check can change a movie to finished, hence the check here (not a good design) MessageBoxButtons.YesNo, MessageBoxIcon.Question);
{ if (dresult == DialogResult.Yes)
GlobalWinF.MovieSession.LatchInputFromPlayer(GlobalWinF.MovieInputSourceAdapter); {
var newresult = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: !ReadOnly, IgnoreGuidMismatch: true, ErrorMessage: out ErrorMSG);
if (newresult == Movie.LoadStateResult.Pass)
{
GlobalWinF.MovieSession.Movie.SwitchToPlay();
SetMainformMovieInfo();
return true;
}
else
{
ShowError(ErrorMSG);
return false;
}
}
else
{
return false;
}
} }
else else
{ {
GlobalWinF.MovieSession.Movie.SwitchToPlay(); ShowError(ErrorMSG);
SetMainformMovieInfo(); return false;
} }
} }
else if (GlobalWinF.MovieSession.Movie.IsFinished) //TimeLine check can change a movie to finished, hence the check here (not a good design)
{
GlobalWinF.MovieSession.LatchInputFromPlayer(GlobalWinF.MovieInputSourceAdapter);
}
else
{
GlobalWinF.MovieSession.Movie.SwitchToPlay();
SetMainformMovieInfo();
}
} }
else else
{ {
var result = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: !ReadOnly, IgnoreGuidMismatch: false, ErrorMessage: out ErrorMSG);
if (result == Movie.LoadStateResult.Pass)
{ {
if (!GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, true)) GlobalWinF.MovieSession.Movie.StartRecording();
SetMainformMovieInfo();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
return true;
}
else
{
if (result == Movie.LoadStateResult.GuidMismatch)
{ {
return false; //GUID Error var dresult = MessageBox.Show("The savestate GUID does not match the current movie. Proceed anyway?",
"GUID Mismatch error",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dresult == DialogResult.Yes)
{
var newresult = GlobalWinF.MovieSession.Movie.CheckTimeLines(reader, OnlyGUID: !ReadOnly, IgnoreGuidMismatch: true, ErrorMessage: out ErrorMSG);
if (newresult == Movie.LoadStateResult.Pass)
{
GlobalWinF.MovieSession.Movie.StartRecording();
SetMainformMovieInfo();
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
return true;
}
else
{
ShowError(ErrorMSG);
return false;
}
}
else
{
return false;
}
} }
else else
{ {
GlobalWinF.MovieSession.Movie.StartRecording(); ShowError(ErrorMSG);
SetMainformMovieInfo(); return false;
reader.BaseStream.Position = 0;
reader.DiscardBufferedData();
GlobalWinF.MovieSession.Movie.LoadLogFromSavestateText(reader);
} }
} }
} }
} }
return true; return true;
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using System.Windows.Forms;
using System.Globalization; using System.Globalization;
using BizHawk.Client.Common; using BizHawk.Client.Common;
@ -796,18 +795,20 @@ namespace BizHawk.MultiClient
return time; return time;
} }
public bool CheckTimeLines(TextReader reader, bool OnlyGUID) public enum LoadStateResult { Pass, GuidMismatch, TimeLineError, FutureEventError, NotInRecording, EmptyLog, MissingFrameNumber }
public LoadStateResult CheckTimeLines(TextReader reader, bool OnlyGUID, bool IgnoreGuidMismatch, out string ErrorMessage)
{ {
//This function will compare the movie data to the savestate movie data to see if they match //This function will compare the movie data to the savestate movie data to see if they match
ErrorMessage = String.Empty;
MovieLog l = new MovieLog(); var log = new MovieLog();
int stateFrame = 0; int stateFrame = 0;
while (true) while (true)
{ {
string line = reader.ReadLine(); string line = reader.ReadLine();
if (line == null) if (line == null)
{ {
return false; return LoadStateResult.EmptyLog;
} }
else if (line.Trim() == "") else if (line.Trim() == "")
{ {
@ -818,22 +819,11 @@ namespace BizHawk.MultiClient
string guid = ParseHeader(line, MovieHeader.GUID); string guid = ParseHeader(line, MovieHeader.GUID);
if (Header.GetHeaderLine(MovieHeader.GUID) != guid) if (Header.GetHeaderLine(MovieHeader.GUID) != guid)
{ {
//GUID Mismatch error if (!IgnoreGuidMismatch)
var result = MessageBox.Show(guid + " : " + Header.GetHeaderLine(MovieHeader.GUID) + "\n" +
"The savestate GUID does not match the current movie. Proceed anyway?", "GUID Mismatch error",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.No)
{ {
//reader.Close(); return LoadStateResult.GuidMismatch;
return false;
} }
} }
else if (OnlyGUID)
{
//reader.Close();
return true;
}
} }
else if (line.Contains("Frame 0x")) //NES stores frame count in hex, yay else if (line.Contains("Frame 0x")) //NES stores frame count in hex, yay
{ {
@ -842,7 +832,11 @@ namespace BizHawk.MultiClient
{ {
stateFrame = int.Parse(strs[1], NumberStyles.HexNumber); stateFrame = int.Parse(strs[1], NumberStyles.HexNumber);
} }
catch { GlobalWinF.OSD.AddMessage("Savestate Frame number failed to parse"); } catch
{
ErrorMessage = "Savestate Frame number failed to parse";
return LoadStateResult.MissingFrameNumber;
}
} }
else if (line.Contains("Frame ")) else if (line.Contains("Frame "))
{ {
@ -851,69 +845,68 @@ namespace BizHawk.MultiClient
{ {
stateFrame = int.Parse(strs[1]); stateFrame = int.Parse(strs[1]);
} }
catch { GlobalWinF.OSD.AddMessage("Savestate Frame number failed to parse"); } catch
{
ErrorMessage = "Savestate Frame number failed to parse";
return LoadStateResult.MissingFrameNumber;
}
} }
else if (line == "[Input]") continue; else if (line == "[Input]") continue;
else if (line == "[/Input]") break; else if (line == "[/Input]") break;
else if (line[0] == '|') else if (line[0] == '|')
l.AppendFrame(line);
}
//reader.BaseStream.Position = 0; //Reset position because this stream may be read again by other code
if (OnlyGUID)
{
//reader.Close();
return true;
}
if (stateFrame == 0)
{
stateFrame = l.Length; //In case the frame count failed to parse, revert to using the entire state input log
}
if (Log.Length < stateFrame)
{
//Future event error
MessageBox.Show("The savestate is from frame " + l.Length.ToString() + " which is greater than the current movie length of " +
Log.Length.ToString() + ".\nCan not load this savestate.", "Future event Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
//reader.Close();
return false;
}
for (int x = 0; x < stateFrame; x++)
{
string xs = Log.GetFrame(x);
string ys = l.GetFrame(x);
if (xs != ys)
{ {
//TimeLine Error log.AppendFrame(line);
MessageBox.Show("The savestate input does not match the movie input at frame " + (x + 1).ToString() + ".",
"Timeline Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
//reader.Close();
return false;
} }
} }
if (OnlyGUID)
{
return LoadStateResult.Pass;
}
if (stateFrame == 0)
{
stateFrame = log.Length; //In case the frame count failed to parse, revert to using the entire state input log
}
if (Log.Length < stateFrame)
{
ErrorMessage = "The savestate is from frame "
+ log.Length.ToString()
+ " which is greater than the current movie length of "
+ Log.Length.ToString();
return LoadStateResult.FutureEventError;
}
for (int i = 0; i < stateFrame; i++)
{
string xs = Log.GetFrame(i);
string ys = log.GetFrame(i); //TODO: huh??
if (xs != ys)
{
ErrorMessage = "The savestate input does not match the movie input at frame "
+ (i + 1).ToString()
+ ".";
return LoadStateResult.TimeLineError;
}
}
if (stateFrame > l.Length) //stateFrame is greater than state input log, so movie finished mode if (stateFrame > log.Length) //stateFrame is greater than state input log, so movie finished mode
{ {
if (Mode == MOVIEMODE.PLAY || Mode == MOVIEMODE.FINISHED) if (Mode == MOVIEMODE.PLAY || Mode == MOVIEMODE.FINISHED)
{ {
Mode = MOVIEMODE.FINISHED; Mode = MOVIEMODE.FINISHED;
return true; return LoadStateResult.Pass;
} }
else else
return false; //For now throw an error if recording, ideally what should happen is that the state gets loaded, and the movie set to movie finished, the movie at its current state is preserved and the state is loaded just fine. This should probably also only happen if checktimelines passes {
return LoadStateResult.NotInRecording; //TODO: For now throw an error if recording, ideally what should happen is that the state gets loaded, and the movie set to movie finished, the movie at its current state is preserved and the state is loaded just fine. This should probably also only happen if checktimelines passes
}
} }
else if (Mode == MOVIEMODE.FINISHED) else if (Mode == MOVIEMODE.FINISHED)
{ {
Mode = MOVIEMODE.PLAY; Mode = MOVIEMODE.PLAY;
} }
//reader.Close(); return LoadStateResult.Pass;
return true;
} }
#endregion #endregion