diff --git a/BizHawk.Client.Common/BizHawk.Client.Common.csproj b/BizHawk.Client.Common/BizHawk.Client.Common.csproj index f8eb645303..64ed20c78d 100644 --- a/BizHawk.Client.Common/BizHawk.Client.Common.csproj +++ b/BizHawk.Client.Common/BizHawk.Client.Common.csproj @@ -136,6 +136,10 @@ + + + + diff --git a/BizHawk.Client.Common/movie/bkm/Movie.HeaderApi.cs b/BizHawk.Client.Common/movie/bkm/Movie.HeaderApi.cs new file mode 100644 index 0000000000..718e725d44 --- /dev/null +++ b/BizHawk.Client.Common/movie/bkm/Movie.HeaderApi.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; + +namespace BizHawk.Client.Common +{ + public partial class Movie : IMovie + { + public IDictionary HeaderEntries + { + get + { + return Header; + } + } + + public SubtitleList Subtitles + { + get { return Header.Subtitles; } + } + + public IList Comments + { + get { return Header.Comments; } + } + + public string SyncSettingsJson + { + get { return Header[HeaderKeys.SYNCSETTINGS]; } + set { Header[HeaderKeys.SYNCSETTINGS] = value; } + } + + public string SavestateBinaryBase64Blob + { + get { return Header.SavestateBinaryBase64Blob; } + set { Header.SavestateBinaryBase64Blob = value; } + } + + public ulong Rerecords + { + get { return Header.Rerecords; } + set { Header.Rerecords = value; } + } + + public bool StartsFromSavestate + { + get { return Header.StartsFromSavestate; } + set { Header.StartsFromSavestate = value; } + } + + public string GameName + { + get { return Header.GameName; } + set { Header.GameName = value; } + } + + public string SystemID + { + get { return Header.SystemID; } + set { Header.SystemID = value; } + } + + public string Hash + { + get { return Header[HeaderKeys.SHA1]; } + set { Header[HeaderKeys.SHA1] = value; } + } + + public string Author + { + get { return Header[HeaderKeys.AUTHOR]; } + set { Header[HeaderKeys.AUTHOR] = value; } + } + + public string Core + { + get { return Header[HeaderKeys.CORE]; } + set { Header[HeaderKeys.CORE] = value; } + } + + public string Platform + { + get { return Header[HeaderKeys.PLATFORM]; } + set { Header[HeaderKeys.PLATFORM] = value; } + } + + public string BoardName + { + get { return Header[HeaderKeys.BOARDNAME]; } + set { Header[HeaderKeys.BOARDNAME] = value; } + } + + public string EmulatorVersion + { + get { return Header[HeaderKeys.EMULATIONVERSION]; } + + set { Header[HeaderKeys.EMULATIONVERSION] = value; } + } + + public string FirmwareHash + { + get { return Header[HeaderKeys.FIRMWARESHA1]; } + set { Header[HeaderKeys.FIRMWARESHA1] = value; } + } + } +} diff --git a/BizHawk.Client.Common/movie/bkm/Movie.IO.cs b/BizHawk.Client.Common/movie/bkm/Movie.IO.cs new file mode 100644 index 0000000000..98de5d620c --- /dev/null +++ b/BizHawk.Client.Common/movie/bkm/Movie.IO.cs @@ -0,0 +1,252 @@ +using System; +using System.IO; +using System.Text; + +using BizHawk.Common; + +namespace BizHawk.Client.Common +{ + public partial class Movie : IMovie + { + private int _preloadFramecount; // Not a a reliable number, used for preloading (when no log has yet been loaded), this is only for quick stat compilation for dialogs such as play movie + + public void SaveAs(string path) + { + Filename = path; + if (!Loaded) + { + return; + } + + var directory_info = new FileInfo(Filename).Directory; + if (directory_info != null) + { + Directory.CreateDirectory(directory_info.FullName); + } + + Write(Filename); + } + + public void Save() + { + if (!Loaded || string.IsNullOrWhiteSpace(Filename)) + { + return; + } + + SaveAs(Filename); + _changes = false; + } + + public void SaveBackup() + { + if (!Loaded || string.IsNullOrWhiteSpace(Filename)) + { + return; + } + + var backupName = Filename; + backupName = backupName.Insert(Filename.LastIndexOf("."), string.Format(".{0:yyyy-MM-dd HH.mm.ss}", DateTime.Now)); + backupName = Path.Combine(Global.Config.PathEntries["Global", "Movie backups"].Path, Path.GetFileName(backupName)); + + var directory_info = new FileInfo(backupName).Directory; + if (directory_info != null) + { + Directory.CreateDirectory(directory_info.FullName); + } + + Write(backupName); + } + + public bool Load() + { + var file = new FileInfo(Filename); + + if (file.Exists == false) + { + Loaded = false; + return false; + } + + Header.Clear(); + _log.Clear(); + + using (var sr = file.OpenText()) + { + string line; + + while ((line = sr.ReadLine()) != null) + { + if (line == string.Empty) + { + continue; + } + + if (line.Contains("LoopOffset")) + { + try + { + _loopOffset = int.Parse(line.Split(new[] { ' ' }, 2)[1]); + } + catch (Exception) + { + continue; + } + } + else if (Header.ParseLineFromFile(line)) + { + continue; + } + else if (line.StartsWith("|")) + { + _log.AppendFrame(line); + } + else + { + Header.Comments.Add(line); + } + } + } + + Loaded = true; + return true; + } + + /// + /// Load Header information only for displaying file information in dialogs such as play movie + /// TODO - consider not loading the SavestateBinaryBase64Blob key? + /// + public bool PreLoadText(HawkFile hawkFile) + { + Loaded = false; + var file = new FileInfo(hawkFile.CanonicalFullPath); + + if (file.Exists == false) + { + return false; + } + + Header.Clear(); + _log.Clear(); + + var origStreamPosn = hawkFile.GetStream().Position; + hawkFile.GetStream().Position = 0; // Reset to start + + // No using block because we're sharing the stream and need to give it back undisposed. + var sr = new StreamReader(hawkFile.GetStream()); + + for (; ; ) + { + //read to first space (key/value delimeter), or pipe, or EOF + int first = sr.Read(); + + if (first == -1) break; //EOF + else if (first == '|') //pipe: begin input log + { + //NOTE - this code is a bit convoluted due to its predating the basic outline of the parser which was upgraded in may 2014 + string line = '|' + sr.ReadLine(); + + //how many bytes are left, total? + long remain = sr.BaseStream.Length - sr.BaseStream.Position; + + //try to find out whether we use \r\n or \n + //but only look for 1K characters. + bool usesR = false; + for (int i = 0; i < 1024; i++) + { + int c = sr.Read(); + if (c == -1) + break; + if (c == '\r') + { + usesR = true; + break; + } + if (c == '\n') + break; + } + + int lineLen = line.Length + 1; //account for \n + if (usesR) lineLen++; //account for \r + + _preloadFramecount = (int)(remain / lineLen); //length is remaining bytes / length per line + _preloadFramecount++; //account for the current line + break; + } + else + { + //a header line. finish reading key token, to make sure it isn't one of the FORBIDDEN keys + StringBuilder sbLine = new StringBuilder(); + sbLine.Append((char)first); + for (; ; ) + { + int c = sr.Read(); + if (c == -1) break; + if (c == '\n') break; + if (c == ' ') break; + sbLine.Append((char)c); + } + + var line = sbLine.ToString(); + + //ignore these suckers, theyre way too big for preloading. seriously, we will get out of memory errors. + var skip = line == HeaderKeys.SAVESTATEBINARYBASE64BLOB; + + if (skip) + { + //skip remainder of the line + sr.DiscardBufferedData(); + var stream = sr.BaseStream; + for (; ; ) + { + int c = stream.ReadByte(); + if (c == -1) break; + if (c == '\n') break; + } + //proceed to next line + continue; + } + + + string remainder = sr.ReadLine(); + sbLine.Append(' '); + sbLine.Append(remainder); + line = sbLine.ToString(); + + if (string.IsNullOrWhiteSpace(line) || Header.ParseLineFromFile(line)) + { + continue; + } + + Header.Comments.Add(line); + } + } + + hawkFile.GetStream().Position = origStreamPosn; + + return true; + } + + private void Write(string fn) + { + using (var fs = new FileStream(fn, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + using (var sw = new StreamWriter(fs)) + { + sw.Write(Header.ToString()); + + // TODO: clean this up + if (_loopOffset.HasValue) + { + sw.WriteLine("LoopOffset " + _loopOffset); + } + + foreach (var input in _log) + { + sw.WriteLine(input); + } + } + } + } + } +} diff --git a/BizHawk.Client.Common/movie/bkm/Movie.InputLog.cs b/BizHawk.Client.Common/movie/bkm/Movie.InputLog.cs new file mode 100644 index 0000000000..c31e11fda0 --- /dev/null +++ b/BizHawk.Client.Common/movie/bkm/Movie.InputLog.cs @@ -0,0 +1,291 @@ +using System.Globalization; +using System.IO; +using System.Text; + +namespace BizHawk.Client.Common +{ + public partial class Movie : IMovie + { + private readonly MovieLog _log = new MovieLog(); + + public string GetInputLog() + { + var sb = new StringBuilder(); + + sb.AppendLine("[Input]"); + + foreach (var record in _log) + { + sb.AppendLine(record); + } + + sb.AppendLine("[/Input]"); + + return sb.ToString(); + } + + public bool ExtractInputLog(TextReader reader, out string errorMessage) + { + errorMessage = string.Empty; + int? stateFrame = null; + + // We are in record mode so replace the movie log with the one from the savestate + if (!Global.MovieSession.MultiTrack.IsActive) + { + if (Global.Config.EnableBackupMovies && _makeBackup && _log.Length > 0) + { + SaveBackup(); + _makeBackup = false; + } + + _log.Clear(); + while (true) + { + var line = reader.ReadLine(); + if (line == null) + { + break; + } + + if (line.Trim() == string.Empty || line == "[Input]") + { + continue; + } + + if (line == "[/Input]") + { + break; + } + + if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay + { + var strs = line.Split('x'); + try + { + stateFrame = int.Parse(strs[1], NumberStyles.HexNumber); + } + catch + { + errorMessage = "Savestate Frame number failed to parse"; + return false; + } + } + else if (line.Contains("Frame ")) + { + var strs = line.Split(' '); + try + { + stateFrame = int.Parse(strs[1]); + } + catch + { + errorMessage = "Savestate Frame number failed to parse"; + return false; + } + } + else if (line[0] == '|') + { + _log.AppendFrame(line); + } + } + } + else + { + var i = 0; + while (true) + { + var line = reader.ReadLine(); + if (line == null) + { + break; + } + + if (line.Trim() == string.Empty || line == "[Input]") + { + continue; + } + + if (line == "[/Input]") + { + break; + } + + if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay + { + var strs = line.Split('x'); + try + { + stateFrame = int.Parse(strs[1], NumberStyles.HexNumber); + } + catch + { + errorMessage = "Savestate Frame number failed to parse"; + return false; + } + } + else if (line.Contains("Frame ")) + { + var strs = line.Split(' '); + try + { + stateFrame = int.Parse(strs[1]); + } + catch + { + errorMessage = "Savestate Frame number failed to parse"; + return false; + } + } + else if (line.StartsWith("|")) + { + _log.SetFrameAt(i, line); + i++; + } + } + } + + if (!stateFrame.HasValue) + { + errorMessage = "Savestate Frame number failed to parse"; + } + + var stateFramei = stateFrame ?? 0; + + if (stateFramei > 0 && stateFramei < _log.Length) + { + if (!Global.Config.VBAStyleMovieLoadState) + { + _log.TruncateStates(stateFramei); + _log.TruncateMovie(stateFramei); + } + } + else if (stateFramei > _log.Length) // Post movie savestate + { + if (!Global.Config.VBAStyleMovieLoadState) + { + _log.TruncateStates(_log.Length); + _log.TruncateMovie(_log.Length); + } + + _mode = Moviemode.Finished; + } + + if (IsCountingRerecords) + { + Rerecords++; + } + + return true; + } + + public bool CheckTimeLines(TextReader reader, out string errorMessage) + { + // This function will compare the movie data to the savestate movie data to see if they match + errorMessage = string.Empty; + var log = new MovieLog(); + var stateFrame = 0; + while (true) + { + var line = reader.ReadLine(); + if (line == null) + { + return false; + } + + if (line.Trim() == string.Empty) + { + continue; + } + + if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay + { + var strs = line.Split('x'); + try + { + stateFrame = int.Parse(strs[1], NumberStyles.HexNumber); + } + catch + { + errorMessage = "Savestate Frame number failed to parse"; + return false; + } + } + else if (line.Contains("Frame ")) + { + var strs = line.Split(' '); + try + { + stateFrame = int.Parse(strs[1]); + } + catch + { + errorMessage = "Savestate Frame number failed to parse"; + return false; + } + } + else if (line == "[Input]") + { + continue; + } + else if (line == "[/Input]") + { + break; + } + else if (line[0] == '|') + { + log.AppendFrame(line); + } + } + + 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) + { + if (IsFinished) + { + return true; + } + + errorMessage = "The savestate is from frame " + + log.Length + + " which is greater than the current movie length of " + + _log.Length; + + return false; + } + + for (var i = 0; i < stateFrame; i++) + { + if (_log[i] != log[i]) + { + errorMessage = "The savestate input does not match the movie input at frame " + + (i + 1) + + "."; + + return false; + } + } + + if (stateFrame > log.Length) // stateFrame is greater than state input log, so movie finished mode + { + if (_mode == Moviemode.Play || _mode == Moviemode.Finished) + { + _mode = Moviemode.Finished; + return true; + } + + return false; + } + + if (_mode == Moviemode.Finished) + { + _mode = Moviemode.Play; + } + + return true; + } + } +} diff --git a/BizHawk.Client.Common/movie/bkm/Movie.ModeApi.cs b/BizHawk.Client.Common/movie/bkm/Movie.ModeApi.cs new file mode 100644 index 0000000000..f77fd0802a --- /dev/null +++ b/BizHawk.Client.Common/movie/bkm/Movie.ModeApi.cs @@ -0,0 +1,97 @@ +namespace BizHawk.Client.Common +{ + public partial class Movie : IMovie + { + private enum Moviemode { Inactive, Play, Record, Finished } + + private Moviemode _mode = Moviemode.Inactive; + + public bool IsPlaying + { + get { return _mode == Moviemode.Play || _mode == Moviemode.Finished; } + } + + public bool IsRecording + { + get { return _mode == Moviemode.Record; } + } + + public bool IsActive + { + get { return _mode != Moviemode.Inactive; } + } + + public bool IsFinished + { + get { return _mode == Moviemode.Finished; } + } + + public void StartNewRecording() + { + // adelikat: ClearSaveRam shouldn't be here at all most likely, especially considering this is an implementation detail + // If Starting a new recording requires clearing sram it shoudl be done at a higher layer and not rely on all IMovies doing this + // Haven't removed it yet because I coudln't guarantee that power-on movies coudl live without it + // And the immediate fire is that Savestate movies are breaking + if (!StartsFromSavestate) + { + Global.Emulator.ClearSaveRam(); + } + + _mode = Moviemode.Record; + if (Global.Config.EnableBackupMovies && _makeBackup && _log.Length > 0) + { + SaveBackup(); + _makeBackup = false; + } + + _log.Clear(); + } + + public void StartNewPlayback() + { + // See StartNewRecording for details as to why this savestate check is here + if (!StartsFromSavestate) + { + Global.Emulator.ClearSaveRam(); + } + + _mode = Moviemode.Play; + } + + public void SwitchToRecord() + { + _mode = Moviemode.Record; + } + + public void SwitchToPlay() + { + _mode = Moviemode.Play; + Save(); + } + + public void Stop(bool saveChanges = true) + { + if (saveChanges) + { + if (_mode == Moviemode.Record || _changes) + { + Save(); + } + } + + _changes = false; + _mode = Moviemode.Inactive; + } + + /// + /// If a movie is in playback mode, this will set it to movie finished + /// + private void Finish() + { + if (_mode == Moviemode.Play) + { + _mode = Moviemode.Finished; + } + } + } +} diff --git a/BizHawk.Client.Common/movie/bkm/Movie.cs b/BizHawk.Client.Common/movie/bkm/Movie.cs index be7da8ed74..d10f9a8fc9 100644 --- a/BizHawk.Client.Common/movie/bkm/Movie.cs +++ b/BizHawk.Client.Common/movie/bkm/Movie.cs @@ -1,23 +1,12 @@ using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Text; - -using BizHawk.Common; using BizHawk.Emulation.Common; - namespace BizHawk.Client.Common { - public class Movie : IMovie + public partial class Movie : IMovie { - private readonly MovieLog _log = new MovieLog(); private readonly PlatformFrameRates _frameRates = new PlatformFrameRates(); private bool _makeBackup = true; - - private Moviemode _mode = Moviemode.Inactive; - private int _preloadFramecount; // Not a a reliable number, used for preloading (when no log has yet been loaded), this is only for quick stat compilation for dialogs such as play movie private bool _changes; private int? _loopOffset; @@ -36,212 +25,20 @@ namespace BizHawk.Client.Common Filename = string.Empty; _preloadFramecount = 0; StartsFromSavestate = startsFromSavestate; - + IsCountingRerecords = true; _mode = Moviemode.Inactive; _makeBackup = true; } - private enum Moviemode { Inactive, Play, Record, Finished } - #region Properties - public SubtitleList Subtitles - { - get { return Header.Subtitles; } - } - - public IList Comments - { - get { return Header.Comments; } - } - - public string SyncSettingsJson - { - get - { - return Header[HeaderKeys.SYNCSETTINGS]; - } - - set - { - Header[HeaderKeys.SYNCSETTINGS] = value; - } - } - - public string SavestateBinaryBase64Blob - { - get - { - return Header.SavestateBinaryBase64Blob; - } - - set - { - Header.SavestateBinaryBase64Blob = value; - } - } - - public ulong Rerecords - { - get - { - return Header.Rerecords; - } - - set - { - Header.Rerecords = value; - } - } - - public bool StartsFromSavestate - { - get - { - return Header.StartsFromSavestate; - } - - set - { - Header.StartsFromSavestate = value; - } - } - - public string GameName - { - get - { - return Header.GameName; - } - - set - { - Header.GameName = value; - } - } - - public string SystemID - { - get - { - return Header.SystemID; - } - - set - { - Header.SystemID = value; - } - } - - public string Hash - { - get - { - return Header[HeaderKeys.SHA1]; - } - - set - { - Header[HeaderKeys.SHA1] = value; - } - } - - public string Author - { - get - { - return Header[HeaderKeys.AUTHOR]; - } - - set - { - Header[HeaderKeys.AUTHOR] = value; - } - } - - public string Core - { - get - { - return Header[HeaderKeys.CORE]; - } - - set - { - Header[HeaderKeys.CORE] = value; - } - } - - public string Platform - { - get - { - return Header[HeaderKeys.PLATFORM]; - } - - set - { - Header[HeaderKeys.PLATFORM] = value; - } - } - - public string BoardName - { - get - { - return Header[HeaderKeys.BOARDNAME]; - } - - set - { - Header[HeaderKeys.BOARDNAME] = value; - } - } - - public string EmulatorVersion - { - get - { - return Header[HeaderKeys.EMULATIONVERSION]; - } - - set - { - Header[HeaderKeys.EMULATIONVERSION] = value; - } - } - - public string FirmwareHash - { - get - { - return Header[HeaderKeys.FIRMWARESHA1]; - } - - set - { - Header[HeaderKeys.FIRMWARESHA1] = value; - } - } - - public IDictionary HeaderEntries - { - get - { - return Header; - } - } - public string PreferredExtension { get { return "bkm"; } } - public MovieHeader Header { get; private set; } - public string Filename { get; set; } public bool IsCountingRerecords { get; set; } - public bool Loaded { get; private set; } - + public int InputLogLength { get { return _log.Length; } @@ -265,325 +62,11 @@ namespace BizHawk.Client.Common } } - #endregion - - #region Mode API - - public bool IsPlaying - { - get { return _mode == Moviemode.Play || _mode == Moviemode.Finished; } - } - - public bool IsRecording - { - get { return _mode == Moviemode.Record; } - } - - public bool IsActive - { - get { return _mode != Moviemode.Inactive; } - } - - public bool IsFinished - { - get { return _mode == Moviemode.Finished; } - } - public bool Changes { get { return _changes; } } - public void StartNewRecording() - { - // adelikat: ClearSaveRam shouldn't be here at all most likely, especially considering this is an implementation detail - // If Starting a new recording requires clearing sram it shoudl be done at a higher layer and not rely on all IMovies doing this - // Haven't removed it yet because I coudln't guarantee that power-on movies coudl live without it - // And the immediate fire is that Savestate movies are breaking - if (!StartsFromSavestate) - { - Global.Emulator.ClearSaveRam(); - } - - _mode = Moviemode.Record; - if (Global.Config.EnableBackupMovies && _makeBackup && _log.Length > 0) - { - SaveBackup(); - _makeBackup = false; - } - - _log.Clear(); - } - - public void StartNewPlayback() - { - // See StartNewRecording for details as to why this savestate check is here - if (!StartsFromSavestate) - { - Global.Emulator.ClearSaveRam(); - } - - _mode = Moviemode.Play; - } - - public void SwitchToRecord() - { - _mode = Moviemode.Record; - } - - public void SwitchToPlay() - { - _mode = Moviemode.Play; - Save(); - } - - public void Stop(bool saveChanges = true) - { - if (saveChanges) - { - if (_mode == Moviemode.Record || _changes) - { - Save(); - } - } - - _changes = false; - _mode = Moviemode.Inactive; - } - - /// - /// If a movie is in playback mode, this will set it to movie finished - /// - private void Finish() - { - if (_mode == Moviemode.Play) - { - _mode = Moviemode.Finished; - } - } - - #endregion - - #region Public File Handling - - public void SaveAs(string path) - { - Filename = path; - if (!Loaded) - { - return; - } - - var directory_info = new FileInfo(Filename).Directory; - if (directory_info != null) - { - Directory.CreateDirectory(directory_info.FullName); - } - - Write(Filename); - } - - public void Save() - { - if (!Loaded || string.IsNullOrWhiteSpace(Filename)) - { - return; - } - - SaveAs(Filename); - _changes = false; - } - - public void SaveBackup() - { - if (!Loaded || string.IsNullOrWhiteSpace(Filename)) - { - return; - } - - var backupName = Filename; - backupName = backupName.Insert(Filename.LastIndexOf("."), string.Format(".{0:yyyy-MM-dd HH.mm.ss}", DateTime.Now)); - backupName = Path.Combine(Global.Config.PathEntries["Global", "Movie backups"].Path, Path.GetFileName(backupName) ?? string.Empty); - - var directory_info = new FileInfo(backupName).Directory; - if (directory_info != null) - { - Directory.CreateDirectory(directory_info.FullName); - } - - Write(backupName); - } - - /// - /// Load Header information only for displaying file information in dialogs such as play movie - /// TODO - consider not loading the SavestateBinaryBase64Blob key? - /// - public bool PreLoadText(HawkFile hawkFile) - { - Loaded = false; - var file = new FileInfo(hawkFile.CanonicalFullPath); - - if (file.Exists == false) - { - return false; - } - - Header.Clear(); - _log.Clear(); - - var origStreamPosn = hawkFile.GetStream().Position; - hawkFile.GetStream().Position = 0; // Reset to start - - // No using block because we're sharing the stream and need to give it back undisposed. - var sr = new StreamReader(hawkFile.GetStream()); - - for(;;) - { - //read to first space (key/value delimeter), or pipe, or EOF - int first = sr.Read(); - - if (first == -1) break; //EOF - else if(first == '|') //pipe: begin input log - { - //NOTE - this code is a bit convoluted due to its predating the basic outline of the parser which was upgraded in may 2014 - string line = '|' + sr.ReadLine(); - - //how many bytes are left, total? - long remain = sr.BaseStream.Length - sr.BaseStream.Position; - - //try to find out whether we use \r\n or \n - //but only look for 1K characters. - bool usesR = false; - for (int i = 0; i < 1024; i++) - { - int c = sr.Read(); - if (c == -1) - break; - if (c == '\r') - { - usesR = true; - break; - } - if (c == '\n') - break; - } - - int lineLen = line.Length + 1; //account for \n - if (usesR) lineLen++; //account for \r - - _preloadFramecount = (int)(remain / lineLen); //length is remaining bytes / length per line - _preloadFramecount++; //account for the current line - break; - } - else - { - //a header line. finish reading key token, to make sure it isn't one of the FORBIDDEN keys - StringBuilder sbLine = new StringBuilder(); - sbLine.Append((char)first); - for (; ; ) - { - int c = sr.Read(); - if (c == -1) break; - if (c == '\n') break; - if (c == ' ') break; - sbLine.Append((char)c); - } - - string line = sbLine.ToString(); - - //ignore these suckers, theyre way too big for preloading. seriously, we will get out of memory errors. - bool skip = false; - if (line == HeaderKeys.SAVESTATEBINARYBASE64BLOB) skip = true; - - if (skip) - { - //skip remainder of the line - sr.DiscardBufferedData(); - var stream = sr.BaseStream; - for (; ; ) - { - int c = stream.ReadByte(); - if (c == -1) break; - if (c == '\n') break; - } - //proceed to next line - continue; - } - - - string remainder = sr.ReadLine(); - sbLine.Append(' '); - sbLine.Append(remainder); - line = sbLine.ToString(); - - if (string.IsNullOrWhiteSpace(line) || Header.ParseLineFromFile(line)) - { - continue; - } - - Header.Comments.Add(line); - } - } - - hawkFile.GetStream().Position = origStreamPosn; - - return true; - } - - public bool Load() - { - var file = new FileInfo(Filename); - - if (file.Exists == false) - { - Loaded = false; - return false; - } - - Header.Clear(); - _log.Clear(); - - using (var sr = file.OpenText()) - { - string line; - - while ((line = sr.ReadLine()) != null) - { - if (line == string.Empty) - { - continue; - } - - if (line.Contains("LoopOffset")) - { - try - { - _loopOffset = int.Parse(line.Split(new[] { ' ' }, 2)[1]); - } - catch (Exception) - { - continue; - } - } - else if (Header.ParseLineFromFile(line)) - { - continue; - } - else if (line.StartsWith("|")) - { - _log.AppendFrame(line); - } - else - { - Header.Comments.Add(line); - } - } - } - - Loaded = true; - return true; - } - #endregion #region Public Log Editing @@ -643,10 +126,6 @@ namespace BizHawk.Client.Common _changes = true; } - #endregion - - #region Public Misc Methods - public void PokeFrame(int frame, IController source) { var mg = new MnemonicsGenerator(); @@ -676,176 +155,6 @@ namespace BizHawk.Client.Common _log.SetFrameAt(frame, mg.GetControllersAsMnemonic()); } - public string GetInputLog() - { - var sb = new StringBuilder(); - - sb.AppendLine("[Input]"); - - foreach (var record in _log) - { - sb.AppendLine(record); - } - - sb.AppendLine("[/Input]"); - - return sb.ToString(); - } - - public bool ExtractInputLog(TextReader reader, out string errorMessage) - { - errorMessage = string.Empty; - int? stateFrame = null; - - // We are in record mode so replace the movie log with the one from the savestate - if (!Global.MovieSession.MultiTrack.IsActive) - { - if (Global.Config.EnableBackupMovies && _makeBackup && _log.Length > 0) - { - SaveBackup(); - _makeBackup = false; - } - - _log.Clear(); - while (true) - { - var line = reader.ReadLine(); - if (line == null) - { - break; - } - - if (line.Trim() == string.Empty || line == "[Input]") - { - continue; - } - - if (line == "[/Input]") - { - break; - } - - if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay - { - var strs = line.Split('x'); - try - { - stateFrame = int.Parse(strs[1], NumberStyles.HexNumber); - } - catch - { - errorMessage = "Savestate Frame number failed to parse"; - return false; - } - } - else if (line.Contains("Frame ")) - { - var strs = line.Split(' '); - try - { - stateFrame = int.Parse(strs[1]); - } - catch - { - errorMessage = "Savestate Frame number failed to parse"; - return false; - } - } - else if (line[0] == '|') - { - _log.AppendFrame(line); - } - } - } - else - { - var i = 0; - while (true) - { - var line = reader.ReadLine(); - if (line == null) - { - break; - } - - if (line.Trim() == string.Empty || line == "[Input]") - { - continue; - } - - if (line == "[/Input]") - { - break; - } - - if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay - { - var strs = line.Split('x'); - try - { - stateFrame = int.Parse(strs[1], NumberStyles.HexNumber); - } - catch - { - errorMessage = "Savestate Frame number failed to parse"; - return false; - } - } - else if (line.Contains("Frame ")) - { - var strs = line.Split(' '); - try - { - stateFrame = int.Parse(strs[1]); - } - catch - { - errorMessage = "Savestate Frame number failed to parse"; - return false; - } - } - else if (line.StartsWith("|")) - { - _log.SetFrameAt(i, line); - i++; - } - } - } - - if (!stateFrame.HasValue) - { - errorMessage = "Savestate Frame number failed to parse"; - } - - var stateFramei = stateFrame ?? 0; - - if (stateFramei > 0 && stateFramei < _log.Length) - { - if (!Global.Config.VBAStyleMovieLoadState) - { - _log.TruncateStates(stateFramei); - _log.TruncateMovie(stateFramei); - } - } - else if (stateFramei > _log.Length) // Post movie savestate - { - if (!Global.Config.VBAStyleMovieLoadState) - { - _log.TruncateStates(_log.Length); - _log.TruncateMovie(_log.Length); - } - - _mode = Moviemode.Finished; - } - - if (IsCountingRerecords) - { - Rerecords++; - } - - return true; - } - public TimeSpan Time { get @@ -860,142 +169,8 @@ namespace BizHawk.Client.Common } } - public bool CheckTimeLines(TextReader reader, out string errorMessage) - { - // This function will compare the movie data to the savestate movie data to see if they match - errorMessage = string.Empty; - var log = new MovieLog(); - var stateFrame = 0; - while (true) - { - var line = reader.ReadLine(); - if (line == null) - { - return false; - } - - if (line.Trim() == string.Empty) - { - continue; - } - - if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay - { - var strs = line.Split('x'); - try - { - stateFrame = int.Parse(strs[1], NumberStyles.HexNumber); - } - catch - { - errorMessage = "Savestate Frame number failed to parse"; - return false; - } - } - else if (line.Contains("Frame ")) - { - var strs = line.Split(' '); - try - { - stateFrame = int.Parse(strs[1]); - } - catch - { - errorMessage = "Savestate Frame number failed to parse"; - return false; - } - } - else if (line == "[Input]") - { - continue; - } - else if (line == "[/Input]") - { - break; - } - else if (line[0] == '|') - { - log.AppendFrame(line); - } - } - - 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) - { - if (IsFinished) - { - return true; - } - - errorMessage = "The savestate is from frame " - + log.Length - + " which is greater than the current movie length of " - + _log.Length; - - return false; - } - - for (var i = 0; i < stateFrame; i++) - { - if (_log[i] != log[i]) - { - errorMessage = "The savestate input does not match the movie input at frame " - + (i + 1) - + "."; - - return false; - } - } - - if (stateFrame > log.Length) // stateFrame is greater than state input log, so movie finished mode - { - if (_mode == Moviemode.Play || _mode == Moviemode.Finished) - { - _mode = Moviemode.Finished; - return true; - } - - return false; - } - - if (_mode == Moviemode.Finished) - { - _mode = Moviemode.Play; - } - - return true; - } - #endregion - #region Helpers - - private void Write(string fn) - { - using (var fs = new FileStream(fn, FileMode.Create, FileAccess.Write, FileShare.Read)) - { - using (var sw = new StreamWriter(fs)) - { - sw.Write(Header.ToString()); - - // TODO: clean this up - if (_loopOffset.HasValue) - { - sw.WriteLine("LoopOffset " + _loopOffset); - } - - foreach (var input in _log) - { - sw.WriteLine(input); - } - } - } - } - private double GetSeconds(int frameCount) { double frames = frameCount; @@ -1023,7 +198,5 @@ namespace BizHawk.Client.Common return _frameRates[system, pal]; } } - - #endregion } } \ No newline at end of file