recording: Recording controls rename and refactor to attempt to simplify

* RecordingControls - > InputRecordingControls (+ function refactors & simplifications)
* Avoid mutating g_FrameCount - now use isolated frameCounter & startingFrame variables for input recordings
* Pause or resume emulation preemptively based on GUI actions
* Exclusively handle GS window title updates in FrameForGS
This commit is contained in:
Tyler Wilding 2020-06-21 00:21:57 -04:00 committed by refractionpcsx2
parent fa894d9d48
commit 6c80e6b93f
16 changed files with 456 additions and 350 deletions

View File

@ -463,10 +463,10 @@ set(pcsx2RDebugHeaders
# Recording sources
set(pcsx2RecordingSources
Recording/InputRecording.cpp
Recording/InputRecordingControls.cpp
Recording/InputRecordingFile.cpp
Recording/NewRecordingFrame.cpp
Recording/PadData.cpp
Recording/RecordingControls.cpp
Recording/RecordingInputManager.cpp
Recording/VirtualPad.cpp
)
@ -474,10 +474,10 @@ set(pcsx2RecordingSources
# Recording headers
set(pcsx2RecordingHeaders
Recording/InputRecording.h
Recording/InputRecordingControls.h
Recording/InputRecordingFile.h
Recording/NewRecordingFrame.h
Recording/PadData.h
Recording/RecordingControls.h
Recording/RecordingInputManager.h
Recording/VirtualPad.h
)

View File

@ -33,7 +33,7 @@
#include "Sio.h"
#ifndef DISABLE_RECORDING
# include "Recording/RecordingControls.h"
# include "Recording/InputRecordingControls.h"
#endif
using namespace Threading;
@ -588,7 +588,7 @@ __fi void rcntUpdate_vSync()
#ifndef DISABLE_RECORDING
if (g_Conf->EmuOptions.EnableRecordingTools)
{
g_RecordingControls.HandleFrameAdvanceAndStop();
g_InputRecordingControls.HandleFrameAdvanceAndPausing();
}
#endif
VSyncStart(vsyncCounter.sCycle);

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2019 PCSX2 Dev Team
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -23,31 +23,51 @@
#include "SaveState.h"
#include "InputRecording.h"
#include "Recording/RecordingControls.h"
#include "InputRecordingControls.h"
#include <vector>
// Tag and save framecount along with savestate
// Save or load PCSX2's global frame counter (g_FrameCount) along with each savestate
//
// This is to prevent any inaccuracy issues caused by having a different
// internal emulation frame count than what it was at the beginning of the
// original recording
void SaveStateBase::InputRecordingFreeze()
{
FreezeTag("InputRecording");
Freeze(g_FrameCount);
#ifndef DISABLE_RECORDING
if (g_FrameCount > 0 && IsLoading())
// Explicitly set the frame change tracking variable as to not
// detect loading a savestate as a frame being drawn
g_InputRecordingControls.SetFrameCountTracker(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.
// TODO - get rid of the -1
if (g_InputRecording.GetStartingFrame() == -1 && g_InputRecording.GetInputRecordingData().FromSaveState()) {
g_InputRecording.SetStartingFrame(g_FrameCount);
// 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_InputRecordingData.IncrementUndoCount();
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
}
#ifndef DISABLE_RECORDING
InputRecording g_InputRecording;
// Main func for handling controller input data
// - Called by Sio.cpp::sioWriteController
void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 buf[])
void InputRecording::ControllerInterrupt(u8 &data, u8 &port, u16 &bufCount, u8 buf[])
{
// TODO - Multi-Tap Support
@ -82,127 +102,195 @@ void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 b
}
}
if (!fInterruptFrame || state == INPUT_RECORDING_MODE_NONE
// We do not want to record or save the first two
// bytes in the data returned from LilyPad
|| bufCount < 3)
// We do not want to record or save the first two bytes in the data returned from the PAD plugin
if (!fInterruptFrame || state == InputRecordingMode::NoneActive || bufCount < 3)
{
return;
}
// Read or Write
const u8& nowBuf = buf[bufCount];
if (state == INPUT_RECORDING_MODE_RECORD)
const u8 &nowBuf = buf[bufCount];
if (state == InputRecordingMode::Recording)
{
InputRecordingData.SetTotalFrames(g_FrameCount);
InputRecordingData.WriteKeyBuffer(g_FrameCount, port, bufCount - 3, nowBuf);
inputRecordingData.WriteKeyBuffer(frameCounter, port, bufCount - 3, nowBuf);
}
else if (state == INPUT_RECORDING_MODE_REPLAY)
else if (state == InputRecordingMode::Replaying)
{
if (InputRecordingData.GetTotalFrames() <= g_FrameCount)
{
// Pause the emulation but the movie is not closed
g_RecordingControls.Pause();
return;
}
u8 tmp = 0;
if (InputRecordingData.ReadKeyBuffer(tmp, g_FrameCount, port, bufCount - 3))
if (inputRecordingData.ReadKeyBuffer(tmp, frameCounter, port, bufCount - 3))
{
buf[bufCount] = tmp;
}
}
}
u32 InputRecording::GetFrameCounter()
{
return frameCounter;
}
InputRecordingFile &InputRecording::GetInputRecordingData()
{
return inputRecordingData;
}
u32 InputRecording::GetStartingFrame()
{
return startingFrame;
}
void InputRecording::IncrementFrameCounter()
{
frameCounter++;
if (state == InputRecordingMode::Recording) {
GetInputRecordingData().SetTotalFrames(frameCounter);
}
}
bool InputRecording::IsInterruptFrame()
{
return fInterruptFrame;
}
bool InputRecording::IsRecordingReplaying()
{
return RecordingActive() && state == InputRecordingMode::Replaying;
}
bool InputRecording::RecordingActive()
{
return state != InputRecordingMode::NoneActive;
}
wxString InputRecording::RecordingModeTitleSegment()
{
switch (state)
{
case InputRecordingMode::Recording:
return wxString("Recording");
break;
case InputRecordingMode::Replaying:
return wxString("Replaying");
break;
default:
return wxString("No Movie");
break;
}
}
void InputRecording::RecordModeToggle()
{
if (state == InputRecordingMode::Replaying)
{
state = InputRecordingMode::Recording;
recordingConLog("[REC]: Record mode ON.\n");
}
else if (state == InputRecordingMode::Recording)
{
state = InputRecordingMode::Replaying;
recordingConLog("[REC]: Replay mode ON.\n");
}
}
void InputRecording::SetFrameCounter(u32 newFrameCounter)
{
frameCounter = newFrameCounter;
if (state == InputRecordingMode::Recording)
{
GetInputRecordingData().SetTotalFrames(frameCounter);
}
}
void InputRecording::SetStartingFrame(u32 newStartingFrame)
{
startingFrame = newStartingFrame;
}
// GUI Handler - Stop recording
void InputRecording::Stop()
{
state = INPUT_RECORDING_MODE_NONE;
if (InputRecordingData.Close())
// Reset the frame counter when starting a new recording
frameCounter = 0;
startingFrame = -1;
state = InputRecordingMode::NoneActive;
if (inputRecordingData.Close())
{
recordingConLog(L"[REC]: InputRecording Recording Stopped.\n");
}
}
// GUI Handler - Start recording
bool InputRecording::Create(wxString FileName, bool fromSaveState, wxString authorName)
{
if (!InputRecordingData.OpenNew(FileName, fromSaveState))
if (!inputRecordingData.OpenNew(FileName, fromSaveState))
{
return false;
}
// Set emulator version
InputRecordingData.GetHeader().SetEmulatorVersion();
inputRecordingData.GetHeader().SetEmulatorVersion();
// Set author name
if (!authorName.IsEmpty())
{
InputRecordingData.GetHeader().SetAuthor(authorName);
inputRecordingData.GetHeader().SetAuthor(authorName);
}
// Set Game Name
InputRecordingData.GetHeader().SetGameName(resolveGameName());
inputRecordingData.GetHeader().SetGameName(resolveGameName());
// Write header contents
InputRecordingData.WriteHeader();
state = INPUT_RECORDING_MODE_RECORD;
inputRecordingData.WriteHeader();
state = InputRecordingMode::Recording;
recordingConLog(wxString::Format(L"[REC]: Started new recording - [%s]\n", FileName));
// In every case, we reset the g_FrameCount
g_FrameCount = 0;
return true;
}
// GUI Handler - Play a recording
bool InputRecording::Play(wxString fileName)
{
if (state != INPUT_RECORDING_MODE_NONE)
if (RecordingActive())
Stop();
// Open the file and verify if it can be played
if (!InputRecordingData.OpenExisting(fileName))
if (!inputRecordingData.OpenExisting(fileName))
{
return false;
}
// Either load the savestate, or restart the game
if (InputRecordingData.FromSaveState())
if (inputRecordingData.FromSaveState())
{
if (!CoreThread.IsOpen())
{
recordingConLog(L"[REC]: Game is not open, aborting playing input recording which starts on a save-state.\n");
InputRecordingData.Close();
inputRecordingData.Close();
return false;
}
FILE* ssFileCheck = wxFopen(InputRecordingData.GetFilename() + "_SaveState.p2s", "r");
FILE* ssFileCheck = wxFopen(inputRecordingData.GetFilename() + "_SaveState.p2s", "r");
if (ssFileCheck == NULL)
{
recordingConLog(wxString::Format("[REC]: Could not locate savestate file at location - %s_SaveState.p2s\n", InputRecordingData.GetFilename()));
InputRecordingData.Close();
recordingConLog(wxString::Format("[REC]: Could not locate savestate file at location - %s_SaveState.p2s\n", inputRecordingData.GetFilename()));
inputRecordingData.Close();
return false;
}
fclose(ssFileCheck);
StateCopy_LoadFromFile(InputRecordingData.GetFilename() + "_SaveState.p2s");
StateCopy_LoadFromFile(inputRecordingData.GetFilename() + "_SaveState.p2s");
}
else
{
g_RecordingControls.Unpause();
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())
{
if (resolveGameName() != InputRecordingData.GetHeader().gameName)
if (resolveGameName() != inputRecordingData.GetHeader().gameName)
{
recordingConLog(L"[REC]: Recording was possibly constructed for a different game.\n");
}
}
state = INPUT_RECORDING_MODE_REPLAY;
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));
recordingConLog(wxString::Format(L"[REC]: Recording File Version: %d\n", InputRecordingData.GetHeader().version));
recordingConLog(wxString::Format(L"[REC]: Associated Game Name or ISO Filename: %s\n", InputRecordingData.GetHeader().gameName));
recordingConLog(wxString::Format(L"[REC]: Author: %s\n", InputRecordingData.GetHeader().author));
recordingConLog(wxString::Format(L"[REC]: Total Frames: %d\n", InputRecordingData.GetTotalFrames()));
recordingConLog(wxString::Format(L"[REC]: Undo Count: %d\n", InputRecordingData.GetUndoCount()));
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));
recordingConLog(wxString::Format(L"[REC]: Recording File Version: %d\n", inputRecordingData.GetHeader().version));
recordingConLog(wxString::Format(L"[REC]: Associated Game Name or ISO Filename: %s\n", inputRecordingData.GetHeader().gameName));
recordingConLog(wxString::Format(L"[REC]: Author: %s\n", inputRecordingData.GetHeader().author));
recordingConLog(wxString::Format(L"[REC]: Total Frames: %d\n", inputRecordingData.GetTotalFrames()));
recordingConLog(wxString::Format(L"[REC]: Undo Count: %d\n", inputRecordingData.GetUndoCount()));
return true;
}
@ -226,33 +314,4 @@ wxString InputRecording::resolveGameName()
return !gameName.IsEmpty() ? gameName : Path::GetFilename(g_Conf->CurrentIso);
}
// Keybind Handler - Toggle between recording input and not
void InputRecording::RecordModeToggle()
{
if (state == INPUT_RECORDING_MODE_REPLAY)
{
state = INPUT_RECORDING_MODE_RECORD;
recordingConLog("[REC]: Record mode ON.\n");
}
else if (state == INPUT_RECORDING_MODE_RECORD)
{
state = INPUT_RECORDING_MODE_REPLAY;
recordingConLog("[REC]: Replay mode ON.\n");
}
}
INPUT_RECORDING_MODE InputRecording::GetModeState()
{
return state;
}
InputRecordingFile& InputRecording::GetInputRecordingData()
{
return InputRecordingData;
}
bool InputRecording::IsInterruptFrame()
{
return fInterruptFrame;
}
#endif

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2019 PCSX2 Dev Team
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -19,41 +19,69 @@
#ifndef DISABLE_RECORDING
enum INPUT_RECORDING_MODE
{
INPUT_RECORDING_MODE_NONE,
INPUT_RECORDING_MODE_RECORD,
INPUT_RECORDING_MODE_REPLAY,
};
class InputRecording
{
public:
InputRecording() {}
~InputRecording() {}
// Main handler for ingesting input data and either saving it to the recording file (recording)
// or mutating it to the contents of the recording file (replaying)
void ControllerInterrupt(u8 &data, u8 &port, u16 &BufCount, u8 buf[]);
void ControllerInterrupt(u8& data, u8& port, u16& BufCount, u8 buf[]);
// The running frame counter for the input recording
u32 GetFrameCounter();
void RecordModeToggle();
InputRecordingFile &GetInputRecordingData();
INPUT_RECORDING_MODE GetModeState();
InputRecordingFile& GetInputRecordingData();
// The internal PCSX2 g_FrameCount value on the first frame of the recording
u32 GetStartingFrame();
void IncrementFrameCounter();
// DEPRECATED: Slated for removal
// If the current frame contains controller / input data
bool IsInterruptFrame();
void Stop();
// If there is currently an input recording being played back or actively being recorded
bool RecordingActive();
bool IsRecordingReplaying();
// String representation of the current recording mode to be interpolated into the title
wxString RecordingModeTitleSegment();
// Switches between recording and replaying the active input recording file
void RecordModeToggle();
// 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);
/// Functions called from GUI
// Create a new input recording file
bool Create(wxString filename, bool fromSaveState, wxString authorName);
// Play an existing input recording from a file
bool Play(wxString filename);
// Stop the active input recording
void Stop();
private:
InputRecordingFile InputRecordingData;
INPUT_RECORDING_MODE state = INPUT_RECORDING_MODE_NONE;
enum class InputRecordingMode {
NoneActive,
Recording,
Replaying,
};
bool fInterruptFrame = false;
InputRecordingFile inputRecordingData;
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
wxString resolveGameName();
};
extern InputRecording g_InputRecording;
static InputRecordingFile& g_InputRecordingData = g_InputRecording.GetInputRecordingData();
static InputRecordingFileHeader& g_InputRecordingHeader = g_InputRecording.GetInputRecordingData().GetHeader();
#endif

