recording: Rework Frame Value & Undo Count Functions

* Handle setting frameCounter to 0 in SetStartingFrame().
* FrameCounter, signed -> unsigned.
* Have SetFrameCounter handle the calculations for the relative frame displacement from the startingFrame.
* Optimizes InputRecording::controllerInterrupt.
* Flip the order of execution in inputRecordingFreeze.
* Set framecounter properly on full/fast boot.
* More accurately increment undo count
This commit is contained in:
sonicfind 2020-09-07 15:05:22 -05:00 committed by refractionpcsx2
parent 0304b124ed
commit b591c5e9ab
5 changed files with 110 additions and 74 deletions

View File

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

View File

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

View File

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

View File

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

View File

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