recording: Resolve issues discovered while testing around savestate handling

* Add a flag to explicitly indicate if the initial SS has been loaded.
* Additionally: bracket formatting / spaces -> tabs.
This commit is contained in:
Tyler Wilding 2020-06-21 00:50:02 -04:00 committed by refractionpcsx2
parent 6c80e6b93f
commit 74bba35765
8 changed files with 125 additions and 68 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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