View File

@ -0,0 +1,140 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "App.h"
#include "Counters.h"
#include "DebugTools/Debug.h"
#include "GSFrame.h"
#include "MemoryTypes.h"
#include "InputRecording.h"
#include "InputRecordingControls.h"
#ifndef DISABLE_RECORDING
InputRecordingControls g_InputRecordingControls;
void InputRecordingControls::HandleFrameAdvanceAndPausing()
{
// This function can be called multiple times per frame via Counters::rcntUpdate_vSync,
// often this is twice per frame but this may vary as Counters.cpp mentions the
// vsync timing can change.
//
// 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) {
frameCountTracker = g_FrameCount;
g_InputRecording.IncrementFrameCounter();
} else {
if (pauseEmulation) {
emulationCurrentlyPaused = true;
CoreThread.PauseSelf();
}
return;
}
if (g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) {
pauseEmulation = true;
}
// If we havn't yet advanced atleast a single frame from when we paused, setup things to be paused
if (frameAdvancing && frameAdvanceMarker < g_InputRecording.GetFrameCounter())
{
frameAdvancing = false;
pauseEmulation = true;
}
// Pause emulation if we need to (either due to frame advancing, or pause has been explicitly toggled on)
if (pauseEmulation && CoreThread.IsOpen() && CoreThread.IsRunning()) {
emulationCurrentlyPaused = true;
CoreThread.PauseSelf();
}
}
void InputRecordingControls::ResumeCoreThreadIfStarted()
{
if (resumeEmulation && CoreThread.IsOpen() && CoreThread.IsPaused()) {
CoreThread.Resume();
resumeEmulation = false;
emulationCurrentlyPaused = false;
}
}
void InputRecordingControls::FrameAdvance()
{
if (g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames())
{
g_InputRecording.RecordModeToggle();
return;
}
frameAdvanceMarker = g_InputRecording.GetFrameCounter();
frameAdvancing = true;
Resume();
}
bool InputRecordingControls::IsRecordingPaused()
{
return (emulationCurrentlyPaused && CoreThread.IsOpen() && CoreThread.IsPaused());
}
void InputRecordingControls::Pause()
{
pauseEmulation = true;
resumeEmulation = false;
}
void InputRecordingControls::PauseImmediately()
{
if (CoreThread.IsPaused()) {
return;
}
Pause();
if (pauseEmulation && CoreThread.IsOpen() && CoreThread.IsRunning()) {
emulationCurrentlyPaused = true;
CoreThread.PauseSelf();
}
}
void InputRecordingControls::Resume()
{
if (g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) {
g_InputRecording.RecordModeToggle();
return;
}
pauseEmulation = false;
resumeEmulation = true;
}
void InputRecordingControls::SetFrameCountTracker(u32 newFrame)
{
frameCountTracker = newFrame;
}
void InputRecordingControls::TogglePause()
{
if (pauseEmulation && g_InputRecording.IsRecordingReplaying() && g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames()) {
g_InputRecording.RecordModeToggle();
return;
}
pauseEmulation = !pauseEmulation;
resumeEmulation = !pauseEmulation;
}
#endif

