diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index af2bc36331..3459752b17 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -39,7 +39,14 @@ void SaveStateBase::InputRecordingFreeze() Freeze(g_FrameCount); #ifndef DISABLE_RECORDING - if (g_InputRecording.IsActive()) + // 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. + if (g_InputRecording.IsSavestateInitializing()) + { + g_InputRecording.SetStartingFrame(g_FrameCount); + } + else if (g_InputRecording.IsActive()) { // Explicitly set the frame change tracking variable as to not // detect loading a savestate as a frame being drawn @@ -52,37 +59,9 @@ void SaveStateBase::InputRecordingFreeze() // 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); + g_InputRecording.SetFrameCounter(g_FrameCount); } } - // 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. - 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())); - } #endif } @@ -105,10 +84,6 @@ void InputRecording::ControllerInterrupt(u8 &data, u8 &port, u16 &bufCount, u8 b if (bufCount == 1) { fInterruptFrame = data == 0x42; - if (!fInterruptFrame) - { - return; - } } else if (bufCount == 2) { @@ -121,28 +96,27 @@ void InputRecording::ControllerInterrupt(u8 &data, u8 &port, u16 &bufCount, u8 b if (buf[bufCount] != 0x5A) { fInterruptFrame = false; - return; } - } - - // We do not want to record or save the first two bytes in the data returned from the PAD plugin - if (!fInterruptFrame || state == InputRecordingMode::NotActive || bufCount < 3) + } // We do not want to record or save the first two bytes in the data returned from the PAD plugin + else if (fInterruptFrame && bufCount >= 3 && frameCounter >= 0) { - return; - } - - // Read or Write - const u8 &nowBuf = buf[bufCount]; - if (state == InputRecordingMode::Recording) - { - inputRecordingData.WriteKeyBuffer(frameCounter, port, bufCount - 3, nowBuf); - } - else if (state == InputRecordingMode::Replaying) - { - u8 tmp = 0; - if (inputRecordingData.ReadKeyBuffer(tmp, frameCounter, port, bufCount - 3)) + // Read or Write + if (state == InputRecordingMode::Recording) { - buf[bufCount] = tmp; + if (incrementUndo) + { + inputRecordingData.IncrementUndoCount(); + incrementUndo = false; + } + inputRecordingData.WriteKeyBuffer(frameCounter, port, bufCount - 3, buf[bufCount]); + } + else if (state == InputRecordingMode::Replaying) + { + u8 tmp = 0; + if (inputRecordingData.ReadKeyBuffer(tmp, frameCounter, port, bufCount - 3)) + { + buf[bufCount] = tmp; + } } } } @@ -165,9 +139,11 @@ u32 InputRecording::GetStartingFrame() void InputRecording::IncrementFrameCounter() { frameCounter++; - if (state == InputRecordingMode::Recording) + if (state == InputRecordingMode::Recording && frameCounter > 0) { - GetInputRecordingData().SetTotalFrames(frameCounter); + GetInputRecordingData().SetTotalFrames((u32)frameCounter); + if (frameCounter == inputRecordingData.GetTotalFrames()) + incrementUndo = false; } } @@ -241,27 +217,58 @@ void InputRecording::SavestateInitialized() savestateInitializing = false; } -void InputRecording::SetFrameCounter(u32 newFrameCounter) +void InputRecording::SetFrameCounter(u32 newGFrameCount) { - frameCounter = newFrameCounter; - if (state == InputRecordingMode::Recording) + // Forces inputRecording to wait for a confirmed pause before resetting + // frameCounter to the proper value. Ensures that any lingering emulation + // before the load doesn't increment the already reset value + const bool initiallyPaused = g_InputRecordingControls.IsRecordingPaused(); + if (!initiallyPaused) + g_InputRecordingControls.PauseImmediately(); + if (newGFrameCount > startingFrame + g_InputRecording.GetInputRecordingData().GetTotalFrames()) { - GetInputRecordingData().SetTotalFrames(frameCounter); + recordingConLog(L"[REC]: Warning, you've loaded PCSX2 emulation to a point after the end of the original recording. This should be avoided.\n"); + recordingConLog(L"[REC]: Savestate's framecount has been ignored.\n"); + frameCounter = g_InputRecording.GetInputRecordingData().GetTotalFrames(); + if (state == InputRecordingMode::Replaying) + { + SetToRecordMode(); + } + incrementUndo = false; + } + else + { + if (newGFrameCount < startingFrame) + { + recordingConLog(L"[REC]: Warning, you've loaded PCSX2 emulation to a point before the start of the original recording. This should be avoided.\n"); + if (state == InputRecordingMode::Recording) + { + SetToReplayMode(); + } + } + frameCounter = static_cast(newGFrameCount - startingFrame); + incrementUndo = true; + if (!initiallyPaused) + g_InputRecordingControls.Resume(); } } void InputRecording::SetStartingFrame(u32 newStartingFrame) { startingFrame = newStartingFrame; + if (savestateInitializing) + { + // TODO - make a function of my own to simplify working with the logging macros + recordingConLog(wxString::Format(L"[REC]: Internal Starting Frame: %d\n", startingFrame)); + savestateInitializing = false; + } + frameCounter = 0; } void InputRecording::Stop() { - // Reset the frame counter when starting a new recording - frameCounter = 0; - startingFrame = 0; - savestateInitializing = false; state = InputRecordingMode::NotActive; + incrementUndo = false; if (inputRecordingData.Close()) { recordingConLog(L"[REC]: InputRecording Recording Stopped.\n"); @@ -275,6 +282,13 @@ bool InputRecording::Create(wxString FileName, bool fromSaveState, wxString auth { return false; } + + if (!fromSaveState) + { + SetStartingFrame(0); + sApp.SysExecute(g_Conf->CdvdSource); + } + // Set emulator version inputRecordingData.GetHeader().SetEmulatorVersion(); @@ -302,6 +316,7 @@ bool InputRecording::Play(wxString fileName) { return false; } + // Either load the savestate, or restart the game if (inputRecordingData.FromSaveState()) { @@ -321,7 +336,11 @@ bool InputRecording::Play(wxString fileName) StateCopy_LoadFromFile(inputRecordingData.GetFilename() + "_SaveState.p2s"); } else - sApp.SysExecute(); + { + savestateInitializing = false; + SetStartingFrame(0); + sApp.SysExecute(g_Conf->CdvdSource); + } // Check if the current game matches with the one used to make the original recording if (!g_Conf->CurrentIso.IsEmpty()) @@ -331,6 +350,8 @@ bool InputRecording::Play(wxString fileName) recordingConLog(L"[REC]: Recording was possibly constructed for a different game.\n"); } } + + incrementUndo = true; state = InputRecordingMode::Replaying; recordingConLog(wxString::Format(L"[REC]: Replaying input recording - [%s]\n", inputRecordingData.GetFilename())); recordingConLog(wxString::Format(L"[REC]: PCSX2 Version Used: %s\n", inputRecordingData.GetHeader().emu)); diff --git a/pcsx2/Recording/InputRecording.h b/pcsx2/Recording/InputRecording.h index 94d67a45c1..e174e48f77 100644 --- a/pcsx2/Recording/InputRecording.h +++ b/pcsx2/Recording/InputRecording.h @@ -70,7 +70,7 @@ public: void SavestateInitialized(); // Set the running frame counter for the input recording to an arbitrary value - void SetFrameCounter(u32 newFrameCounter); + void SetFrameCounter(u32 newGFrameCount); // Store the starting internal PCSX2 g_FrameCount value void SetStartingFrame(u32 newStartingFrame); @@ -97,7 +97,8 @@ private: InputRecordingFile inputRecordingData; bool savestateInitializing = false; u32 startingFrame = 0; - u32 frameCounter = 0; + s32 frameCounter = 0; + bool incrementUndo = false; InputRecordingMode state = InputRecording::InputRecordingMode::NotActive; diff --git a/pcsx2/Recording/InputRecordingFile.cpp b/pcsx2/Recording/InputRecordingFile.cpp index a5910825c7..7d797eacd0 100644 --- a/pcsx2/Recording/InputRecordingFile.cpp +++ b/pcsx2/Recording/InputRecordingFile.cpp @@ -153,7 +153,6 @@ bool InputRecordingFile::OpenNew(const wxString path, bool fromSavestate) else if (open(path, true)) { savestate.fromSavestate = false; - sApp.SysExecute(); return true; } return false; @@ -172,11 +171,7 @@ bool InputRecordingFile::ReadKeyBuffer(u8 &result, const uint &frame, const uint } long seek = getRecordingBlockSeekPoint(frame) + controllerInputBytes * port + bufIndex; - if (fseek(recordingFile, seek, SEEK_SET) != 0) - { - return false; - } - if (fread(&result, 1, 1, recordingFile) != 1) + if (fseek(recordingFile, seek, SEEK_SET) != 0 || fread(&result, 1, 1, recordingFile) != 1) { return false; } @@ -221,8 +216,7 @@ bool InputRecordingFile::WriteKeyBuffer(const uint &frame, const uint port, cons long seek = getRecordingBlockSeekPoint(frame) + 18 * port + bufIndex; - if (fseek(recordingFile, seek, SEEK_SET) != 0 - || fwrite(&buf, 1, 1, recordingFile) != 1) + if (fseek(recordingFile, seek, SEEK_SET) != 0 || fwrite(&buf, 1, 1, recordingFile) != 1) { return false; } diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index 6f38f6e15a..435646e3ff 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -641,6 +641,10 @@ public: bool OnCmdLineError( wxCmdLineParser& parser ); bool ParseOverrides( wxCmdLineParser& parser ); +#ifndef DISABLE_RECORDING + void ResetRecordingCounter(); +#endif + #ifdef __WXDEBUG__ void OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg ); #endif diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 13cbab529d..a9c32c3544 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -1152,8 +1152,24 @@ void Pcsx2App::SysExecute() void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override ) { SysExecutorThread.PostEvent( new SysExecEvent_Execute(cdvdsrc, elf_override) ); +#ifndef DISABLE_RECORDING + ResetRecordingCounter(); +#endif } +#ifndef DISABLE_RECORDING +// Sets the frame counter for a running input recording file to the proper displacement +// from the recording's starting frame to the g_framecount of a fresh emulation boot/reboot +// Simply: frameCounter = -startingFrame +void Pcsx2App::ResetRecordingCounter() +{ + if (g_InputRecording.IsActive()) + { + g_InputRecording.SetFrameCounter(0); + } +} +#endif + // Returns true if there is a "valid" virtual machine state from the user's perspective. This // means the user has started the emulator and not issued a full reset. // Thread Safety: The state of the system can change in parallel to execution of the