diff --git a/src/BizHawk.Client.Common/movie/BasicMovieInfo.cs b/src/BizHawk.Client.Common/movie/BasicMovieInfo.cs new file mode 100644 index 0000000000..2d6736e426 --- /dev/null +++ b/src/BizHawk.Client.Common/movie/BasicMovieInfo.cs @@ -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 Comments { get; } = new List(); + + 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 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++; + } + } + }); + } + } +} diff --git a/src/BizHawk.Client.Common/movie/MovieSession.cs b/src/BizHawk.Client.Common/movie/MovieSession.cs index 71335f79fc..cc86797627 100644 --- a/src/BizHawk.Client.Common/movie/MovieSession.cs +++ b/src/BizHawk.Client.Common/movie/MovieSession.cs @@ -188,7 +188,7 @@ namespace BizHawk.Client.Common return true; } - /// is and . does not match . + /// is and . does not match . public void QueueNewMovie(IMovie movie, bool record, string systemId, IDictionary 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); } } -} \ No newline at end of file +} diff --git a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs index 07e4245eff..afb9e7dda0 100644 --- a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs +++ b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.HeaderApi.cs @@ -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 HeaderEntries => Header; - - public SubtitleList Subtitles { get; } = new SubtitleList(); - public IList Comments { get; } = new List(); - 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; } diff --git a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.IO.cs b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.IO.cs index eaa65c0655..4441908e01 100644 --- a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.IO.cs +++ b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.IO.cs @@ -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; diff --git a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.cs b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.cs index 15bc755428..2208c9efeb 100755 --- a/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.cs +++ b/src/BizHawk.Client.Common/movie/bk2/Bk2Movie.cs @@ -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 log) diff --git a/src/BizHawk.Client.Common/movie/interfaces/IBasicMovieInfo.cs b/src/BizHawk.Client.Common/movie/interfaces/IBasicMovieInfo.cs new file mode 100644 index 0000000000..fd2fa02ef5 --- /dev/null +++ b/src/BizHawk.Client.Common/movie/interfaces/IBasicMovieInfo.cs @@ -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; } + + /// + /// Gets the total number of frames that count towards the completion time of the movie + /// + int FrameCount { get; } + + /// + /// Gets the actual length of time a movie lasts for. For subframe cores, this will be different then the above two options + /// + TimeSpan TimeLength { get; } + + /// + /// Gets the frame rate in frames per second for the movie's system. + /// + double FrameRate { get; } + + SubtitleList Subtitles { get; } + + IList Comments { get; } + + string SystemID { get; set; } + + ulong Rerecords { get; set; } + + /// either CRC32, MD5, or SHA1, hex-encoded, unprefixed + 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; } + + /// + /// Gets the header key value pairs stored in the movie file + /// + IDictionary HeaderEntries { get; } + + /// + /// Tells the movie to load the contents of Filename + /// + /// Return whether or not the file was successfully loaded + bool Load(); + } +} diff --git a/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs b/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs index 6f7c17119c..d6e1b64933 100644 --- a/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs +++ b/src/BizHawk.Client.Common/movie/interfaces/IMovie.cs @@ -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 { /// /// Gets the current movie mode @@ -41,29 +40,12 @@ namespace BizHawk.Client.Common bool Changes { get; } - string Name { get; } - - /// - /// Gets the total number of frames that count towards the completion time of the movie - /// - int FrameCount { get; } - /// /// 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 /// int InputLogLength { get; } - /// - /// Gets the actual length of time a movie lasts for. For subframe cores, this will be different then the above two options - /// - TimeSpan TimeLength { get; } - - /// - /// Gets the frame rate in frames per second for the movie's system. - /// - double FrameRate { get; } - /// /// Gets the file extension for the current implementation /// @@ -74,9 +56,6 @@ namespace BizHawk.Client.Common /// string SyncSettingsJson { get; set; } - SubtitleList Subtitles { get; } - IList 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; } - /// either CRC32, MD5, or SHA1, hex-encoded, unprefixed - 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; } - /// - /// 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 - /// - bool PreLoadHeaderAndLength(); - - /// - /// Gets the header key value pairs stored in the movie file - /// - IDictionary HeaderEntries { get; } - /// /// Forces the creation of a backup file of the current movie state /// @@ -123,15 +79,6 @@ namespace BizHawk.Client.Common /// ILogEntryGenerator LogGeneratorInstance(IController source); - // Filename of the movie, settable by the client - string Filename { get; set; } - - /// - /// Tells the movie to load the contents of Filename - /// - /// Return whether or not the file was successfully loaded - bool Load(bool preload); - /// /// Instructs the movie to save the current contents to Filename /// diff --git a/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs b/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs index ef3e4e7d1e..af601e837a 100644 --- a/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs +++ b/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs @@ -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"); } } diff --git a/src/BizHawk.Client.EmuHawk/movie/PlayMovie.cs b/src/BizHawk.Client.EmuHawk/movie/PlayMovie.cs index 2d789b090f..520263c849 100644 --- a/src/BizHawk.Client.EmuHawk/movie/PlayMovie.cs +++ b/src/BizHawk.Client.EmuHawk/movie/PlayMovie.cs @@ -27,7 +27,7 @@ namespace BizHawk.Client.EmuHawk private readonly IEmulator _emulator; private readonly IMovieSession _movieSession; - private List _movieList = new List(); + private List _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 ColumnSorts - = new RigidMultiPredicateSort(new Dictionary> + private static readonly RigidMultiPredicateSort ColumnSorts + = new(new Dictionary> { ["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) diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/HeaderEditor.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/HeaderEditor.cs index e32d31ca12..9952d94013 100644 --- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/HeaderEditor.cs +++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/HeaderEditor.cs @@ -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;