View File

@ -0,0 +1,72 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef DISABLE_RECORDING
class InputRecordingControls
{
public:
// Intended to be called at the end of each frame, but will no-op if the frame count has not
// truly incremented
//
// Increments the input recording's frame counter and will pause emulation if:
// - The InputRecordingControls::FrameAdvance was hit on the previous frame
// - Emulation was explicitly paused using InputRecordingControls::TogglePause
// - We are replaying an input recording and have hit the end
void HandleFrameAdvanceAndPausing();
// Called much more frequently than HandleFrameAdvanceAndPausing, instead of being per frame
// this hooks into pcsx2's main App event handler as it has to be able to resume emulation
// when drawing frames has compltely stopped
//
// Resumes emulation if:
// - CoreThread is currently open and paused
// - We've signaled emulation to be resumed via TogglePause or FrameAdvancing
void ResumeCoreThreadIfStarted();
// 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();
// 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);
// 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;
// The input recording frame that frame advancing began on
u32 frameAdvanceMarker = 0;
// Used to detect if the internal PCSX2 g_FrameCount has changed
u32 frameCountTracker = -1;
// Indicates if we intend to call CoreThread.PauseSelf() on the current or next available vsync
bool pauseEmulation = false;
// Indicates if we intend to call CoreThread.Resume() when the next pcsx2 App event is handled
bool resumeEmulation = false;
};
extern InputRecordingControls g_InputRecordingControls;
#endif

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2019 PCSX2 Dev Team
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -260,19 +260,4 @@ bool InputRecordingFile::verifyRecordingFileHeader()
}
return true;
}
bool InputRecordingFile::writeSaveState() {
if (recordingFile == NULL)
{
return false;
}
fseek(recordingFile, seekpointSaveStateHeader, SEEK_SET);
if (fwrite(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1)
{
return false;
}
return true;
}
#endif

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2019 PCSX2 Dev Team
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -82,6 +82,8 @@ public:
// Writes the current frame's input data to the file so it can be replayed
bool WriteKeyBuffer(const uint &frame, const uint port, const uint bufIndex, const u8 &buf);
unsigned long recordingFrameCounter = 0;
private:
static const int controllerPortsSupported = 2;
static const int controllerInputBytes = 18;
@ -99,6 +101,8 @@ private:
wxString filename = "";
FILE * recordingFile = NULL;
InputRecordingSavestate savestate;
// An unsigned 32-bit frame limit is equivalent to 2.25 years of continuous 60fps footage
unsigned long totalFrames = 0;
unsigned long undoCount = 0;
@ -106,6 +110,5 @@ private:
long getRecordingBlockSeekPoint(const long& frame);
bool open(const wxString path, bool newRecording);
bool verifyRecordingFileHeader();
bool writeSaveState();
};
#endif

