diff --git a/BizHawk.Client.Common/movie/MovieSession.cs b/BizHawk.Client.Common/movie/MovieSession.cs index 5858bfe007..d66124632c 100644 --- a/BizHawk.Client.Common/movie/MovieSession.cs +++ b/BizHawk.Client.Common/movie/MovieSession.cs @@ -2,6 +2,12 @@ using System.IO; using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES; +using BizHawk.Emulation.Cores.Nintendo.NES; +using BizHawk.Emulation.Cores.Nintendo.SNES9X; +using BizHawk.Emulation.Cores.Nintendo.SNES; + +using BizHawk.Client.Common; namespace BizHawk.Client.Common { @@ -17,6 +23,21 @@ namespace BizHawk.Client.Common MovieControllerAdapter = MovieService.DefaultInstance.LogGeneratorInstance().MovieControllerAdapter; } + /// + /// When initializing a movie, it will be stored here until Rom processes have been completed, then it will be moved to the Movie property + /// If an existing movie is still active, it will remain in the Movie property while the new movie is queued + /// + public IMovie QueuedMovie { get; set; } + + // This wrapper but the logic could change, don't make the client code understand these details + public bool MovieIsQueued + { + get { return QueuedMovie != null; } + } + + //TODO: this shouldn't be a hack anymore, the contents of this should be reliably in QueuedMovie + public object SyncSettingsHack { get; set; } + public MultitrackRecording MultiTrack { get { return _multiTrack; } } public IMovieController MovieControllerAdapter{ get; set; } @@ -149,6 +170,7 @@ namespace BizHawk.Client.Common ModeChangedCallback(); } + // Movie Refactor TODO: delete me, any code calling this is poorly designed public bool MovieLoad() { MovieControllerAdapter = Movie.LogGeneratorInstance().MovieControllerAdapter; @@ -387,5 +409,95 @@ namespace BizHawk.Client.Common MessageCallback("MultiTrack cannot be enabled while not recording."); } } + + // Movie Load Refactor TODO: a better name + /// + /// Sets the Movie property with the QueuedMovie, clears the queued movie, and starts the new movie + /// + public void RunQueuedMovie(bool recordMode) + { + Movie = QueuedMovie; + QueuedMovie = null; + + if (Movie.IsRecording) + { + Movie.StartNewRecording(); + ReadOnly = false; + } + else + { + Movie.StartNewPlayback(); + } + } + + public void QueueNewMovie(IMovie movie, bool record) + { + 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(); + if (movie.SystemID != Global.Emulator.SystemId) + { + MessageCallback("Movie does not match the currently loaded system, unable to load"); + return; + } + } + + //If a movie is already loaded, save it before starting a new movie + if (Global.MovieSession.Movie.IsActive && !string.IsNullOrEmpty(Global.MovieSession.Movie.Filename)) + { + Global.MovieSession.Movie.Save(); + } + + // Note: this populates MovieControllerAdapter's Type with the approparite controller + // Don't set it to a movie instance of the adapter or you will lose the definition! + InputManager.RewireInputChain(); + + if (!record && Global.Emulator.SystemId == "NES") // For NES we need special logic since the movie will drive which core to load + { + var quicknesName = ((CoreAttributes)Attribute.GetCustomAttribute(typeof(QuickNES), typeof(CoreAttributes))).CoreName; + var neshawkName = ((CoreAttributes)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttributes))).CoreName; + + // If either is specified use that, else use whatever is currently set + if (Global.MovieSession.Movie.Core == quicknesName) + { + Global.Config.NES_InQuickNES = true; + } + else if (Global.MovieSession.Movie.Core == neshawkName) + { + Global.Config.NES_InQuickNES = false; + } + } + else if (!record && Global.Emulator.SystemId == "SNES") // ditto with snes9x vs bsnes + { + var snes9xName = ((CoreAttributes)Attribute.GetCustomAttribute(typeof(Snes9x), typeof(CoreAttributes))).CoreName; + var bsnesName = ((CoreAttributes)Attribute.GetCustomAttribute(typeof(LibsnesCore), typeof(CoreAttributes))).CoreName; + + if (Global.MovieSession.Movie.Core == snes9xName) + { + Global.Config.SNES_InSnes9x = true; + } + else + { + Global.Config.SNES_InSnes9x = false; + } + } + + var s = Global.MovieSession.Movie.SyncSettingsJson; + if (!string.IsNullOrWhiteSpace(s)) + { + SyncSettingsHack = ConfigService.LoadWithType(s); + } + + if (record) // This is a hack really, we need to set the movie to its propert state so that it will be considered active later + { + movie.SwitchToRecord(); + } + else + { + movie.SwitchToPlay(); + } + + QueuedMovie = movie; + } } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 751dab4108..2421e8273e 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -300,11 +300,6 @@ namespace BizHawk.Client.EmuHawk private void CloseRomMenuItem_Click(object sender, EventArgs e) { - if (Global.MovieSession.Movie.IsActive) - { - Global.MovieSession.Movie.Stop(); - } - CloseRom(); } @@ -2377,7 +2372,7 @@ namespace BizHawk.Client.EmuHawk } else { - StopMovieThenLoadRom(CurrentlyOpenRom); + LoadRom(CurrentlyOpenRom); } string errorMsg; @@ -2399,7 +2394,7 @@ namespace BizHawk.Client.EmuHawk } else { - StopMovieThenLoadRom(filePaths[0]); + LoadRom(filePaths[0]); } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Movie.cs b/BizHawk.Client.EmuHawk/MainForm.Movie.cs index 15b88cf5f7..bc4512a304 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Movie.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Movie.cs @@ -14,116 +14,27 @@ namespace BizHawk.Client.EmuHawk { public void StartNewMovie(IMovie movie, bool record) { - 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(); - } + Global.MovieSession.QueueNewMovie(movie, record); - if (movie.SystemID != Global.Emulator.SystemId) - { - GlobalWin.OSD.AddMessage("Movie does not match the currently loaded system, unable to load"); - return; - } - - //If a movie is already loaded, save it before starting a new movie - if (Global.MovieSession.Movie.IsActive && !string.IsNullOrEmpty(Global.MovieSession.Movie.Filename)) - { - Global.MovieSession.Movie.Save(); - } - - Global.MovieSession = new MovieSession - { - Movie = movie, - MovieControllerAdapter = movie.LogGeneratorInstance().MovieControllerAdapter, - MessageCallback = GlobalWin.OSD.AddMessage, - AskYesNoCallback = StateErrorAskUser, - PauseCallback = PauseEmulator, - ModeChangedCallback = SetMainformMovieInfo - }; - - InputManager.RewireInputChain(); - - if (!record) - { - Global.MovieSession.MovieLoad(); // TODO this loads it a 2nd time, ugh - } - - try - { - if (!record && Global.Emulator.SystemId == "NES") // For NES we need special logic since the movie will drive which core to load - { - var quicknesName = ((CoreAttributes)Attribute.GetCustomAttribute(typeof(QuickNES), typeof(CoreAttributes))).CoreName; - var neshawkName = ((CoreAttributes)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttributes))).CoreName; - - // If either is specified use that, else use whatever is currently set - if (Global.MovieSession.Movie.Core == quicknesName) - { - Global.Config.NES_InQuickNES = true; - } - else if (Global.MovieSession.Movie.Core == neshawkName) - { - Global.Config.NES_InQuickNES = false; - } - } - else if (!record && Global.Emulator.SystemId == "SNES") // ditto with snes9x vs bsnes - { - var snes9xName = ((CoreAttributes)Attribute.GetCustomAttribute(typeof(Snes9x), typeof(CoreAttributes))).CoreName; - var bsnesName = ((CoreAttributes)Attribute.GetCustomAttribute(typeof(LibsnesCore), typeof(CoreAttributes))).CoreName; - - if (Global.MovieSession.Movie.Core == snes9xName) - { - Global.Config.SNES_InSnes9x = true; - } - else - { - Global.Config.SNES_InSnes9x = false; - } - } - - var s = Global.MovieSession.Movie.SyncSettingsJson; - if (!string.IsNullOrWhiteSpace(s)) - { - _syncSettingsHack = ConfigService.LoadWithType(s); - } - - if (record) // This is a hack really, we need to set the movie to its propert state so that it will be considered active later - { - Global.MovieSession.Movie.SwitchToRecord(); - } - else - { - Global.MovieSession.Movie.SwitchToRecord(); - } - - LoadRom(GlobalWin.MainForm.CurrentlyOpenRom); - } - finally - { - // ensure subsequent calls to LoadRom won't get the settings object created here - this._syncSettingsHack = null; - } + LoadRom(GlobalWin.MainForm.CurrentlyOpenRom); Global.Config.RecentMovies.Add(movie.Filename); if (Global.MovieSession.Movie.StartsFromSavestate) { if (Global.MovieSession.Movie.TextSavestate != null) + { Global.Emulator.LoadStateText(new StringReader(Global.MovieSession.Movie.TextSavestate)); + } else + { Global.Emulator.LoadStateBinary(new BinaryReader(new MemoryStream(Global.MovieSession.Movie.BinarySavestate, false))); + } Global.Emulator.ResetCounters(); } - if (record) - { - Global.MovieSession.Movie.StartNewRecording(); - Global.MovieSession.ReadOnly = false; - } - else - { - Global.MovieSession.Movie.StartNewPlayback(); - } + Global.MovieSession.RunQueuedMovie(record); SetMainformMovieInfo(); UpdateStatusSlots(); @@ -156,11 +67,12 @@ namespace BizHawk.Client.EmuHawk SetWindowText(); } + // Movie Refactor TODO: this needs to be considered, and adapated to the queue system public void RestartMovie() { if (Global.MovieSession.Movie.IsActive) { - StopMovieThenLoadRom(CurrentlyOpenRom); + LoadRom(CurrentlyOpenRom); if (Global.MovieSession.Movie.StartsFromSavestate) { // TODO: why does this code exist twice?? diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index d133aaa170..2dca3e7ce5 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -262,7 +262,7 @@ namespace BizHawk.Client.EmuHawk if (cmdRom != null) { // Commandline should always override auto-load - StopMovieThenLoadRom(cmdRom); + LoadRom(cmdRom); if (Global.Game == null) { MessageBox.Show("Failed to load " + cmdRom + " specified on commandline"); @@ -818,7 +818,7 @@ namespace BizHawk.Client.EmuHawk public void RebootCore() { - StopMovieThenLoadRom(CurrentlyOpenRom); + LoadRom(CurrentlyOpenRom); } public void PauseEmulator() @@ -1208,8 +1208,6 @@ namespace BizHawk.Client.EmuHawk // Resources Bitmap StatusBarDiskLightOnImage, StatusBarDiskLightOffImage; - private object _syncSettingsHack; - #endregion #region Private methods @@ -1569,7 +1567,7 @@ namespace BizHawk.Client.EmuHawk private void LoadRomFromRecent(string rom) { - if (!StopMovieThenLoadRom(rom)) + if (!LoadRom(rom)) { Global.Config.RecentRoms.HandleLoadError(rom); } @@ -1833,7 +1831,7 @@ namespace BizHawk.Client.EmuHawk var file = new FileInfo(ofd.FileName); Global.Config.LastRomPath = file.DirectoryName; _lastOpenRomFilter = ofd.FilterIndex; - StopMovieThenLoadRom(file.FullName); + LoadRom(file.FullName); } private void CoreSyncSettings(object sender, RomLoader.SettingsLoadArgs e) @@ -1842,7 +1840,7 @@ namespace BizHawk.Client.EmuHawk // A movie is loaded, then load rom is called, which closes the current rom which closes the current movie (which is the movie just loaded) // As such the movie is "inactive". So instead we load the movie and populate the _syncSettingsHack // Then let the rom logic work its magic, then use it here, as such it will be null unless a movie invoked the load rom call - e.Settings = _syncSettingsHack ?? Global.Config.GetCoreSyncSettings(e.Core); + e.Settings = Global.MovieSession.SyncSettingsHack ?? Global.Config.GetCoreSyncSettings(e.Core); } private static void CoreSettings(object sender, RomLoader.SettingsLoadArgs e) @@ -3024,6 +3022,8 @@ namespace BizHawk.Client.EmuHawk private void ShowLoadError(object sender, RomLoader.RomErrorArgs e) { + Global.MovieSession.SyncSettingsHack = null; // ensure subsequent calls to LoadRom won't get the settings object created here + if (e.Type == RomLoader.LoadErrorType.MissingFirmware) { var result = MessageBox.Show( @@ -3064,17 +3064,6 @@ namespace BizHawk.Client.EmuHawk return platformChooser.PlatformChoice; } - // TODO: a better name for this method, but this is the one that should be called, in general - public bool StopMovieThenLoadRom(string path, bool? deterministicemulation = null) - { - if (Global.MovieSession.Movie.IsActive) - { - Global.MovieSession.Movie.Stop(); - } - - return LoadRom(path, deterministicemulation); - } - // Still needs a good bit of refactoring public bool LoadRom(string path, bool? deterministicemulation = null) { @@ -3097,7 +3086,6 @@ namespace BizHawk.Client.EmuHawk }; loader.OnLoadError += ShowLoadError; - loader.OnLoadSettings += CoreSettings; loader.OnLoadSyncSettings += CoreSyncSettings; @@ -3106,8 +3094,6 @@ namespace BizHawk.Client.EmuHawk // the new settings objects CommitCoreSettingsToConfig(); // adelikat: I Think by reordering things, this isn't necessary anymore CloseGame(); - - //Global.Emulator.Dispose(); // CloseGame() already killed and disposed the emulator; this is killing the new one; that's bad var nextComm = CreateCoreComm(); CoreFileProvider.SyncCoreCommInputSignals(nextComm); @@ -3116,12 +3102,9 @@ namespace BizHawk.Client.EmuHawk if (result) { - if (loader.LoadedEmulator is TI83) + if (loader.LoadedEmulator is TI83 && Global.Config.TI83autoloadKeyPad) { - if (Global.Config.TI83autoloadKeyPad) - { - GlobalWin.Tools.Load(); - } + GlobalWin.Tools.Load(); } Global.Emulator = loader.LoadedEmulator; @@ -3153,8 +3136,6 @@ namespace BizHawk.Client.EmuHawk } } - SetWindowText(); - Global.Rewinder.ResetRewindBuffer(); if (Global.Emulator.CoreComm.RomStatusDetails == null && loader.Rom != null) @@ -3180,7 +3161,9 @@ namespace BizHawk.Client.EmuHawk Global.Config.RecentRoms.Add(loader.CanonicalFullPath); JumpLists.AddRecentItem(loader.CanonicalFullPath); - if (File.Exists(PathManager.SaveRamPath(loader.Game))) + + // Don't load Save Ram if a movie is being loaded + if (!Global.MovieSession.MovieIsQueued && File.Exists(PathManager.SaveRamPath(loader.Game))) { LoadSaveRam(); } @@ -3195,6 +3178,7 @@ namespace BizHawk.Client.EmuHawk } } + SetWindowText(); CurrentlyOpenRom = loader.CanonicalFullPath; HandlePlatformMenus(); _stateSlots.Clear(); @@ -3303,6 +3287,11 @@ namespace BizHawk.Client.EmuHawk StopAv(); CommitCoreSettingsToConfig(); + if (Global.MovieSession.Movie.IsActive) // Note: this must be called after CommitCoreSettingsToConfig() + { + StopMovie(true); + } + Global.Emulator.Dispose(); Global.CoreComm = CreateCoreComm(); diff --git a/BizHawk.Client.EmuHawk/Program.cs b/BizHawk.Client.EmuHawk/Program.cs index 0ce3aa6fac..14f3823c0c 100644 --- a/BizHawk.Client.EmuHawk/Program.cs +++ b/BizHawk.Client.EmuHawk/Program.cs @@ -241,7 +241,7 @@ namespace BizHawk.Client.EmuHawk void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e) { - (MainForm as MainForm).StopMovieThenLoadRom(e.CommandLine[0]); + (MainForm as MainForm).LoadRom(e.CommandLine[0]); } protected override void OnCreateMainForm() diff --git a/BizHawk.Client.EmuHawk/tools/GB/DualGBXMLCreator.cs b/BizHawk.Client.EmuHawk/tools/GB/DualGBXMLCreator.cs index 73033db095..e16bf5a50b 100644 --- a/BizHawk.Client.EmuHawk/tools/GB/DualGBXMLCreator.cs +++ b/BizHawk.Client.EmuHawk/tools/GB/DualGBXMLCreator.cs @@ -169,7 +169,7 @@ namespace BizHawk.Client.EmuHawk DialogResult = DialogResult.OK; Close(); - GlobalWin.MainForm.StopMovieThenLoadRom(textBoxOutputDir.Text); + GlobalWin.MainForm.LoadRom(textBoxOutputDir.Text); } } diff --git a/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Client.cs b/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Client.cs index d979fb3d6c..9c34491791 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Client.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Client.cs @@ -140,7 +140,7 @@ namespace BizHawk.Client.EmuHawk )] public static void OpenRom(string path) { - GlobalWin.MainForm.StopMovieThenLoadRom(path); + GlobalWin.MainForm.LoadRom(path); } [LuaMethodAttributes(