Implement IBasicMovieInfo interface for IMovie (#3634)
* Implement IBasicMovieInfo interface for IMovie this allows parsing basic movie fields into an own class that is independent from Bk2Movie/TasMovie This is mainly useful for the PlayMovie dialog which can now load movie information from files on disk without having to go through the entire Bk2Movie/TasMovie loading process * don't potentially iterate input log twice * Optimize LoadFramecount
This commit is contained in:
parent
9b278d3130
commit
bc16a2cdaa
|
@ -0,0 +1,260 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class BasicMovieInfo : IBasicMovieInfo
|
||||
{
|
||||
private string _filename;
|
||||
private bool IsPal => Header[HeaderKeys.Pal] == "1";
|
||||
|
||||
protected readonly Bk2Header Header = new();
|
||||
|
||||
public BasicMovieInfo(string filename)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filename))
|
||||
{
|
||||
throw filename is null
|
||||
? new ArgumentNullException(paramName: nameof(filename))
|
||||
: new ArgumentException(message: "path cannot be blank", paramName: nameof(filename));
|
||||
}
|
||||
|
||||
Filename = filename;
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string Filename
|
||||
{
|
||||
get => _filename;
|
||||
set
|
||||
{
|
||||
_filename = value;
|
||||
Name = Path.GetFileName(Filename);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int FrameCount { get; private set; }
|
||||
|
||||
public TimeSpan TimeLength
|
||||
{
|
||||
get
|
||||
{
|
||||
double dblSeconds;
|
||||
|
||||
if (Header.TryGetValue(HeaderKeys.CycleCount, out var numCyclesStr) && Header.TryGetValue(HeaderKeys.ClockRate, out var clockRateStr))
|
||||
{
|
||||
var numCycles = Convert.ToUInt64(numCyclesStr);
|
||||
var clockRate = Convert.ToDouble(clockRateStr, CultureInfo.InvariantCulture);
|
||||
dblSeconds = numCycles / clockRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
var numFrames = (ulong)FrameCount;
|
||||
dblSeconds = numFrames / FrameRate;
|
||||
}
|
||||
|
||||
var seconds = (int)(dblSeconds % 60);
|
||||
var days = seconds / 86400;
|
||||
var hours = seconds / 3600;
|
||||
var minutes = (seconds / 60) % 60;
|
||||
var milliseconds = (int)((dblSeconds - seconds) * 1000);
|
||||
return new TimeSpan(days, hours, minutes, seconds, milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
public double FrameRate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SystemID == VSystemID.Raw.Arcade && Header.TryGetValue(HeaderKeys.VsyncAttoseconds, out var vsyncAttoStr))
|
||||
{
|
||||
const decimal attosInSec = 1000000000000000000;
|
||||
return (double)(attosInSec / Convert.ToUInt64(vsyncAttoStr));
|
||||
}
|
||||
else
|
||||
{
|
||||
return PlatformFrameRates.GetFrameRate(SystemID, IsPal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SubtitleList Subtitles { get; } = new();
|
||||
public IList<string> Comments { get; } = new List<string>();
|
||||
|
||||
public virtual string GameName
|
||||
{
|
||||
get => Header[HeaderKeys.GameName];
|
||||
set => Header[HeaderKeys.GameName] = value;
|
||||
}
|
||||
|
||||
public virtual string SystemID
|
||||
{
|
||||
get => Header[HeaderKeys.Platform];
|
||||
set => Header[HeaderKeys.Platform] = value;
|
||||
}
|
||||
|
||||
public virtual ulong Rerecords
|
||||
{
|
||||
get => ulong.Parse(Header[HeaderKeys.Rerecords]);
|
||||
set => Header[HeaderKeys.Rerecords] = value.ToString();
|
||||
}
|
||||
|
||||
public virtual string Hash
|
||||
{
|
||||
get => Header[HeaderKeys.Sha1];
|
||||
set => Header[HeaderKeys.Sha1] = value;
|
||||
}
|
||||
|
||||
public virtual string Author
|
||||
{
|
||||
get => Header[HeaderKeys.Author];
|
||||
set => Header[HeaderKeys.Author] = value;
|
||||
}
|
||||
|
||||
public virtual string Core
|
||||
{
|
||||
get => Header[HeaderKeys.Core];
|
||||
set => Header[HeaderKeys.Core] = value;
|
||||
}
|
||||
|
||||
public virtual string BoardName
|
||||
{
|
||||
get => Header[HeaderKeys.BoardName];
|
||||
set => Header[HeaderKeys.BoardName] = value;
|
||||
}
|
||||
|
||||
public virtual string EmulatorVersion
|
||||
{
|
||||
get => Header[HeaderKeys.EmulatorVersion];
|
||||
set => Header[HeaderKeys.EmulatorVersion] = value;
|
||||
}
|
||||
|
||||
public virtual string OriginalEmulatorVersion
|
||||
{
|
||||
get => Header[HeaderKeys.OriginalEmulatorVersion];
|
||||
set => Header[HeaderKeys.OriginalEmulatorVersion] = value;
|
||||
}
|
||||
|
||||
public virtual string FirmwareHash
|
||||
{
|
||||
get => Header[HeaderKeys.FirmwareSha1];
|
||||
set => Header[HeaderKeys.FirmwareSha1] = value;
|
||||
}
|
||||
|
||||
public IDictionary<string, string> HeaderEntries => Header;
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
var file = new FileInfo(Filename);
|
||||
if (!file.Exists)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var bl = ZipStateLoader.LoadAndDetect(Filename, true);
|
||||
if (bl is null) return false;
|
||||
ClearBeforeLoad();
|
||||
LoadFields(bl);
|
||||
if (FrameCount == 0)
|
||||
{
|
||||
// only iterate the input log if it hasn't been loaded already
|
||||
LoadFramecount(bl);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (InvalidDataException e) when (e.StackTrace.Contains("ZipArchive.ReadEndOfCentralDirectory"))
|
||||
{
|
||||
throw new Exception("Archive appears to be corrupt. Make a backup, then try to repair it with e.g. 7-Zip.", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ClearBeforeLoad()
|
||||
{
|
||||
Header.Clear();
|
||||
Subtitles.Clear();
|
||||
Comments.Clear();
|
||||
}
|
||||
|
||||
protected virtual void LoadFields(ZipStateLoader bl)
|
||||
{
|
||||
bl.GetLump(BinaryStateLump.Movieheader, abort: true, tr =>
|
||||
{
|
||||
while (tr.ReadLine() is string line)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
var pair = line.Split(new[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (pair.Length > 1)
|
||||
{
|
||||
if (!Header.ContainsKey(pair[0]))
|
||||
{
|
||||
Header.Add(pair[0], pair[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bl.GetLump(BinaryStateLump.Comments, abort: false, tr =>
|
||||
{
|
||||
while (tr.ReadLine() is string line)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
Comments.Add(line);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bl.GetLump(BinaryStateLump.Subtitles, abort: false, tr =>
|
||||
{
|
||||
while (tr.ReadLine() is string line)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
Subtitles.AddFromString(line);
|
||||
}
|
||||
}
|
||||
|
||||
Subtitles.Sort();
|
||||
});
|
||||
|
||||
bl.GetLump(BinaryStateLump.Subtitles, abort: false, tr =>
|
||||
{
|
||||
while (tr.ReadLine() is string line)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
Subtitles.AddFromString(line);
|
||||
}
|
||||
}
|
||||
|
||||
Subtitles.Sort();
|
||||
});
|
||||
}
|
||||
|
||||
private void LoadFramecount(ZipStateLoader bl)
|
||||
{
|
||||
bl.GetLump(BinaryStateLump.Input, abort: true, tr =>
|
||||
{
|
||||
// just skim through the input log and count input lines
|
||||
// FIXME: this is potentially expensive and shouldn't be necessary for something as simple as frame count
|
||||
while (tr.ReadLine() is string line)
|
||||
{
|
||||
if (line.Length > 0 && line[0] == '|')
|
||||
{
|
||||
FrameCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -188,7 +188,7 @@ namespace BizHawk.Client.Common
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <exception cref="MoviePlatformMismatchException"><paramref name="record"/> is <see langword="false"/> and <paramref name="movie"/>.<see cref="IMovie.SystemID"/> does not match <paramref name="systemId"/>.<see cref="IEmulator.SystemId"/></exception>
|
||||
/// <exception cref="MoviePlatformMismatchException"><paramref name="record"/> is <see langword="false"/> and <paramref name="movie"/>.<see cref="IBasicMovieInfo.SystemID"/> does not match <paramref name="systemId"/>.<see cref="IEmulator.SystemId"/></exception>
|
||||
public void QueueNewMovie(IMovie movie, bool record, string systemId, IDictionary<string, string> preferredCores)
|
||||
{
|
||||
if (movie.IsActive() && movie.Changes)
|
||||
|
@ -198,7 +198,7 @@ namespace BizHawk.Client.Common
|
|||
|
||||
if (!record) // The semantics of record is that we are starting a new movie, and even wiping a pre-existing movie with the same path, but non-record means we are loading an existing movie into playback mode
|
||||
{
|
||||
movie.Load(false);
|
||||
movie.Load();
|
||||
|
||||
if (movie.SystemID != systemId)
|
||||
{
|
||||
|
@ -400,4 +400,4 @@ namespace BizHawk.Client.Common
|
|||
Movie.RecordFrame(Movie.Emulator.Frame, MovieController);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public partial class Bk2Movie
|
||||
{
|
||||
protected readonly Bk2Header Header = new Bk2Header();
|
||||
private string _syncSettingsJson = "";
|
||||
|
||||
public IDictionary<string, string> HeaderEntries => Header;
|
||||
|
||||
public SubtitleList Subtitles { get; } = new SubtitleList();
|
||||
public IList<string> Comments { get; } = new List<string>();
|
||||
|
||||
public string SyncSettingsJson
|
||||
{
|
||||
get => _syncSettingsJson;
|
||||
|
@ -26,11 +19,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public ulong Rerecords
|
||||
public override ulong Rerecords
|
||||
{
|
||||
get => Header.TryGetValue(HeaderKeys.Rerecords, out var s)
|
||||
? ulong.Parse(s)
|
||||
: 0UL; // Modifying the header itself can cause a race condition between loading a movie and rendering the rerecord count, causing a movie's rerecord count to be overwritten with 0 during loading.
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.Rerecords] != value.ToString())
|
||||
|
@ -80,9 +70,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string GameName
|
||||
public override string GameName
|
||||
{
|
||||
get => Header.TryGetValue(HeaderKeys.GameName, out var s) ? s : string.Empty;
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.GameName] != value)
|
||||
|
@ -93,9 +82,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string SystemID
|
||||
public override string SystemID
|
||||
{
|
||||
get => Header.TryGetValue(HeaderKeys.Platform, out var s) ? s : string.Empty;
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.Platform] != value)
|
||||
|
@ -106,9 +94,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string Hash
|
||||
public override string Hash
|
||||
{
|
||||
get => Header[HeaderKeys.Sha1];
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.Sha1] != value)
|
||||
|
@ -119,9 +106,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string Author
|
||||
public override string Author
|
||||
{
|
||||
get => Header[HeaderKeys.Author];
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.Author] != value)
|
||||
|
@ -132,9 +118,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string Core
|
||||
public override string Core
|
||||
{
|
||||
get => Header[HeaderKeys.Core];
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.Core] != value)
|
||||
|
@ -145,9 +130,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string BoardName
|
||||
public override string BoardName
|
||||
{
|
||||
get => Header[HeaderKeys.BoardName];
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.BoardName] != value)
|
||||
|
@ -158,9 +142,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string EmulatorVersion
|
||||
public override string EmulatorVersion
|
||||
{
|
||||
get => Header[HeaderKeys.EmulatorVersion];
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.EmulatorVersion] != value)
|
||||
|
@ -171,9 +154,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string OriginalEmulatorVersion
|
||||
public override string OriginalEmulatorVersion
|
||||
{
|
||||
get => Header[HeaderKeys.OriginalEmulatorVersion];
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.OriginalEmulatorVersion] != value)
|
||||
|
@ -184,9 +166,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public string FirmwareHash
|
||||
public override string FirmwareHash
|
||||
{
|
||||
get => Header[HeaderKeys.FirmwareSha1];
|
||||
set
|
||||
{
|
||||
if (Header[HeaderKeys.FirmwareSha1] != value)
|
||||
|
@ -209,10 +190,6 @@ namespace BizHawk.Client.Common
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
// ReSharper disable SimplifyConditionalTernaryExpression
|
||||
public bool IsPal => Header.TryGetValue(HeaderKeys.Pal, out var s) ? s == "1" : false;
|
||||
// ReSharper restore SimplifyConditionalTernaryExpression
|
||||
|
||||
public string TextSavestate { get; set; }
|
||||
public byte[] BinarySavestate { get; set; }
|
||||
public int[] SavestateFramebuffer { get; set; }
|
||||
|
|
|
@ -29,31 +29,6 @@ namespace BizHawk.Client.Common
|
|||
Write(backupName, isBackup: true);
|
||||
}
|
||||
|
||||
public virtual bool Load(bool preload)
|
||||
{
|
||||
var file = new FileInfo(Filename);
|
||||
if (!file.Exists)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var bl = ZipStateLoader.LoadAndDetect(Filename, true);
|
||||
if (bl is null) return false;
|
||||
ClearBeforeLoad();
|
||||
LoadFields(bl, preload);
|
||||
Changes = false;
|
||||
return true;
|
||||
}
|
||||
catch (InvalidDataException e) when (e.StackTrace.Contains("ZipArchive.ReadEndOfCentralDirectory"))
|
||||
{
|
||||
throw new Exception("Archive appears to be corrupt. Make a backup, then try to repair it with e.g. 7-Zip.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public bool PreLoadHeaderAndLength() => Load(true);
|
||||
|
||||
protected virtual void Write(string fn, bool isBackup = false)
|
||||
{
|
||||
SetCycleValues();
|
||||
|
@ -136,49 +111,28 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
protected virtual void ClearBeforeLoad()
|
||||
protected override void ClearBeforeLoad()
|
||||
{
|
||||
base.ClearBeforeLoad();
|
||||
ClearBk2Fields();
|
||||
}
|
||||
|
||||
protected void ClearBk2Fields()
|
||||
private void ClearBk2Fields()
|
||||
{
|
||||
Header.Clear();
|
||||
Log.Clear();
|
||||
Subtitles.Clear();
|
||||
Comments.Clear();
|
||||
_syncSettingsJson = "";
|
||||
TextSavestate = null;
|
||||
BinarySavestate = null;
|
||||
}
|
||||
|
||||
protected virtual void LoadFields(ZipStateLoader bl, bool preload)
|
||||
protected override void LoadFields(ZipStateLoader bl)
|
||||
{
|
||||
LoadBk2Fields(bl, preload);
|
||||
base.LoadFields(bl);
|
||||
LoadBk2Fields(bl);
|
||||
}
|
||||
|
||||
protected void LoadBk2Fields(ZipStateLoader bl, bool preload)
|
||||
private void LoadBk2Fields(ZipStateLoader bl)
|
||||
{
|
||||
bl.GetLump(BinaryStateLump.Movieheader, abort: true, tr =>
|
||||
{
|
||||
string line;
|
||||
while ((line = tr.ReadLine()) != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
var pair = line.Split(new[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (pair.Length > 1)
|
||||
{
|
||||
if (!Header.ContainsKey(pair[0]))
|
||||
{
|
||||
Header.Add(pair[0], pair[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bl.GetLump(BinaryStateLump.Input, abort: true, tr =>
|
||||
{
|
||||
IsCountingRerecords = false;
|
||||
|
@ -186,37 +140,6 @@ namespace BizHawk.Client.Common
|
|||
IsCountingRerecords = true;
|
||||
});
|
||||
|
||||
if (preload)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bl.GetLump(BinaryStateLump.Comments, abort: false, tr =>
|
||||
{
|
||||
string line;
|
||||
while ((line = tr.ReadLine()) != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
Comments.Add(line);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bl.GetLump(BinaryStateLump.Subtitles, abort: false, tr =>
|
||||
{
|
||||
string line;
|
||||
while ((line = tr.ReadLine()) != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
Subtitles.AddFromString(line);
|
||||
}
|
||||
}
|
||||
|
||||
Subtitles.Sort();
|
||||
});
|
||||
|
||||
bl.GetLump(BinaryStateLump.SyncSettings, abort: false, tr =>
|
||||
{
|
||||
string line;
|
||||
|
|
|
@ -1,26 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public partial class Bk2Movie : IMovie
|
||||
public partial class Bk2Movie : BasicMovieInfo, IMovie
|
||||
{
|
||||
private Bk2Controller _adapter;
|
||||
|
||||
public Bk2Movie(IMovieSession session, string filename)
|
||||
public Bk2Movie(IMovieSession session, string filename) : base(filename)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filename))
|
||||
{
|
||||
throw filename is null
|
||||
? new ArgumentNullException(paramName: nameof(filename))
|
||||
: new ArgumentException(message: "path cannot be blank", paramName: nameof(filename));
|
||||
}
|
||||
|
||||
Session = session;
|
||||
Filename = filename;
|
||||
Header[HeaderKeys.MovieVersion] = "BizHawk v2.0.0";
|
||||
}
|
||||
|
||||
|
@ -36,20 +25,6 @@ namespace BizHawk.Client.Common
|
|||
|
||||
protected bool MakeBackup { get; set; } = true;
|
||||
|
||||
private string _filename;
|
||||
|
||||
public string Filename
|
||||
{
|
||||
get => _filename;
|
||||
set
|
||||
{
|
||||
_filename = value;
|
||||
Name = Path.GetFileName(Filename);
|
||||
}
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public virtual string PreferredExtension => Extension;
|
||||
|
||||
public const string Extension = "bk2";
|
||||
|
@ -62,52 +37,9 @@ namespace BizHawk.Client.Common
|
|||
return new Bk2LogEntryGenerator(Emulator?.SystemId ?? SystemID, source);
|
||||
}
|
||||
|
||||
public int FrameCount => Log.Count;
|
||||
public override int FrameCount => Log.Count;
|
||||
public int InputLogLength => Log.Count;
|
||||
|
||||
public TimeSpan TimeLength
|
||||
{
|
||||
get
|
||||
{
|
||||
double dblSeconds;
|
||||
|
||||
if (Header.TryGetValue(HeaderKeys.CycleCount, out var numCyclesStr) && Header.TryGetValue(HeaderKeys.ClockRate, out var clockRateStr))
|
||||
{
|
||||
var numCycles = Convert.ToUInt64(numCyclesStr);
|
||||
var clockRate = Convert.ToDouble(clockRateStr, CultureInfo.InvariantCulture);
|
||||
dblSeconds = numCycles / clockRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
var numFrames = (ulong)FrameCount;
|
||||
dblSeconds = numFrames / FrameRate;
|
||||
}
|
||||
|
||||
var seconds = (int)(dblSeconds % 60);
|
||||
var days = seconds / 86400;
|
||||
var hours = seconds / 3600;
|
||||
var minutes = (seconds / 60) % 60;
|
||||
var milliseconds = (int)((dblSeconds - seconds) * 1000);
|
||||
return new TimeSpan(days, hours, minutes, seconds, milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
public double FrameRate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SystemID == VSystemID.Raw.Arcade && Header.TryGetValue(HeaderKeys.VsyncAttoseconds, out var vsyncAttoStr))
|
||||
{
|
||||
const decimal attosInSec = 1000000000000000000;
|
||||
return (double)(attosInSec / Convert.ToUInt64(vsyncAttoStr));
|
||||
}
|
||||
else
|
||||
{
|
||||
return PlatformFrameRates.GetFrameRate(SystemID, IsPal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IStringLog GetLogEntries() => Log;
|
||||
|
||||
public void CopyLog(IEnumerable<string> log)
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public interface IBasicMovieInfo
|
||||
{
|
||||
// Filename of the movie, settable by the client
|
||||
string Filename { get; set; }
|
||||
|
||||
string Name { get; }
|
||||
|
||||
string GameName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of frames that count towards the completion time of the movie
|
||||
/// </summary>
|
||||
int FrameCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual length of time a movie lasts for. For subframe cores, this will be different then the above two options
|
||||
/// </summary>
|
||||
TimeSpan TimeLength { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the frame rate in frames per second for the movie's system.
|
||||
/// </summary>
|
||||
double FrameRate { get; }
|
||||
|
||||
SubtitleList Subtitles { get; }
|
||||
|
||||
IList<string> Comments { get; }
|
||||
|
||||
string SystemID { get; set; }
|
||||
|
||||
ulong Rerecords { get; set; }
|
||||
|
||||
/// <value>either CRC32, MD5, or SHA1, hex-encoded, unprefixed</value>
|
||||
string Hash { get; set; }
|
||||
|
||||
string Author { get; set; }
|
||||
string Core { get; set; }
|
||||
string EmulatorVersion { get; set; }
|
||||
string OriginalEmulatorVersion { get; set; }
|
||||
string FirmwareHash { get; set; }
|
||||
string BoardName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header key value pairs stored in the movie file
|
||||
/// </summary>
|
||||
IDictionary<string, string> HeaderEntries { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Tells the movie to load the contents of Filename
|
||||
/// </summary>
|
||||
/// <returns>Return whether or not the file was successfully loaded</returns>
|
||||
bool Load();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
|
@ -30,7 +29,7 @@ namespace BizHawk.Client.Common
|
|||
|
||||
// TODO: message callback / event handler
|
||||
// TODO: consider other event handlers, switching modes?
|
||||
public interface IMovie
|
||||
public interface IMovie : IBasicMovieInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current movie mode
|
||||
|
@ -41,29 +40,12 @@ namespace BizHawk.Client.Common
|
|||
|
||||
bool Changes { get; }
|
||||
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of frames that count towards the completion time of the movie
|
||||
/// </summary>
|
||||
int FrameCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual length of the input log, should only be used by code that needs the input log length
|
||||
/// specifically, not the frame count
|
||||
/// </summary>
|
||||
int InputLogLength { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual length of time a movie lasts for. For subframe cores, this will be different then the above two options
|
||||
/// </summary>
|
||||
TimeSpan TimeLength { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the frame rate in frames per second for the movie's system.
|
||||
/// </summary>
|
||||
double FrameRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file extension for the current <see cref="IMovie"/> implementation
|
||||
/// </summary>
|
||||
|
@ -74,9 +56,6 @@ namespace BizHawk.Client.Common
|
|||
/// </summary>
|
||||
string SyncSettingsJson { get; set; }
|
||||
|
||||
SubtitleList Subtitles { get; }
|
||||
IList<string> Comments { get; }
|
||||
|
||||
// savestate anchor.
|
||||
string TextSavestate { get; set; }
|
||||
byte[] BinarySavestate { get; set; }
|
||||
|
@ -85,34 +64,11 @@ namespace BizHawk.Client.Common
|
|||
// saveram anchor
|
||||
byte[] SaveRam { get; set; }
|
||||
|
||||
ulong Rerecords { get; set; }
|
||||
bool StartsFromSavestate { get; set; }
|
||||
bool StartsFromSaveRam { get; set; }
|
||||
string GameName { get; set; }
|
||||
string SystemID { get; set; }
|
||||
|
||||
/// <value>either CRC32, MD5, or SHA1, hex-encoded, unprefixed</value>
|
||||
string Hash { get; set; }
|
||||
|
||||
string Author { get; set; }
|
||||
string Core { get; set; }
|
||||
string EmulatorVersion { get; set; }
|
||||
string OriginalEmulatorVersion { get; set; }
|
||||
string FirmwareHash { get; set; }
|
||||
string BoardName { get; set; }
|
||||
string LogKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Loads from the HawkFile the minimal amount of information needed to determine Header info and Movie length.
|
||||
/// This method is intended to be more performant than a full load
|
||||
/// </summary>
|
||||
bool PreLoadHeaderAndLength();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header key value pairs stored in the movie file
|
||||
/// </summary>
|
||||
IDictionary<string, string> HeaderEntries { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Forces the creation of a backup file of the current movie state
|
||||
/// </summary>
|
||||
|
@ -123,15 +79,6 @@ namespace BizHawk.Client.Common
|
|||
/// </summary>
|
||||
ILogEntryGenerator LogGeneratorInstance(IController source);
|
||||
|
||||
// Filename of the movie, settable by the client
|
||||
string Filename { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tells the movie to load the contents of Filename
|
||||
/// </summary>
|
||||
/// <returns>Return whether or not the file was successfully loaded</returns>
|
||||
bool Load(bool preload);
|
||||
|
||||
/// <summary>
|
||||
/// Instructs the movie to save the current contents to Filename
|
||||
/// </summary>
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace BizHawk.Client.Common
|
|||
|
||||
protected override void ClearBeforeLoad()
|
||||
{
|
||||
ClearBk2Fields();
|
||||
base.ClearBeforeLoad();
|
||||
ClearTasprojExtras();
|
||||
}
|
||||
|
||||
|
@ -61,21 +61,18 @@ namespace BizHawk.Client.Common
|
|||
ChangeLog.Clear();
|
||||
}
|
||||
|
||||
protected override void LoadFields(ZipStateLoader bl, bool preload)
|
||||
protected override void LoadFields(ZipStateLoader bl)
|
||||
{
|
||||
LoadBk2Fields(bl, preload);
|
||||
base.LoadFields(bl);
|
||||
|
||||
if (!preload)
|
||||
if (MovieService.IsCurrentTasVersion(Header[HeaderKeys.MovieVersion]))
|
||||
{
|
||||
if (MovieService.IsCurrentTasVersion(Header[HeaderKeys.MovieVersion]))
|
||||
{
|
||||
LoadTasprojExtras(bl);
|
||||
}
|
||||
else
|
||||
{
|
||||
Session.PopupMessage("The current .tasproj is not compatible with this version of BizHawk! .tasproj features failed to load.");
|
||||
Markers.Add(0, StartsFromSavestate ? "Savestate" : "Power on");
|
||||
}
|
||||
LoadTasprojExtras(bl);
|
||||
}
|
||||
else
|
||||
{
|
||||
Session.PopupMessage("The current .tasproj is not compatible with this version of BizHawk! .tasproj features failed to load.");
|
||||
Markers.Add(0, StartsFromSavestate ? "Savestate" : "Power on");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
private readonly IEmulator _emulator;
|
||||
private readonly IMovieSession _movieSession;
|
||||
|
||||
private List<IMovie> _movieList = new List<IMovie>();
|
||||
private List<IBasicMovieInfo> _movieList = new();
|
||||
private bool _sortReverse;
|
||||
private string _sortedCol;
|
||||
|
||||
|
@ -66,8 +66,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private void PlayMovie_Load(object sender, EventArgs e)
|
||||
{
|
||||
_suppressCheckedChanged = true;
|
||||
IncludeSubDirectories.Checked = _config.PlayMovieIncludeSubDir;
|
||||
MatchHashCheckBox.Checked = _config.PlayMovieMatchHash;
|
||||
_suppressCheckedChanged = false;
|
||||
ScanFiles();
|
||||
PreHighlightMovie();
|
||||
TurboCheckbox.Checked = _config.TurboSeek;
|
||||
|
@ -87,7 +89,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
var indices = MovieView.SelectedIndices;
|
||||
if (indices.Count > 0) // Import file if necessary
|
||||
{
|
||||
_mainForm.StartNewMovie(_movieList[MovieView.SelectedIndices[0]], false);
|
||||
var movie = _movieSession.Get(_movieList[MovieView.SelectedIndices[0]].Filename);
|
||||
movie.Load();
|
||||
_mainForm.StartNewMovie(movie, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +103,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
return null;
|
||||
}
|
||||
|
||||
var movie = PreLoadMovieFile(file, force);
|
||||
var movie = LoadMovieInfo(file, force);
|
||||
if (movie == null)
|
||||
{
|
||||
return null;
|
||||
|
@ -138,13 +142,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
return null;
|
||||
}
|
||||
|
||||
private IMovie PreLoadMovieFile(HawkFile hf, bool force)
|
||||
private IBasicMovieInfo LoadMovieInfo(HawkFile hf, bool force)
|
||||
{
|
||||
var movie = _movieSession.Get(hf.CanonicalFullPath);
|
||||
IBasicMovieInfo movie = new BasicMovieInfo(hf.CanonicalFullPath);
|
||||
|
||||
try
|
||||
{
|
||||
movie.PreLoadHeaderAndLength();
|
||||
movie.Load();
|
||||
|
||||
// Don't do this from browse
|
||||
if (movie.Hash == _game.Hash
|
||||
|
@ -338,8 +342,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
Close();
|
||||
}
|
||||
|
||||
private static readonly RigidMultiPredicateSort<IMovie> ColumnSorts
|
||||
= new RigidMultiPredicateSort<IMovie>(new Dictionary<string, Func<IMovie, IComparable>>
|
||||
private static readonly RigidMultiPredicateSort<IBasicMovieInfo> ColumnSorts
|
||||
= new(new Dictionary<string, Func<IBasicMovieInfo, IComparable>>
|
||||
{
|
||||
["File"] = x => Path.GetFileName(x.Filename),
|
||||
["SysID"] = x => x.SystemID,
|
||||
|
@ -499,7 +503,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
var indices = MovieView.SelectedIndices;
|
||||
if (indices.Count > 0)
|
||||
{
|
||||
var form = new EditCommentsForm(_movieList[MovieView.SelectedIndices[0]], _movieSession.ReadOnly);
|
||||
// TODO this will allocate unnecessary memory when this movie is a TasMovie due to TasStateManager
|
||||
var movie = _movieSession.Get(_movieList[MovieView.SelectedIndices[0]].Filename);
|
||||
movie.Load();
|
||||
// TODO movie should be disposed if movie is ITasMovie
|
||||
var form = new EditCommentsForm(movie, _movieSession.ReadOnly);
|
||||
form.Show();
|
||||
}
|
||||
}
|
||||
|
@ -509,7 +517,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
var indices = MovieView.SelectedIndices;
|
||||
if (indices.Count > 0)
|
||||
{
|
||||
using EditSubtitlesForm s = new(DialogController, _movieList[MovieView.SelectedIndices[0]], _config.PathEntries, readOnly: true);
|
||||
// TODO this will allocate unnecessary memory when this movie is a TasMovie due to TasStateManager
|
||||
var movie = _movieSession.Get(_movieList[MovieView.SelectedIndices[0]].Filename);
|
||||
movie.Load();
|
||||
// TODO movie should be disposed if movie is ITasMovie
|
||||
using EditSubtitlesForm s = new(DialogController, movie, _config.PathEntries, readOnly: true);
|
||||
s.Show();
|
||||
}
|
||||
}
|
||||
|
@ -543,6 +555,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private void IncludeSubDirectories_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_suppressCheckedChanged) return;
|
||||
|
||||
_config.PlayMovieIncludeSubDir = IncludeSubDirectories.Checked;
|
||||
ScanFiles();
|
||||
PreHighlightMovie();
|
||||
|
@ -550,6 +564,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private void MatchHashCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_suppressCheckedChanged) return;
|
||||
|
||||
_config.PlayMovieMatchHash = MatchHashCheckBox.Checked;
|
||||
ScanFiles();
|
||||
PreHighlightMovie();
|
||||
|
@ -578,6 +594,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
private bool _programmaticallyChangingStopFrameCheckbox;
|
||||
private bool _suppressCheckedChanged;
|
||||
|
||||
private void StopOnFrameCheckbox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (!_programmaticallyChangingStopFrameCheckbox)
|
||||
|
|
|
@ -7,10 +7,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
public partial class MovieHeaderEditor : Form
|
||||
{
|
||||
private readonly IMovie _movie;
|
||||
private readonly IBasicMovieInfo _movie;
|
||||
private readonly Config _config;
|
||||
|
||||
public MovieHeaderEditor(IMovie movie, Config config)
|
||||
public MovieHeaderEditor(IBasicMovieInfo movie, Config config)
|
||||
{
|
||||
_movie = movie;
|
||||
_config = config;
|
||||
|
|
Loading…
Reference in New Issue