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:
Moritz Bender 2023-04-20 10:43:02 +02:00 committed by GitHub
parent 9b278d3130
commit bc16a2cdaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 386 additions and 273 deletions

View File

@ -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++;
}
}
});
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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; }

View File

@ -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;

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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");
}
}

View File

@ -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)

View File

@ -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;