BizHawk/BizHawk.Client.Common/movie/Movie.cs

808 lines
16 KiB
C#
Raw Normal View History

2013-10-25 00:59:34 +00:00
using System;
2013-11-30 02:20:34 +00:00
using System.Collections.Generic;
2013-10-25 00:59:34 +00:00
using System.Globalization;
2013-11-30 02:20:34 +00:00
using System.IO;
using System.Text;
2013-11-27 21:45:50 +00:00
using BizHawk.Common;
using BizHawk.Emulation.Common;
2013-10-25 00:59:34 +00:00
namespace BizHawk.Client.Common
{
public class Movie : IMovie
2013-10-25 00:59:34 +00:00
{
#region Constructors
public Movie(string filename, bool startsFromSavestate = false)
: this(startsFromSavestate)
2013-10-25 00:59:34 +00:00
{
Header.Rerecords = 0;
2013-10-25 00:59:34 +00:00
Filename = filename;
Loaded = !String.IsNullOrWhiteSpace(filename);
2013-10-25 00:59:34 +00:00
}
public Movie(bool startsFromSavestate = false)
2013-10-25 00:59:34 +00:00
{
Header = new MovieHeader();
2013-10-25 00:59:34 +00:00
Filename = String.Empty;
_preloadFramecount = 0;
2013-11-30 02:20:34 +00:00
Header.StartsFromSavestate = startsFromSavestate;
2013-10-25 00:59:34 +00:00
IsCountingRerecords = true;
_mode = Moviemode.Inactive;
MakeBackup = true;
2013-10-25 00:59:34 +00:00
}
#endregion
#region Properties
public IMovieHeader Header { get; private set; }
public bool MakeBackup { get; set; }
public string Filename { get; set; }
public bool IsCountingRerecords { get; set; }
2013-10-25 00:59:34 +00:00
public bool Loaded { get; private set; }
public int InputLogLength
2013-10-25 00:59:34 +00:00
{
get { return Loaded ? _log.Length : _preloadFramecount; }
2013-10-25 00:59:34 +00:00
}
public double FrameCount
2013-10-25 00:59:34 +00:00
{
get
{
if (_loopOffset.HasValue)
2013-10-25 00:59:34 +00:00
{
return double.PositiveInfinity;
}
else if (Loaded)
{
return _log.Length;
2013-10-25 00:59:34 +00:00
}
else
{
return _preloadFramecount;
2013-10-25 00:59:34 +00:00
}
}
}
#endregion
#region Mode API
2013-10-25 00:59:34 +00:00
public bool IsPlaying
{
get { return _mode == Moviemode.Play || _mode == Moviemode.Finished; }
2013-10-25 00:59:34 +00:00
}
public bool IsRecording
{
get { return _mode == Moviemode.Record; }
2013-10-25 00:59:34 +00:00
}
public bool IsActive
{
get { return _mode != Moviemode.Inactive; }
2013-10-25 00:59:34 +00:00
}
public bool IsFinished
{
get { return _mode == Moviemode.Finished; }
2013-10-25 00:59:34 +00:00
}
public bool Changes
2013-10-25 00:59:34 +00:00
{
get { return _changes; }
2013-10-25 00:59:34 +00:00
}
public void StartNewRecording()
2013-10-25 00:59:34 +00:00
{
Global.Emulator.ClearSaveRam();
_mode = Moviemode.Record;
if (Global.Config.EnableBackupMovies && MakeBackup && _log.Length > 0)
2013-10-25 00:59:34 +00:00
{
SaveAs();
2013-10-25 00:59:34 +00:00
MakeBackup = false;
}
2013-11-30 02:20:34 +00:00
_log.Clear();
2013-10-25 00:59:34 +00:00
}
public void StartNewPlayback()
2013-10-25 00:59:34 +00:00
{
Global.Emulator.ClearSaveRam();
_mode = Moviemode.Play;
2013-10-25 00:59:34 +00:00
}
public void SwitchToRecord()
{
_mode = Moviemode.Record;
2013-10-25 00:59:34 +00:00
}
public void SwitchToPlay()
{
_mode = Moviemode.Play;
Save();
2013-10-25 00:59:34 +00:00
}
public void Stop(bool saveChanges = true)
2013-10-25 00:59:34 +00:00
{
if (saveChanges)
2013-10-25 00:59:34 +00:00
{
if (_mode == Moviemode.Record || _changes)
2013-10-25 00:59:34 +00:00
{
Save();
2013-10-25 00:59:34 +00:00
}
}
2013-11-30 02:20:34 +00:00
_changes = false;
_mode = Moviemode.Inactive;
2013-10-25 00:59:34 +00:00
}
/// <summary>
/// If a movie is in playback mode, this will set it to movie finished
/// </summary>
private void Finish()
2013-10-25 00:59:34 +00:00
{
if (_mode == Moviemode.Play)
2013-10-25 00:59:34 +00:00
{
_mode = Moviemode.Finished;
2013-10-25 00:59:34 +00:00
}
}
#endregion
#region Public File Handling
public void SaveAs(string path)
2013-10-25 00:59:34 +00:00
{
if (!Loaded)
{
return;
}
2013-11-30 02:20:34 +00:00
2013-10-25 00:59:34 +00:00
var directory_info = new FileInfo(Filename).Directory;
2013-11-30 02:20:34 +00:00
if (directory_info != null)
{
Directory.CreateDirectory(directory_info.FullName);
}
Write(Filename);
2013-10-25 00:59:34 +00:00
}
public void Save()
2013-10-25 00:59:34 +00:00
{
2013-10-28 00:44:01 +00:00
if (!Loaded || String.IsNullOrWhiteSpace(Filename))
2013-10-25 00:59:34 +00:00
{
return;
}
SaveAs(Filename);
_changes = false;
2013-10-25 00:59:34 +00:00
}
public void SaveAs()
2013-10-25 00:59:34 +00:00
{
2013-10-28 00:44:01 +00:00
if (!Loaded || String.IsNullOrWhiteSpace(Filename))
2013-10-25 00:59:34 +00:00
{
return;
}
2013-11-30 02:20:34 +00:00
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);
2013-10-25 00:59:34 +00:00
2013-11-30 02:20:34 +00:00
var directory_info = new FileInfo(backupName).Directory;
if (directory_info != null)
{
Directory.CreateDirectory(directory_info.FullName);
}
2013-10-25 00:59:34 +00:00
Write(backupName);
2013-10-25 00:59:34 +00:00
}
/// <summary>
/// Load Header information only for displaying file information in dialogs such as play movie
/// </summary>
2013-11-27 21:45:50 +00:00
public bool PreLoadText(HawkFile hawkFile)
2013-10-25 00:59:34 +00:00
{
Loaded = false;
2013-11-27 21:45:50 +00:00
var file = new FileInfo(hawkFile.CanonicalFullPath);
2013-10-25 00:59:34 +00:00
if (file.Exists == false)
2013-11-30 02:20:34 +00:00
{
2013-10-25 00:59:34 +00:00
return false;
2013-11-30 02:20:34 +00:00
}
2013-10-25 00:59:34 +00:00
else
{
Header.Clear();
_log.Clear();
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
var origStreamPosn = hawkFile.GetStream().Position;
hawkFile.GetStream().Position = 0; // Reset to start
var sr = new StreamReader(hawkFile.GetStream());
// No using block because we're sharing the stream and need to give it back undisposed.
if (!sr.EndOfStream)
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
string line;
while ((line = sr.ReadLine()) != null)
2013-10-25 00:59:34 +00:00
{
if (line.Contains("LoopOffset"))
{
try
{
_loopOffset = int.Parse(line.Split(new[] { ' ' }, 2)[1]);
}
catch (Exception)
{
continue;
}
}
else if (String.IsNullOrWhiteSpace(line) || Header.ParseLineFromFile(line))
2013-10-25 00:59:34 +00:00
{
continue;
}
else if (line.StartsWith("|"))
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
var frames = sr.ReadToEnd();
var length = line.Length;
2013-10-25 00:59:34 +00:00
// Account for line breaks of either size.
if (frames.IndexOf("\r\n") != -1)
{
length++;
}
length++;
2013-11-30 02:20:34 +00:00
_preloadFramecount = (frames.Length / length) + 1; // Count the remaining frames and the current one.
2013-10-25 00:59:34 +00:00
break;
}
else
{
2013-11-30 02:20:34 +00:00
Header.Comments.Add(line);
2013-10-25 00:59:34 +00:00
}
}
}
2013-11-30 02:20:34 +00:00
2013-11-27 21:45:50 +00:00
hawkFile.GetStream().Position = origStreamPosn;
2013-10-25 00:59:34 +00:00
return true;
}
public bool Load()
2013-10-25 00:59:34 +00:00
{
var file = new FileInfo(Filename);
if (file.Exists == false)
{
Loaded = false;
return false;
}
return LoadText();
}
#endregion
#region Public Log Editing
public string GetInput(int frame)
{
if (frame < _log.Length)
2013-10-25 00:59:34 +00:00
{
if (frame >= 0)
2013-10-25 00:59:34 +00:00
{
int getframe;
if (_loopOffset.HasValue)
{
if (frame < _log.Length)
{
getframe = frame;
}
else
{
getframe = ((frame - _loopOffset.Value) % (_log.Length - _loopOffset.Value)) + _loopOffset.Value;
}
}
else
{
getframe = frame;
}
return _log[getframe];
2013-10-25 00:59:34 +00:00
}
else
{
return String.Empty;
2013-10-25 00:59:34 +00:00
}
}
else
{
Finish();
return String.Empty;
2013-10-25 00:59:34 +00:00
}
}
public void ClearFrame(int frame)
{
_log.SetFrameAt(frame, MnemonicsGenerator.GetEmptyMnemonic);
_changes = true;
2013-10-25 00:59:34 +00:00
}
public void AppendFrame(MnemonicsGenerator mg)
2013-10-25 00:59:34 +00:00
{
_log.AppendFrame(mg.GetControllersAsMnemonic());
_changes = true;
2013-10-25 00:59:34 +00:00
}
public void Truncate(int frame)
2013-10-25 00:59:34 +00:00
{
_log.TruncateMovie(frame);
_log.TruncateStates(frame);
_changes = true;
2013-10-25 00:59:34 +00:00
}
#endregion
#region Public Misc Methods
2013-12-03 15:59:46 +00:00
public void PokeFrame(int frame, MnemonicsGenerator mg)
2013-10-25 00:59:34 +00:00
{
_changes = true;
2013-12-03 15:59:46 +00:00
_log.SetFrameAt(frame, mg.GetControllersAsMnemonic());
2013-10-25 00:59:34 +00:00
}
2013-12-03 15:59:46 +00:00
public void RecordFrame(int frame, MnemonicsGenerator mg)
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
// Note: Truncation here instead of loadstate will make VBA style loadstates
// (Where an entire movie is loaded then truncated on the next frame
// this allows users to restore a movie with any savestate from that "timeline"
2013-10-25 00:59:34 +00:00
if (Global.Config.VBAStyleMovieLoadState)
{
if (Global.Emulator.Frame < _log.Length)
2013-10-25 00:59:34 +00:00
{
_log.TruncateMovie(Global.Emulator.Frame);
2013-10-25 00:59:34 +00:00
}
}
2013-11-30 02:20:34 +00:00
_changes = true;
2013-12-03 15:59:46 +00:00
_log.SetFrameAt(frame, mg.GetControllersAsMnemonic());
2013-10-25 00:59:34 +00:00
}
public string GetInputLog()
2013-10-25 00:59:34 +00:00
{
var sb = new StringBuilder();
sb.AppendLine("[Input]");
2013-10-28 00:44:01 +00:00
foreach (var record in _log)
2013-10-25 00:59:34 +00:00
{
sb.AppendLine(record);
2013-10-25 00:59:34 +00:00
}
2013-10-28 00:44:01 +00:00
sb.AppendLine("[/Input]");
return sb.ToString();
2013-10-25 00:59:34 +00:00
}
public bool ExtractInputLog(TextReader reader, out string errorMessage)
2013-10-25 00:59:34 +00:00
{
errorMessage = String.Empty;
2013-10-25 00:59:34 +00:00
int? stateFrame = null;
2013-11-30 02:20:34 +00:00
// We are in record mode so replace the movie log with the one from the savestate
if (!Global.MovieSession.MultiTrack.IsActive)
2013-10-25 00:59:34 +00:00
{
if (Global.Config.EnableBackupMovies && MakeBackup && _log.Length > 0)
2013-10-25 00:59:34 +00:00
{
SaveAs();
2013-10-25 00:59:34 +00:00
MakeBackup = false;
}
2013-11-30 02:20:34 +00:00
_log.Clear();
2013-10-25 00:59:34 +00:00
while (true)
{
2013-11-30 02:20:34 +00:00
var line = reader.ReadLine();
if (line == null)
{
break;
}
else if (line.Trim() == String.Empty)
{
continue;
}
else if (line == "[Input]")
{
continue;
}
else if (line == "[/Input]")
{
break;
}
else if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
var strs = line.Split('x');
2013-10-25 00:59:34 +00:00
try
{
stateFrame = int.Parse(strs[1], NumberStyles.HexNumber);
}
catch
{
errorMessage = "Savestate Frame number failed to parse";
return false;
}
2013-10-25 00:59:34 +00:00
}
else if (line.Contains("Frame "))
{
2013-11-30 02:20:34 +00:00
var strs = line.Split(' ');
2013-10-25 00:59:34 +00:00
try
{
stateFrame = int.Parse(strs[1]);
}
catch
{
errorMessage = "Savestate Frame number failed to parse";
return false;
}
2013-10-25 00:59:34 +00:00
}
if (line[0] == '|')
{
_log.AppendFrame(line);
2013-10-25 00:59:34 +00:00
}
}
}
else
{
int i = 0;
while (true)
{
2013-11-30 02:20:34 +00:00
var line = reader.ReadLine();
if (line == null)
{
break;
}
else if (line.Trim() == string.Empty)
{
continue;
}
else if (line == "[Input]")
{
continue;
}
else if (line == "[/Input]")
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
break;
}
else if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay
{
var strs = line.Split('x');
2013-10-25 00:59:34 +00:00
try
{
stateFrame = int.Parse(strs[1], NumberStyles.HexNumber);
}
catch
{
errorMessage = "Savestate Frame number failed to parse";
return false;
}
2013-10-25 00:59:34 +00:00
}
else if (line.Contains("Frame "))
{
2013-11-30 02:20:34 +00:00
var strs = line.Split(' ');
2013-10-25 00:59:34 +00:00
try
{
stateFrame = int.Parse(strs[1]);
}
catch
{
errorMessage = "Savestate Frame number failed to parse";
return false;
}
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
else if (line.StartsWith("|"))
2013-10-25 00:59:34 +00:00
{
_log.SetFrameAt(i, line);
2013-10-25 00:59:34 +00:00
i++;
}
}
}
2013-11-30 02:20:34 +00:00
2013-10-25 00:59:34 +00:00
if (stateFrame == null)
2013-11-30 02:20:34 +00:00
{
errorMessage = "Savestate Frame number failed to parse";
2013-11-30 02:20:34 +00:00
}
var stateFramei = (int)stateFrame;
2013-10-25 00:59:34 +00:00
if (stateFramei > 0 && stateFramei < _log.Length)
2013-10-25 00:59:34 +00:00
{
if (!Global.Config.VBAStyleMovieLoadState)
{
_log.TruncateStates(stateFramei);
_log.TruncateMovie(stateFramei);
2013-10-25 00:59:34 +00:00
}
}
2013-11-30 02:20:34 +00:00
else if (stateFramei > _log.Length) // Post movie savestate
2013-10-25 00:59:34 +00:00
{
if (!Global.Config.VBAStyleMovieLoadState)
{
_log.TruncateStates(_log.Length);
_log.TruncateMovie(_log.Length);
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
_mode = Moviemode.Finished;
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
2013-10-25 00:59:34 +00:00
if (IsCountingRerecords)
2013-11-30 02:20:34 +00:00
{
Header.Rerecords++;
2013-11-30 02:20:34 +00:00
}
return true;
2013-10-25 00:59:34 +00:00
}
public TimeSpan Time
2013-10-25 00:59:34 +00:00
{
get
2013-10-25 00:59:34 +00:00
{
double dblseconds = GetSeconds(Loaded ? _log.Length : _preloadFramecount);
int seconds = (int)(dblseconds % 60);
int days = seconds / 86400;
int hours = seconds / 3600;
int minutes = (seconds / 60) % 60;
int milliseconds = (int)((dblseconds - (double)seconds) * 1000);
return new TimeSpan(days, hours, minutes, seconds, milliseconds);
2013-10-25 00:59:34 +00:00
}
}
2013-12-04 03:16:35 +00:00
public bool CheckTimeLines(TextReader reader, out string errorMessage)
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
// This function will compare the movie data to the savestate movie data to see if they match
errorMessage = String.Empty;
2013-10-25 00:59:34 +00:00
var log = new MovieLog();
int stateFrame = 0;
while (true)
{
2013-11-30 02:20:34 +00:00
var line = reader.ReadLine();
2013-10-25 00:59:34 +00:00
if (line == null)
{
2013-12-04 03:16:35 +00:00
return false;
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
else if (line.Trim() == string.Empty)
2013-10-25 00:59:34 +00:00
{
continue;
}
2013-11-30 02:20:34 +00:00
else if (line.Contains("Frame 0x")) // NES stores frame count in hex, yay
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
var strs = line.Split('x');
2013-10-25 00:59:34 +00:00
try
{
stateFrame = int.Parse(strs[1], NumberStyles.HexNumber);
}
catch
{
errorMessage = "Savestate Frame number failed to parse";
2013-12-04 03:16:35 +00:00
return false;
2013-10-25 00:59:34 +00:00
}
}
else if (line.Contains("Frame "))
{
2013-11-30 02:20:34 +00:00
var strs = line.Split(' ');
2013-10-25 00:59:34 +00:00
try
{
stateFrame = int.Parse(strs[1]);
}
catch
{
errorMessage = "Savestate Frame number failed to parse";
2013-12-04 03:16:35 +00:00
return false;
2013-10-25 00:59:34 +00:00
}
}
2013-11-30 02:20:34 +00:00
else if (line == "[Input]")
{
continue;
}
else if (line == "[/Input]")
{
break;
}
2013-10-25 00:59:34 +00:00
else if (line[0] == '|')
{
log.AppendFrame(line);
}
}
2013-10-25 00:59:34 +00:00
if (stateFrame == 0)
{
2013-11-30 02:20:34 +00:00
stateFrame = log.Length; // In case the frame count failed to parse, revert to using the entire state input log
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
if (_log.Length < stateFrame)
2013-10-25 00:59:34 +00:00
{
2013-11-23 00:13:36 +00:00
if (IsFinished)
{
2013-12-04 03:16:35 +00:00
return true;
2013-11-23 00:13:36 +00:00
}
else
{
errorMessage = "The savestate is from frame "
2013-11-30 02:20:34 +00:00
+ log.Length
2013-11-23 00:13:36 +00:00
+ " which is greater than the current movie length of "
2013-11-30 02:20:34 +00:00
+ _log.Length;
2013-12-04 03:16:35 +00:00
return false;
2013-11-23 00:13:36 +00:00
}
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
for (var i = 0; i < stateFrame; i++)
2013-10-25 00:59:34 +00:00
{
2013-10-28 00:44:01 +00:00
if (_log[i] != log[i])
2013-10-25 00:59:34 +00:00
{
errorMessage = "The savestate input does not match the movie input at frame "
2013-11-30 02:20:34 +00:00
+ (i + 1)
2013-10-25 00:59:34 +00:00
+ ".";
2013-12-04 03:16:35 +00:00
return false;
2013-10-25 00:59:34 +00:00
}
}
2013-11-30 02:20:34 +00:00
if (stateFrame > log.Length) // stateFrame is greater than state input log, so movie finished mode
2013-10-25 00:59:34 +00:00
{
if (_mode == Moviemode.Play || _mode == Moviemode.Finished)
2013-10-25 00:59:34 +00:00
{
_mode = Moviemode.Finished;
2013-12-04 03:16:35 +00:00
return true;
2013-10-25 00:59:34 +00:00
}
else
{
2013-12-04 03:16:35 +00:00
return false;
2013-10-25 00:59:34 +00:00
}
}
else if (_mode == Moviemode.Finished)
2013-10-25 00:59:34 +00:00
{
_mode = Moviemode.Play;
2013-10-25 00:59:34 +00:00
}
2013-12-04 03:16:35 +00:00
return true;
2013-10-25 00:59:34 +00:00
}
#endregion
#region Private Vars
2013-10-25 00:59:34 +00:00
private readonly MovieLog _log = new MovieLog();
private enum Moviemode { Inactive, Play, Record, Finished };
private Moviemode _mode = Moviemode.Inactive;
2013-11-30 02:20:34 +00:00
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;
private readonly PlatformFrameRates _frameRates = new PlatformFrameRates();
2013-10-25 00:59:34 +00:00
#endregion
#region Helpers
private void Write(string fn)
2013-10-25 00:59:34 +00:00
{
using (var fs = new FileStream(fn, FileMode.Create, FileAccess.Write, FileShare.Read))
2013-11-30 02:20:34 +00:00
{
2013-10-25 00:59:34 +00:00
WriteText(fs);
2013-11-30 02:20:34 +00:00
}
2013-10-25 00:59:34 +00:00
}
private void WriteText(Stream stream)
{
using (var sw = new StreamWriter(stream))
2013-10-25 00:59:34 +00:00
{
sw.Write(Header.ToString());
2013-10-25 00:59:34 +00:00
// TODO: clean this up
if (_loopOffset.HasValue)
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
sw.WriteLine("LoopOffset " + _loopOffset);
2013-10-25 00:59:34 +00:00
}
for (int i = 0; i < _log.Length; i++)
{
sw.WriteLine(_log[i]);
}
2013-10-25 00:59:34 +00:00
}
}
private bool LoadText()
{
var file = new FileInfo(Filename);
if (file.Exists == false)
{
Loaded = false;
return false;
}
else
{
Header.Clear();
_log.Clear();
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
using (var sr = file.OpenText())
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
string line;
2013-10-25 00:59:34 +00:00
2013-11-30 02:20:34 +00:00
while ((line = sr.ReadLine()) != null)
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
if (line == String.Empty)
2013-10-25 00:59:34 +00:00
{
continue;
}
2013-11-30 02:20:34 +00:00
if (line.Contains("LoopOffset"))
2013-10-25 00:59:34 +00:00
{
try
{
2013-11-30 02:20:34 +00:00
_loopOffset = int.Parse(line.Split(new[] { ' ' }, 2)[1]);
2013-10-25 00:59:34 +00:00
}
2013-11-30 02:20:34 +00:00
catch (Exception)
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
continue;
2013-10-25 00:59:34 +00:00
}
}
2013-11-30 02:20:34 +00:00
else if (Header.ParseLineFromFile(line))
2013-10-25 00:59:34 +00:00
{
continue;
}
2013-11-30 02:20:34 +00:00
else if (line.StartsWith("|"))
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
_log.AppendFrame(line);
2013-10-25 00:59:34 +00:00
}
else
{
2013-11-30 02:20:34 +00:00
Header.Comments.Add(line);
2013-10-25 00:59:34 +00:00
}
}
}
2013-11-30 02:20:34 +00:00
2013-10-25 00:59:34 +00:00
Loaded = true;
return true;
}
2013-11-30 02:20:34 +00:00
private static string MakeDigits(int num)
2013-10-25 00:59:34 +00:00
{
2013-11-30 02:20:34 +00:00
return num < 10 ? "0" + num : num.ToString();
2013-10-25 00:59:34 +00:00
}
private double GetSeconds(int frameCount)
{
double frames = frameCount;
if (frames < 1)
{
return 0;
}
2013-11-30 02:20:34 +00:00
var system = Header[HeaderKeys.PLATFORM];
var pal = Header.ContainsKey(HeaderKeys.PAL) &&
Header[HeaderKeys.PAL] == "1";
2013-10-25 00:59:34 +00:00
return frames / _frameRates[system, pal];
2013-10-25 00:59:34 +00:00
}
public double Fps
{
get
{
2013-11-30 02:20:34 +00:00
var system = Header[HeaderKeys.PLATFORM];
var pal = Header.ContainsKey(HeaderKeys.PAL) &&
Header[HeaderKeys.PAL] == "1";
return _frameRates[system, pal];
}
}
2013-10-25 00:59:34 +00:00
#endregion
}
}