View File

@ -1,120 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "App.h"
#include "Counters.h"
#include "Common.h"
#include "GSFrame.h"
#include "MemoryTypes.h"
#include "RecordingControls.h"
#ifndef DISABLE_RECORDING
RecordingControls g_RecordingControls;
//-----------------------------------------------
// Current recording status, returns true if:
// - Recording is Paused
// - GSFrame CoreThread is both running AND paused
//-----------------------------------------------
bool RecordingControls::IsEmulationAndRecordingPaused()
{
return fPauseState && CoreThread.IsOpen() && CoreThread.IsPaused();
}
//-----------------------------------------------
// Called after inputs are recorded for that frame, places lock on input recording, effectively releasing resources and resuming CoreThread.
//-----------------------------------------------
void RecordingControls::ResumeCoreThreadIfStarted()
{
if (fStart && CoreThread.IsOpen() && CoreThread.IsPaused())
{
CoreThread.Resume();
fStart = false;
fPauseState = false;
}
}
//-----------------------------------------------
// Called at VSYNC End / VRender Begin, updates everything recording related for the next frame,
// toggles RecordingControl flags back to enable input recording for the next frame.
//-----------------------------------------------
void RecordingControls::HandleFrameAdvanceAndStop()
{
if (fFrameAdvance)
{
if (stopFrameCount < g_FrameCount)
{
fFrameAdvance = false;
fStop = true;
stopFrameCount = g_FrameCount;
// We force the frame counter in the title bar to change
wxString oldTitle = wxGetApp().GetGsFrame().GetTitle();
wxString title = g_Conf->Templates.RecordingTemplate;
wxString frameCount = wxString::Format("%d", g_FrameCount);
title.Replace(L"${frame}", frameCount);
int frameIndex = title.find(wxString::Format(L"%d", g_FrameCount));
frameIndex += frameCount.length();
title.replace(frameIndex, oldTitle.length() - frameIndex, oldTitle.c_str().AsChar() + frameIndex);
wxGetApp().GetGsFrame().SetTitle(title);
}
}
if (fStop && CoreThread.IsOpen() && CoreThread.IsRunning())
{
fPauseState = true;
CoreThread.PauseSelf();
}
}
bool RecordingControls::GetStopFlag()
{
return (fStop || fFrameAdvance);
}
void RecordingControls::FrameAdvance()
{
stopFrameCount = g_FrameCount;
fFrameAdvance = true;
fStop = false;
fStart = true;
}
void RecordingControls::TogglePause()
{
fStop = !fStop;
if (fStop == false)
{
fStart = true;
}
}
void RecordingControls::Pause()
{
fStop = true;
}
void RecordingControls::Unpause()
{
fStop = false;
fStart = true;
}
#endif

