diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index b630006724..15517d209c 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -39,27 +39,51 @@ void SaveStateBase::InputRecordingFreeze() Freeze(g_FrameCount); #ifndef DISABLE_RECORDING - // Explicitly set the frame change tracking variable as to not - // detect loading a savestate as a frame being drawn - g_InputRecordingControls.SetFrameCountTracker(g_FrameCount); + if (g_InputRecording.IsRecordingActive()) + { + // Explicitly set the frame change tracking variable as to not + // detect loading a savestate as a frame being drawn + g_InputRecordingControls.SetFrameCountTracker(g_FrameCount); + + // If the starting savestate has been loaded (on a current-frame recording) and a save-state is loaded while recording + // or replaying the movie it is an undo operation that needs to be tracked. + // + // The reason it's also an undo operation when replaying is because the user can switch modes at any time + // and begin undoing. While this isn't impossible to resolve, it's a separate issue and not of the utmost importance (this is just interesting metadata) + if (IsLoading() && !g_InputRecording.IsSavestateInitializing()) + { + g_InputRecording.GetInputRecordingData().IncrementUndoCount(); + // Reloading a save-state means the internal recording frame counter may need to be adjusted + // Since we persist the g_FrameCount of the beginning of the movie, we can use it to recalculate it + u32 newFrameCounter = g_FrameCount - (g_InputRecording.GetStartingFrame()); + // It is possible for someone to load a savestate outside of the original recording's context + // this should be avoided (hence the log) but I don't think there is a mechanism to reverse loading + // the save-state + // Therefore, the best we can do is limit the frame counter within the min/max of the recording + if (newFrameCounter < 0) + { + newFrameCounter = 0; + recordingConLog(L"[REC]: Warning, you loaded a savestate outside of the bounds of the original recording. This should be avoided. Savestate's framecount has been ignored.\n"); + } + else if (newFrameCounter >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) + { + newFrameCounter = g_InputRecording.GetInputRecordingData().GetTotalFrames(); + recordingConLog(L"[REC]: Warning, you loaded a savestate outside of the bounds of the original recording. This should be avoided. Savestate's framecount has been ignored.\n"); + } + g_InputRecording.SetFrameCounter(newFrameCounter); + } + } + // Loading a save-state is an asynchronous task, if we are playing a recording // that starts from a savestate (not power-on) and the starting (pcsx2 internal) frame // marker has not been set (which comes from the save-state), we initialize it. - // TODO - get rid of the -1 - if (g_InputRecording.GetStartingFrame() == -1 && g_InputRecording.GetInputRecordingData().FromSaveState()) { + if (g_InputRecording.IsSavestateInitializing()) + { g_InputRecording.SetStartingFrame(g_FrameCount); + g_InputRecording.SavestateInitialized(); // TODO - make a function of my own to simplify working with the logging macros recordingConLog(wxString::Format(L"[REC]: Internal Starting Frame: %d\n", g_InputRecording.GetStartingFrame())); } - // Otherwise the starting savestate has been loaded and if loaded a save-state while recording the movie - // it is an undo operation that needs to be tracked. - else if (g_InputRecording.RecordingActive() && IsLoading()) - { - g_InputRecording.GetInputRecordingData().IncrementUndoCount(); - // Reloading a save-state means the internal recording frame counter may need to be adjusted - // Since we persist the g_FrameCount of the beginning of the movie, we can use it to recalculate it - g_InputRecording.SetFrameCounter(g_FrameCount - (g_InputRecording.GetStartingFrame())); - } #endif } @@ -142,7 +166,8 @@ u32 InputRecording::GetStartingFrame() void InputRecording::IncrementFrameCounter() { frameCounter++; - if (state == InputRecordingMode::Recording) { + if (state == InputRecordingMode::Recording) + { GetInputRecordingData().SetTotalFrames(frameCounter); } } @@ -152,16 +177,21 @@ bool InputRecording::IsInterruptFrame() return fInterruptFrame; } -bool InputRecording::IsRecordingReplaying() -{ - return RecordingActive() && state == InputRecordingMode::Replaying; -} - -bool InputRecording::RecordingActive() +bool InputRecording::IsRecordingActive() { return state != InputRecordingMode::NoneActive; } +bool InputRecording::IsSavestateInitializing() +{ + return savestateInitializing; +} + +bool InputRecording::IsRecordingReplaying() +{ + return IsRecordingActive() && state == InputRecordingMode::Replaying; +} + wxString InputRecording::RecordingModeTitleSegment() { switch (state) @@ -192,6 +222,11 @@ void InputRecording::RecordModeToggle() } } +void InputRecording::SavestateInitialized() +{ + savestateInitializing = false; +} + void InputRecording::SetFrameCounter(u32 newFrameCounter) { frameCounter = newFrameCounter; @@ -210,7 +245,8 @@ void InputRecording::Stop() { // Reset the frame counter when starting a new recording frameCounter = 0; - startingFrame = -1; + startingFrame = 0; + savestateInitializing = false; state = InputRecordingMode::NoneActive; if (inputRecordingData.Close()) { @@ -220,6 +256,7 @@ void InputRecording::Stop() bool InputRecording::Create(wxString FileName, bool fromSaveState, wxString authorName) { + savestateInitializing = fromSaveState; if (!inputRecordingData.OpenNew(FileName, fromSaveState)) { return false; @@ -232,6 +269,7 @@ bool InputRecording::Create(wxString FileName, bool fromSaveState, wxString auth { inputRecordingData.GetHeader().SetAuthor(authorName); } + // Set Game Name inputRecordingData.GetHeader().SetGameName(resolveGameName()); // Write header contents @@ -243,7 +281,7 @@ bool InputRecording::Create(wxString FileName, bool fromSaveState, wxString auth bool InputRecording::Play(wxString fileName) { - if (RecordingActive()) + if (IsRecordingActive()) Stop(); if (!inputRecordingData.OpenExisting(fileName)) @@ -259,21 +297,17 @@ bool InputRecording::Play(wxString fileName) inputRecordingData.Close(); return false; } - FILE* ssFileCheck = wxFopen(inputRecordingData.GetFilename() + "_SaveState.p2s", "r"); - if (ssFileCheck == NULL) + if (!wxFileExists(inputRecordingData.GetFilename() + "_SaveState.p2s")) { recordingConLog(wxString::Format("[REC]: Could not locate savestate file at location - %s_SaveState.p2s\n", inputRecordingData.GetFilename())); inputRecordingData.Close(); return false; } - fclose(ssFileCheck); + savestateInitializing = true; StateCopy_LoadFromFile(inputRecordingData.GetFilename() + "_SaveState.p2s"); } else - { - g_InputRecordingControls.Resume(); sApp.SysExecute(); - } // Check if the current game matches with the one used to make the original recording if (!g_Conf->CurrentIso.IsEmpty()) diff --git a/pcsx2/Recording/InputRecording.h b/pcsx2/Recording/InputRecording.h index 63c2ea8925..243a9c1846 100644 --- a/pcsx2/Recording/InputRecording.h +++ b/pcsx2/Recording/InputRecording.h @@ -41,8 +41,15 @@ public: bool IsInterruptFrame(); // If there is currently an input recording being played back or actively being recorded - bool RecordingActive(); - bool IsRecordingReplaying(); + bool IsRecordingActive(); + + // Whether or not the recording's initial save state has yet to be loaded or saved and + // the rest of the recording can be initialized + // This is not applicable to recordings from a "power-on" state + bool IsSavestateInitializing(); + + // If there is currently an input recording being played back + bool IsRecordingReplaying(); // String representation of the current recording mode to be interpolated into the title wxString RecordingModeTitleSegment(); @@ -50,8 +57,12 @@ public: // Switches between recording and replaying the active input recording file void RecordModeToggle(); + // Mark the recording's initial savestate as having been loaded or saved successfully + void SavestateInitialized(); + // Set the running frame counter for the input recording to an arbitrary value void SetFrameCounter(u32 newFrameCounter); + // Store the starting internal PCSX2 g_FrameCount value void SetStartingFrame(u32 newStartingFrame); @@ -65,18 +76,20 @@ public: void Stop(); private: - enum class InputRecordingMode { + enum class InputRecordingMode + { NoneActive, Recording, Replaying, }; + // DEPRECATED: Slated for removal bool fInterruptFrame = false; - InputRecordingFile inputRecordingData; + bool savestateInitializing = false; + u32 startingFrame = 0; InputRecordingMode state = InputRecording::InputRecordingMode::NoneActive; u32 frameCounter = 0; - u32 startingFrame = -1; // Resolve the name and region of the game currently loaded using the GameDB // If the game cannot be found in the DB, the fallback is the ISO filename diff --git a/pcsx2/Recording/InputRecordingControls.cpp b/pcsx2/Recording/InputRecordingControls.cpp index da476ee2ec..fffbf15df2 100644 --- a/pcsx2/Recording/InputRecordingControls.cpp +++ b/pcsx2/Recording/InputRecordingControls.cpp @@ -39,18 +39,23 @@ void InputRecordingControls::HandleFrameAdvanceAndPausing() // As a safeguard, use the global g_FrameCount to know when the frame counter has truly changed. // // NOTE - Do not mutate g_FrameCount or use it's value to set the input recording's internal frame counter - if (frameCountTracker != g_FrameCount) { + if (frameCountTracker != g_FrameCount) + { frameCountTracker = g_FrameCount; g_InputRecording.IncrementFrameCounter(); - } else { - if (pauseEmulation) { + } + else + { + if (pauseEmulation) + { emulationCurrentlyPaused = true; CoreThread.PauseSelf(); } return; } - if (g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) { + if (g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) + { pauseEmulation = true; } @@ -62,7 +67,8 @@ void InputRecordingControls::HandleFrameAdvanceAndPausing() } // Pause emulation if we need to (either due to frame advancing, or pause has been explicitly toggled on) - if (pauseEmulation && CoreThread.IsOpen() && CoreThread.IsRunning()) { + if (pauseEmulation && CoreThread.IsOpen() && CoreThread.IsRunning()) + { emulationCurrentlyPaused = true; CoreThread.PauseSelf(); } @@ -70,7 +76,8 @@ void InputRecordingControls::HandleFrameAdvanceAndPausing() void InputRecordingControls::ResumeCoreThreadIfStarted() { - if (resumeEmulation && CoreThread.IsOpen() && CoreThread.IsPaused()) { + if (resumeEmulation && CoreThread.IsOpen() && CoreThread.IsPaused()) + { CoreThread.Resume(); resumeEmulation = false; emulationCurrentlyPaused = false; @@ -81,7 +88,7 @@ void InputRecordingControls::FrameAdvance() { if (g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) { - g_InputRecording.RecordModeToggle(); + g_InputRecording.RecordModeToggle(); return; } frameAdvanceMarker = g_InputRecording.GetFrameCounter(); @@ -102,11 +109,13 @@ void InputRecordingControls::Pause() void InputRecordingControls::PauseImmediately() { - if (CoreThread.IsPaused()) { + if (CoreThread.IsPaused()) + { return; } Pause(); - if (pauseEmulation && CoreThread.IsOpen() && CoreThread.IsRunning()) { + if (pauseEmulation && CoreThread.IsOpen() && CoreThread.IsRunning()) + { emulationCurrentlyPaused = true; CoreThread.PauseSelf(); } @@ -114,8 +123,9 @@ void InputRecordingControls::PauseImmediately() void InputRecordingControls::Resume() { - if (g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) { - g_InputRecording.RecordModeToggle(); + if (g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) + { + g_InputRecording.RecordModeToggle(); return; } pauseEmulation = false; @@ -129,8 +139,9 @@ void InputRecordingControls::SetFrameCountTracker(u32 newFrame) void InputRecordingControls::TogglePause() { - if (pauseEmulation && g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) { - g_InputRecording.RecordModeToggle(); + if (pauseEmulation && g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) + { + g_InputRecording.RecordModeToggle(); return; } pauseEmulation = !pauseEmulation; diff --git a/pcsx2/Recording/InputRecordingControls.h b/pcsx2/Recording/InputRecordingControls.h index 9d3924d7a0..7a87523360 100644 --- a/pcsx2/Recording/InputRecordingControls.h +++ b/pcsx2/Recording/InputRecordingControls.h @@ -38,26 +38,26 @@ public: // Resume emulation (incase the emulation is currently paused) and pause after a single frame has passed void FrameAdvance(); - // Returns true if the input recording has been paused, which can occur: - // - After a single frame has passed after InputRecordingControls::FrameAdvance - // - Explicitly paused via InputRecordingControls::TogglePause - bool IsRecordingPaused(); + // Returns true if the input recording has been paused, which can occur: + // - After a single frame has passed after InputRecordingControls::FrameAdvance + // - Explicitly paused via InputRecordingControls::TogglePause + bool IsRecordingPaused(); // Pause emulation at the next available Vsync void Pause(); // Pause emulation immediately, not waiting for the next Vsync void PauseImmediately(); // Resume emulation when the next pcsx2 App event is handled void Resume(); - void SetFrameCountTracker(u32 newFrame); + void SetFrameCountTracker(u32 newFrame); // Alternates emulation between a paused and unpaused state void TogglePause(); private: - // Indicates if the input recording controls have explicitly paused emulation or not - bool emulationCurrentlyPaused = false; - // Indicates on the next VSync if we are frame advancing, this value - // and should be cleared once a single frame has passed - bool frameAdvancing = false; + // Indicates if the input recording controls have explicitly paused emulation or not + bool emulationCurrentlyPaused = false; + // Indicates on the next VSync if we are frame advancing, this value + // and should be cleared once a single frame has passed + bool frameAdvancing = false; // The input recording frame that frame advancing began on u32 frameAdvanceMarker = 0; // Used to detect if the internal PCSX2 g_FrameCount has changed diff --git a/pcsx2/Recording/InputRecordingFile.cpp b/pcsx2/Recording/InputRecordingFile.cpp index 739f0a0214..a5910825c7 100644 --- a/pcsx2/Recording/InputRecordingFile.cpp +++ b/pcsx2/Recording/InputRecordingFile.cpp @@ -156,7 +156,7 @@ bool InputRecordingFile::OpenNew(const wxString path, bool fromSavestate) sApp.SysExecute(); return true; } - return open(path, true); + return false; } bool InputRecordingFile::OpenExisting(const wxString path) diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 45eb842edd..cc997a3403 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -1059,7 +1059,7 @@ void Pcsx2App::OnProgramLogClosed( wxWindowID id ) void Pcsx2App::OnMainFrameClosed( wxWindowID id ) { #ifndef DISABLE_RECORDING - if (g_InputRecording.RecordingActive()) + if (g_InputRecording.IsRecordingActive()) { g_InputRecording.Stop(); } diff --git a/pcsx2/gui/FrameForGS.cpp b/pcsx2/gui/FrameForGS.cpp index 0a432d533e..ec3612b2b5 100644 --- a/pcsx2/gui/FrameForGS.cpp +++ b/pcsx2/gui/FrameForGS.cpp @@ -732,7 +732,7 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt ) #ifndef DISABLE_RECORDING wxString title; wxString movieMode; - if (g_InputRecording.RecordingActive()) + if (g_InputRecording.IsRecordingActive()) { title = templates.RecordingTemplate; title.Replace(L"${frame}", pxsFmt(L"%d", g_InputRecording.GetFrameCounter())); diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index ae27bd6b8f..666d6130db 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -32,7 +32,7 @@ #ifndef DISABLE_RECORDING # include "Recording/InputRecording.h" -# include "Recording/InputRecordingControls.h" +# include "Recording/InputRecordingControls.h" # include "Recording/VirtualPad.h" #endif @@ -473,7 +473,6 @@ void MainEmuFrame::Menu_EnableBackupStates_Click( wxCommandEvent& ) // (1st save after the toggle keeps the old pre-toggle value).. // wonder what that means for all the other menu checkboxes which only use AppSaveSettings... (avih) AppApplySettings(); - AppSaveSettings(); } @@ -530,7 +529,7 @@ void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent& event) else { //Properly close any currently loaded recording file before disabling - if (g_InputRecording.RecordingActive()) + if (g_InputRecording.IsRecordingActive()) Menu_Recording_Stop_Click(event); GetMenuBar()->Remove(TopLevelMenu_InputRecording); // Always turn controller logs off, but never turn it on by default @@ -876,16 +875,16 @@ void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event) const bool initiallyPaused = g_InputRecordingControls.IsRecordingPaused(); if (!initiallyPaused) g_InputRecordingControls.PauseImmediately(); - NewRecordingFrame* NewRecordingFrame = wxGetApp().GetNewRecordingFramePtr(); - if (NewRecordingFrame) + NewRecordingFrame* newRecordingFrame = wxGetApp().GetNewRecordingFramePtr(); + if (newRecordingFrame) { - if (NewRecordingFrame->ShowModal() == wxID_CANCEL) + if (newRecordingFrame->ShowModal() == wxID_CANCEL) { if (!initiallyPaused) g_InputRecordingControls.Resume(); return; } - if (!g_InputRecording.Create(NewRecordingFrame->GetFile(), !NewRecordingFrame->GetFrom(), NewRecordingFrame->GetAuthor())) + if (!g_InputRecording.Create(newRecordingFrame->GetFile(), !newRecordingFrame->GetFrom(), newRecordingFrame->GetAuthor())) { if (!initiallyPaused) g_InputRecordingControls.Resume(); @@ -913,7 +912,7 @@ void MainEmuFrame::Menu_Recording_Play_Click(wxCommandEvent &event) } wxString path = openFileDialog.GetPath(); - const bool recordingLoaded = g_InputRecording.RecordingActive(); + const bool recordingLoaded = g_InputRecording.IsRecordingActive(); if (!g_InputRecording.Play(path)) { if (recordingLoaded)