View File

@ -1,50 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef DISABLE_RECORDING
class RecordingControls
{
public:
// Movie controls main functions
bool IsEmulationAndRecordingPaused();
void ResumeCoreThreadIfStarted();
void HandleFrameAdvanceAndStop();
// Shortcut Keys
void FrameAdvance();
void TogglePause();
// Setters
void Pause();
void Unpause();
// Getters
bool GetStopFlag();
private:
uint stopFrameCount = false;
bool fStop = false;
bool fStart = false;
bool fFrameAdvance = false;
bool fPauseState = false;
};
extern RecordingControls g_RecordingControls;
#endif

View File

@ -32,7 +32,7 @@
#include "Debugger/DisassemblyDialog.h"
#ifndef DISABLE_RECORDING
# include "Recording/RecordingControls.h"
# include "Recording/InputRecordingControls.h"
# include "Recording/InputRecording.h"
#endif
@ -620,7 +620,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
#ifndef DISABLE_RECORDING
if (g_Conf->EmuOptions.EnableRecordingTools)
{
if (g_RecordingControls.IsEmulationAndRecordingPaused())
if (g_InputRecordingControls.IsRecordingPaused())
{
// When the GSFrame CoreThread is paused, so is the logical VSync
// Meaning that we have to grab the user-input through here to potentially
@ -633,7 +633,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
}
}
g_RecordingControls.ResumeCoreThreadIfStarted();
g_InputRecordingControls.ResumeCoreThreadIfStarted();
}
#endif
(handler->*func)(event);
@ -1059,7 +1059,7 @@ void Pcsx2App::OnProgramLogClosed( wxWindowID id )
void Pcsx2App::OnMainFrameClosed( wxWindowID id )
{
#ifndef DISABLE_RECORDING
if (g_InputRecording.GetModeState() == INPUT_RECORDING_MODE_NONE)
if (g_InputRecording.RecordingActive())
{
g_InputRecording.Stop();
}

View File

@ -425,7 +425,7 @@ void GSPanel::DirectKeyCommand( wxKeyEvent& evt )
void GSPanel::UpdateScreensaver()
{
bool prevent = g_Conf->GSWindow.DisableScreenSaver
&& m_HasFocus && m_coreRunning;
&& m_HasFocus && m_coreRunning;
ScreensaverAllow(!prevent);
}
@ -732,25 +732,15 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
#ifndef DISABLE_RECORDING
wxString title;
wxString movieMode;
switch (g_InputRecording.GetModeState())
if (g_InputRecording.RecordingActive())
{
case INPUT_RECORDING_MODE_RECORD:
movieMode = "Recording";
title = templates.RecordingTemplate;
break;
case INPUT_RECORDING_MODE_REPLAY:
movieMode = "Replaying";
title = templates.RecordingTemplate;
break;
case INPUT_RECORDING_MODE_NONE:
movieMode = "No movie";
title = templates.TitleTemplate;
break;
title = templates.RecordingTemplate;
title.Replace(L"${frame}", pxsFmt(L"%d", g_InputRecording.GetFrameCounter()));
title.Replace(L"${maxFrame}", pxsFmt(L"%d", g_InputRecording.GetInputRecordingData().GetTotalFrames()));
title.Replace(L"${mode}", g_InputRecording.RecordingModeTitleSegment());
} else {
title = templates.TitleTemplate;
}
title.Replace(L"${frame}", pxsFmt(L"%d", g_FrameCount));
title.Replace(L"${maxFrame}", pxsFmt(L"%d", g_InputRecording.GetInputRecordingData().GetTotalFrames()));
title.Replace(L"${mode}", movieMode);
#else
wxString title = templates.TitleTemplate;
#endif

View File

@ -23,7 +23,7 @@
#include "AppSaveStates.h"
#ifndef DISABLE_RECORDING
# include "Recording/RecordingControls.h"
# include "Recording/InputRecordingControls.h"
# include "Recording/InputRecording.h"
#endif
@ -473,7 +473,7 @@ namespace Implementations
{
if (g_Conf->EmuOptions.EnableRecordingTools)
{
g_RecordingControls.FrameAdvance();
g_InputRecordingControls.FrameAdvance();
}
}
@ -481,7 +481,7 @@ namespace Implementations
{
if (g_Conf->EmuOptions.EnableRecordingTools)
{
g_RecordingControls.TogglePause();
g_InputRecordingControls.TogglePause();
}
}

View File

@ -32,9 +32,8 @@
#ifndef DISABLE_RECORDING
# include "Recording/InputRecording.h"
# include "Recording/RecordingControls.h"
# include "Recording/InputRecordingControls.h"
# include "Recording/VirtualPad.h"
# include "Recording/RecordingControls.h"
#endif
@ -531,7 +530,7 @@ void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent& event)
else
{
//Properly close any currently loaded recording file before disabling
if (g_InputRecording.GetModeState() != INPUT_RECORDING_MODE_NONE)
if (g_InputRecording.RecordingActive())
Menu_Recording_Stop_Click(event);
GetMenuBar()->Remove(TopLevelMenu_InputRecording);
// Always turn controller logs off, but never turn it on by default
@ -544,8 +543,8 @@ void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent& event)
viewport->InitDefaultAccelerators();
}
}
if (g_RecordingControls.IsEmulationAndRecordingPaused())
g_RecordingControls.Unpause();
if (g_InputRecordingControls.IsRecordingPaused())
g_InputRecordingControls.Resume();
}
g_Conf->EmuOptions.EnableRecordingTools = checked;
@ -690,11 +689,11 @@ void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent& event)
// If the CoreThread is paused prior to opening the PAD plugin settings then when the settings
// are closed the PAD will not re-open. To avoid this, we resume emulation prior to the plugins
// configuration handler doing so.
if (g_Conf->EmuOptions.EnableRecordingTools && g_RecordingControls.IsEmulationAndRecordingPaused())
if (g_Conf->EmuOptions.EnableRecordingTools && g_InputRecordingControls.IsRecordingPaused())
{
g_RecordingControls.Unpause();
g_InputRecordingControls.Resume();
GetCorePlugins().Configure(pid);
g_RecordingControls.Pause();
g_InputRecordingControls.Pause();
}
else
GetCorePlugins().Configure(pid);
@ -874,53 +873,53 @@ void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_Click(wxCommandEvent & eve
#ifndef DISABLE_RECORDING
void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event)
{
const bool initiallyPaused = g_RecordingControls.IsEmulationAndRecordingPaused();
const bool initiallyPaused = g_InputRecordingControls.IsRecordingPaused();
if (!initiallyPaused)
g_RecordingControls.Pause();
g_InputRecordingControls.PauseImmediately();
NewRecordingFrame* NewRecordingFrame = wxGetApp().GetNewRecordingFramePtr();
if (NewRecordingFrame)
{
if (NewRecordingFrame->ShowModal() == wxID_CANCEL)
{
if (!initiallyPaused)
g_RecordingControls.Unpause();
g_InputRecordingControls.Resume();
return;
}
if (!g_InputRecording.Create(NewRecordingFrame->GetFile(), !NewRecordingFrame->GetFrom(), NewRecordingFrame->GetAuthor()))
{
if (!initiallyPaused)
g_RecordingControls.Unpause();
g_InputRecordingControls.Resume();
return;
}
}
m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false);
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
if (!g_InputRecordingData.FromSaveState())
g_RecordingControls.Unpause();
if (!g_InputRecording.GetInputRecordingData().FromSaveState())
g_InputRecordingControls.Resume();
}
void MainEmuFrame::Menu_Recording_Play_Click(wxCommandEvent &event)
{
const bool initiallyPaused = g_RecordingControls.IsEmulationAndRecordingPaused();
const bool initiallyPaused = g_InputRecordingControls.IsRecordingPaused();
if (!initiallyPaused)
g_RecordingControls.Pause();
g_InputRecordingControls.PauseImmediately();
wxFileDialog openFileDialog(this, _("Select P2M2 record file."), L"", L"",
L"p2m2 file(*.p2m2)|*.p2m2", wxFD_OPEN);
if (openFileDialog.ShowModal() == wxID_CANCEL)
{
if (!initiallyPaused)
g_RecordingControls.Unpause();
g_InputRecordingControls.Resume();
return;
}
wxString path = openFileDialog.GetPath();
const bool recordingLoaded = g_InputRecording.GetModeState() != INPUT_RECORDING_MODE_NONE;
const bool recordingLoaded = g_InputRecording.RecordingActive();
if (!g_InputRecording.Play(path))
{
if (recordingLoaded)
Menu_Recording_Stop_Click(event);
if (!initiallyPaused)
g_RecordingControls.Unpause();
g_InputRecordingControls.Resume();
return;
}
if (!recordingLoaded)
@ -928,7 +927,7 @@ void MainEmuFrame::Menu_Recording_Play_Click(wxCommandEvent &event)
m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false);
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
}
g_RecordingControls.Unpause();
g_InputRecordingControls.Resume();
}
void MainEmuFrame::Menu_Recording_Stop_Click(wxCommandEvent &event)

View File

@ -192,12 +192,12 @@
<ClCompile Include="..\..\ps2\Iop\PsxBios.cpp" />
<ClCompile Include="..\..\ps2\LegacyDmac.cpp" />
<ClCompile Include="..\..\ps2\pgif.cpp" />
<ClCompile Include="..\..\Recording\InputRecordingControls.cpp" />
<ClCompile Include="..\..\ShiftJisToUnicode.cpp" />
<ClCompile Include="..\..\sif2.cpp" />
<ClCompile Include="..\..\Recording\InputRecording.cpp" />
<ClCompile Include="..\..\Recording\NewRecordingFrame.cpp" />
<ClCompile Include="..\..\Recording\InputRecordingFile.cpp" />
<ClCompile Include="..\..\Recording\RecordingControls.cpp" />
<ClCompile Include="..\..\Recording\PadData.cpp" />
<ClCompile Include="..\..\Recording\RecordingInputManager.cpp" />
<ClCompile Include="..\..\Recording\VirtualPad.cpp" />
@ -430,9 +430,9 @@
<ClInclude Include="..\..\PrecompiledHeader.h" />
<ClInclude Include="..\..\ps2\pgif.h" />
<ClInclude Include="..\..\Recording\InputRecording.h" />
<ClInclude Include="..\..\Recording\InputRecordingControls.h" />
<ClInclude Include="..\..\Recording\NewRecordingFrame.h" />
<ClInclude Include="..\..\Recording\InputRecordingFile.h" />
<ClInclude Include="..\..\Recording\RecordingControls.h" />
<ClInclude Include="..\..\Recording\PadData.h" />
<ClInclude Include="..\..\Recording\RecordingInputManager.h" />
<ClInclude Include="..\..\Recording\VirtualPad.h" />

View File

@ -847,9 +847,6 @@
<ClCompile Include="..\..\Recording\PadData.cpp">
<Filter>Recording</Filter>
</ClCompile>
<ClCompile Include="..\..\Recording\RecordingControls.cpp">
<Filter>Recording</Filter>
</ClCompile>
<ClCompile Include="..\..\Recording\RecordingInputManager.cpp">
<Filter>Recording</Filter>
</ClCompile>
@ -862,6 +859,9 @@
<ClCompile Include="..\..\IPU\IPUdither.cpp">
<Filter>System\Ps2\IPU</Filter>
</ClCompile>
<ClCompile Include="..\..\Recording\InputRecordingControls.cpp">
<Filter>Recording</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Patch.h">
@ -1299,9 +1299,6 @@
<ClInclude Include="..\..\Recording\PadData.h">
<Filter>Recording</Filter>
</ClInclude>
<ClInclude Include="..\..\Recording\RecordingControls.h">
<Filter>Recording</Filter>
</ClInclude>
<ClInclude Include="..\..\Recording\RecordingInputManager.h">
<Filter>Recording</Filter>
</ClInclude>
@ -1311,6 +1308,9 @@
<ClInclude Include="..\..\Recording\VirtualPad.h">
<Filter>Recording\gui</Filter>
</ClInclude>
<ClInclude Include="..\..\Recording\InputRecordingControls.h">
<Filter>Recording</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\wxResources.rc">