mirror of https://github.com/PCSX2/pcsx2.git
input-rec: refactor core code, freeze wxWidgets version
This commit is contained in:
parent
2e8076d909
commit
9aecf79d7f
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -15,13 +15,14 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
// TODO - Vaser - kill with wxWidgets
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
#include "SaveState.h"
|
||||
#include "Counters.h"
|
||||
#include "SaveState.h"
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
|
||||
#include "gui/App.h"
|
||||
#include "gui/AppSaveStates.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
|
@ -31,12 +32,8 @@
|
|||
#include "InputRecordingControls.h"
|
||||
#include "Utilities/InputRecordingLogger.h"
|
||||
|
||||
#include "gui/AppSaveStates.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#endif
|
||||
|
||||
void SaveStateBase::InputRecordingFreeze()
|
||||
{
|
||||
// NOTE - BE CAREFUL
|
||||
|
@ -44,7 +41,6 @@ void SaveStateBase::InputRecordingFreeze()
|
|||
FreezeTag("InputRecording");
|
||||
Freeze(g_FrameCount);
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
if (g_Conf->EmuOptions.EnableRecordingTools)
|
||||
{
|
||||
// Loading a save-state is an asynchronous task. If we are playing a recording
|
||||
|
@ -55,11 +51,8 @@ void SaveStateBase::InputRecordingFreeze()
|
|||
else if (g_InputRecording.IsActive() && IsLoading())
|
||||
g_InputRecording.SetFrameCounter(g_FrameCount);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
|
||||
InputRecording g_InputRecording;
|
||||
|
||||
InputRecording::InputRecordingPad::InputRecordingPad()
|
||||
|
@ -124,7 +117,7 @@ void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 b
|
|||
|
||||
// Update controller data state for future VirtualPad / logging usage.
|
||||
pads[port].padData->UpdateControllerData(bufIndex, bufVal);
|
||||
|
||||
|
||||
if (pads[port].virtualPad->IsShown())
|
||||
pads[port].virtualPad->UpdateControllerData(bufIndex, pads[port].padData);
|
||||
}
|
||||
|
@ -308,12 +301,12 @@ void InputRecording::SetupInitialState(u32 newStartingFrame)
|
|||
incrementUndo = true;
|
||||
inputRec::log("Replaying input recording");
|
||||
inputRec::consoleMultiLog({fmt::format("File: {}", inputRecordingData.GetFilename().ToUTF8()),
|
||||
fmt::format("PCSX2 Version Used: {}", std::string(inputRecordingData.GetHeader().emu)),
|
||||
fmt::format("Recording File Version: {}", inputRecordingData.GetHeader().version),
|
||||
fmt::format("Associated Game Name or ISO Filename: {}", std::string(inputRecordingData.GetHeader().gameName)),
|
||||
fmt::format("Author: {}", inputRecordingData.GetHeader().author),
|
||||
fmt::format("Total Frames: {}", inputRecordingData.GetTotalFrames()),
|
||||
fmt::format("Undo Count: {}", inputRecordingData.GetUndoCount())});
|
||||
fmt::format("PCSX2 Version Used: {}", std::string(inputRecordingData.GetHeader().emu)),
|
||||
fmt::format("Recording File Version: {}", inputRecordingData.GetHeader().version),
|
||||
fmt::format("Associated Game Name or ISO Filename: {}", std::string(inputRecordingData.GetHeader().gameName)),
|
||||
fmt::format("Author: {}", inputRecordingData.GetHeader().author),
|
||||
fmt::format("Total Frames: {}", inputRecordingData.GetTotalFrames()),
|
||||
fmt::format("Undo Count: {}", inputRecordingData.GetUndoCount())});
|
||||
SetToReplayMode();
|
||||
}
|
||||
|
||||
|
@ -394,7 +387,7 @@ bool InputRecording::Play(wxWindow* parent, wxString filename)
|
|||
if (!wxFileExists(savestate))
|
||||
{
|
||||
wxFileDialog loadStateDialog(parent, _("Select the savestate that will accompany this recording"), L"", L"",
|
||||
L"Savestate files (*.p2s)|*.p2s", wxFD_OPEN);
|
||||
L"Savestate files (*.p2s)|*.p2s", wxFD_OPEN);
|
||||
if (loadStateDialog.ShowModal() == wxID_CANCEL)
|
||||
{
|
||||
inputRec::consoleLog(fmt::format("Could not locate savestate file at location - {}", savestate.ToUTF8()));
|
||||
|
@ -432,7 +425,7 @@ void InputRecording::GoToFirstFrame(wxWindow* parent)
|
|||
|
||||
inputRec::consoleLog(fmt::format("Could not locate savestate file at location - {}\n", savestate.ToUTF8()));
|
||||
wxFileDialog loadStateDialog(parent, _("Select a savestate to accompany the recording with"), L"", L"",
|
||||
L"Savestate files (*.p2s)|*.p2s", wxFD_OPEN);
|
||||
L"Savestate files (*.p2s)|*.p2s", wxFD_OPEN);
|
||||
int result = loadStateDialog.ShowModal();
|
||||
if (!initiallyPaused)
|
||||
g_InputRecordingControls.Resume();
|
||||
|
@ -443,7 +436,7 @@ void InputRecording::GoToFirstFrame(wxWindow* parent)
|
|||
return;
|
||||
}
|
||||
savestate = loadStateDialog.GetPath();
|
||||
inputRec::consoleLog(fmt::format ("Base savestate swapped to {}", savestate.ToUTF8()));
|
||||
inputRec::consoleLog(fmt::format("Base savestate swapped to {}", savestate.ToUTF8()));
|
||||
}
|
||||
StateCopy_LoadFromFile(savestate);
|
||||
}
|
||||
|
@ -470,4 +463,365 @@ wxString InputRecording::resolveGameName()
|
|||
return !gameName.empty() ? StringUtil::UTF8StringToWxString(gameName) : Path::GetFilename(g_Conf->CurrentIso);
|
||||
}
|
||||
|
||||
#endif
|
||||
#else
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
#include "SaveState.h"
|
||||
#include "Counters.h"
|
||||
#include "SaveState.h"
|
||||
|
||||
#include "VMManager.h"
|
||||
|
||||
#include "DebugTools/Debug.h"
|
||||
#include "GameDatabase.h"
|
||||
|
||||
#include "InputRecording.h"
|
||||
#include "InputRecordingControls.h"
|
||||
#include "Utilities/InputRecordingLogger.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
void SaveStateBase::InputRecordingFreeze()
|
||||
{
|
||||
// NOTE - BE CAREFUL
|
||||
// CHANGING THIS WILL BREAK BACKWARDS COMPATIBILITY ON SAVESTATES
|
||||
FreezeTag("InputRecording");
|
||||
Freeze(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.IsInitialLoad())
|
||||
g_InputRecording.SetupInitialState(g_FrameCount);
|
||||
else if (g_InputRecording.IsActive() && IsLoading())
|
||||
g_InputRecording.SetFrameCounter(g_FrameCount);
|
||||
}
|
||||
|
||||
InputRecording g_InputRecording;
|
||||
|
||||
InputRecording::InputRecordingPad::InputRecordingPad()
|
||||
{
|
||||
padData = new PadData;
|
||||
}
|
||||
|
||||
InputRecording::InputRecordingPad::~InputRecordingPad()
|
||||
{
|
||||
delete padData;
|
||||
}
|
||||
|
||||
void InputRecording::RecordingReset()
|
||||
{
|
||||
// Booting is an asynchronous task. If we are playing a recording
|
||||
// that starts from power-on and the starting (pcsx2 internal) frame
|
||||
// marker has not been set, we initialize it.
|
||||
if (g_InputRecording.IsInitialLoad())
|
||||
g_InputRecording.SetupInitialState(0);
|
||||
else if (g_InputRecording.IsActive())
|
||||
{
|
||||
g_InputRecording.SetFrameCounter(0);
|
||||
g_InputRecordingControls.Lock(0);
|
||||
}
|
||||
else
|
||||
g_InputRecordingControls.Resume();
|
||||
}
|
||||
|
||||
void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 buf[])
|
||||
{
|
||||
// TODO - Multi-Tap Support
|
||||
|
||||
if (bufCount == 1)
|
||||
fInterruptFrame = data == READ_DATA_AND_VIBRATE_FIRST_BYTE;
|
||||
else if (bufCount == 2)
|
||||
{
|
||||
if (buf[bufCount] != READ_DATA_AND_VIBRATE_SECOND_BYTE)
|
||||
fInterruptFrame = false;
|
||||
}
|
||||
else if (fInterruptFrame)
|
||||
{
|
||||
u8& bufVal = buf[bufCount];
|
||||
const u16 bufIndex = bufCount - 3;
|
||||
if (state == InputRecordingMode::Replaying)
|
||||
{
|
||||
if (frameCounter >= 0 && frameCounter < INT_MAX)
|
||||
{
|
||||
if (!inputRecordingData.ReadKeyBuffer(bufVal, frameCounter, port, bufIndex))
|
||||
inputRec::consoleLog(fmt::format("Failed to read input data at frame {}", frameCounter));
|
||||
|
||||
// Update controller data state for future VirtualPad / logging usage.
|
||||
pads[port].padData->UpdateControllerData(bufIndex, bufVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update controller data state for future VirtualPad / logging usage.
|
||||
pads[port].padData->UpdateControllerData(bufIndex, bufVal);
|
||||
|
||||
// Commit the byte to the movie file if we are recording
|
||||
if (state == InputRecordingMode::Recording)
|
||||
{
|
||||
if (frameCounter >= 0)
|
||||
{
|
||||
if (incrementUndo)
|
||||
{
|
||||
inputRecordingData.IncrementUndoCount();
|
||||
incrementUndo = false;
|
||||
}
|
||||
|
||||
if (frameCounter < INT_MAX && !inputRecordingData.WriteKeyBuffer(frameCounter, port, bufIndex, bufVal))
|
||||
inputRec::consoleLog(fmt::format("Failed to write input data at frame {}", frameCounter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 InputRecording::GetFrameCounter()
|
||||
{
|
||||
return frameCounter;
|
||||
}
|
||||
|
||||
InputRecordingFile& InputRecording::GetInputRecordingData()
|
||||
{
|
||||
return inputRecordingData;
|
||||
}
|
||||
|
||||
u32 InputRecording::GetStartingFrame()
|
||||
{
|
||||
return startingFrame;
|
||||
}
|
||||
|
||||
void InputRecording::IncrementFrameCounter()
|
||||
{
|
||||
if (frameCounter < INT_MAX)
|
||||
{
|
||||
frameCounter++;
|
||||
switch (state)
|
||||
{
|
||||
case InputRecordingMode::Recording:
|
||||
inputRecordingData.SetTotalFrames(frameCounter);
|
||||
[[fallthrough]];
|
||||
case InputRecordingMode::Replaying:
|
||||
if (frameCounter == inputRecordingData.GetTotalFrames())
|
||||
incrementUndo = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecording::LogAndRedraw()
|
||||
{
|
||||
for (u8 port = 0; port < 2; port++)
|
||||
{
|
||||
pads[port].padData->LogPadData(port);
|
||||
}
|
||||
}
|
||||
|
||||
bool InputRecording::IsInterruptFrame()
|
||||
{
|
||||
return fInterruptFrame;
|
||||
}
|
||||
|
||||
bool InputRecording::IsActive()
|
||||
{
|
||||
return state != InputRecordingMode::NotActive;
|
||||
}
|
||||
|
||||
bool InputRecording::IsInitialLoad()
|
||||
{
|
||||
return initialLoad;
|
||||
}
|
||||
|
||||
bool InputRecording::IsReplaying()
|
||||
{
|
||||
return state == InputRecordingMode::Replaying;
|
||||
}
|
||||
|
||||
bool InputRecording::IsRecording()
|
||||
{
|
||||
return state == InputRecordingMode::Recording;
|
||||
}
|
||||
|
||||
void InputRecording::SetToRecordMode()
|
||||
{
|
||||
state = InputRecordingMode::Recording;
|
||||
inputRec::log("Record mode ON");
|
||||
}
|
||||
|
||||
void InputRecording::SetToReplayMode()
|
||||
{
|
||||
state = InputRecordingMode::Replaying;
|
||||
inputRec::log("Replay mode ON");
|
||||
}
|
||||
|
||||
void InputRecording::SetFrameCounter(u32 newGFrameCount)
|
||||
{
|
||||
if (newGFrameCount > startingFrame + (u32)inputRecordingData.GetTotalFrames())
|
||||
{
|
||||
inputRec::consoleLog("Warning, you've loaded PCSX2 emulation to a point after the end of the original recording. This should be avoided.");
|
||||
inputRec::consoleLog("Savestate's framecount has been ignored.");
|
||||
frameCounter = inputRecordingData.GetTotalFrames();
|
||||
if (state == InputRecordingMode::Replaying)
|
||||
SetToRecordMode();
|
||||
incrementUndo = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newGFrameCount < startingFrame)
|
||||
{
|
||||
inputRec::consoleLog("Warning, you've loaded PCSX2 emulation to a point before the start of the original recording. This should be avoided.");
|
||||
if (state == InputRecordingMode::Recording)
|
||||
SetToReplayMode();
|
||||
}
|
||||
else if (newGFrameCount == 0 && state == InputRecordingMode::Recording)
|
||||
SetToReplayMode();
|
||||
frameCounter = newGFrameCount - (s32)startingFrame;
|
||||
incrementUndo = true;
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecording::SetupInitialState(u32 newStartingFrame)
|
||||
{
|
||||
startingFrame = newStartingFrame;
|
||||
if (state != InputRecordingMode::Replaying)
|
||||
{
|
||||
inputRec::log("Started new input recording");
|
||||
inputRec::consoleLog(fmt::format("Filename {}", inputRecordingData.GetFilename()));
|
||||
SetToRecordMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the current game matches with the one used to make the original recording
|
||||
// TODO - Vaser - this should be the CRC in hindsight anyway
|
||||
if (!VMManager::GetDiscPath().empty())
|
||||
if (resolveGameName() != inputRecordingData.GetHeader().gameName)
|
||||
inputRec::consoleLog("Input recording was possibly constructed for a different game.");
|
||||
|
||||
incrementUndo = true;
|
||||
inputRec::log("Replaying input recording");
|
||||
inputRec::consoleMultiLog({fmt::format("File: {}", inputRecordingData.GetFilename()),
|
||||
fmt::format("PCSX2 Version Used: {}", std::string(inputRecordingData.GetHeader().emu)),
|
||||
fmt::format("Recording File Version: {}", inputRecordingData.GetHeader().version),
|
||||
fmt::format("Associated Game Name or ISO Filename: {}", std::string(inputRecordingData.GetHeader().gameName)),
|
||||
fmt::format("Author: {}", inputRecordingData.GetHeader().author),
|
||||
fmt::format("Total Frames: {}", inputRecordingData.GetTotalFrames()),
|
||||
fmt::format("Undo Count: {}", inputRecordingData.GetUndoCount())});
|
||||
SetToReplayMode();
|
||||
}
|
||||
|
||||
if (inputRecordingData.FromSaveState())
|
||||
inputRec::consoleLog(fmt::format("Internal Starting Frame: {}", startingFrame));
|
||||
frameCounter = 0;
|
||||
initialLoad = false;
|
||||
g_InputRecordingControls.Lock(startingFrame);
|
||||
}
|
||||
|
||||
void InputRecording::FailedSavestate()
|
||||
{
|
||||
inputRec::consoleLog(fmt::format("{} is not compatible with this version of PCSX2", savestate));
|
||||
inputRec::consoleLog(fmt::format("Original PCSX2 version used: {}", inputRecordingData.GetHeader().emu));
|
||||
inputRecordingData.Close();
|
||||
initialLoad = false;
|
||||
state = InputRecordingMode::NotActive;
|
||||
g_InputRecordingControls.Resume();
|
||||
}
|
||||
|
||||
void InputRecording::Stop()
|
||||
{
|
||||
state = InputRecordingMode::NotActive;
|
||||
incrementUndo = false;
|
||||
if (inputRecordingData.Close())
|
||||
inputRec::log("Input recording stopped");
|
||||
}
|
||||
|
||||
bool InputRecording::Create(const std::string_view& fileName, const bool fromSaveState, const std::string_view& authorName)
|
||||
{
|
||||
if (!inputRecordingData.OpenNew(fileName, fromSaveState))
|
||||
return false;
|
||||
|
||||
initialLoad = true;
|
||||
state = InputRecordingMode::Recording;
|
||||
if (fromSaveState)
|
||||
{
|
||||
savestate = fmt::format("{}_SaveState.p2s", fileName);
|
||||
if (fs::exists(savestate))
|
||||
{
|
||||
fs::copy_file(savestate, fmt::format("{}.bak", savestate));
|
||||
}
|
||||
VMManager::SaveState(savestate.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vaser - CHECK - don't need to specify a source anymore? (cdvd/etc?)
|
||||
VMManager::Execute();
|
||||
}
|
||||
|
||||
// Set emulator version
|
||||
inputRecordingData.GetHeader().SetEmulatorVersion();
|
||||
|
||||
// Set author name
|
||||
if (!authorName.empty())
|
||||
inputRecordingData.GetHeader().SetAuthor(authorName);
|
||||
|
||||
// Set Game Name
|
||||
inputRecordingData.GetHeader().SetGameName(resolveGameName());
|
||||
// Write header contents
|
||||
inputRecordingData.WriteHeader();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputRecording::Play(const fs::path& filename)
|
||||
{
|
||||
if (!inputRecordingData.OpenExisting(filename))
|
||||
return false;
|
||||
|
||||
// Either load the savestate, or restart the game
|
||||
if (inputRecordingData.FromSaveState())
|
||||
{
|
||||
// TODO - Vaser - VM State is atomic, be careful.
|
||||
if (VMManager::GetState() != VMState::Running && VMManager::GetState() != VMState::Paused)
|
||||
{
|
||||
inputRec::consoleLog("Game is not open, aborting playing input recording which starts on a save-state.");
|
||||
inputRecordingData.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
savestate = fmt::format("{}_SaveState.p2s", inputRecordingData.GetFilename());
|
||||
if (!fs::exists(savestate))
|
||||
{
|
||||
inputRec::consoleLog(fmt::format("Could not locate savestate file at location - {}", savestate));
|
||||
inputRec::log("Savestate load failed");
|
||||
inputRecordingData.Close();
|
||||
return false;
|
||||
}
|
||||
state = InputRecordingMode::Replaying;
|
||||
initialLoad = true;
|
||||
VMManager::LoadState(savestate.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
state = InputRecordingMode::Replaying;
|
||||
initialLoad = true;
|
||||
// Vaser - CHECK - don't need to specify a source anymore? (cdvd/etc?)
|
||||
VMManager::Execute();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string InputRecording::resolveGameName()
|
||||
{
|
||||
std::string gameName;
|
||||
// TODO - Vaser - there is probably a way to get rid of the wx usage here i imagine
|
||||
const std::string gameKey(StringUtil::wxStringToUTF8String(SysGetDiscID()));
|
||||
if (!gameKey.empty())
|
||||
{
|
||||
auto game = GameDatabase::findGame(gameKey);
|
||||
if (game)
|
||||
{
|
||||
gameName = game->name;
|
||||
gameName += " (" + game->region + ")";
|
||||
}
|
||||
}
|
||||
// TODO - Vaser - there is probably a way to get rid of the wx usage here i imagine
|
||||
return !gameName.empty() ? gameName : VMManager::GetGameName();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -15,7 +15,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
// TODO - Vaser - kill with wxWidgets
|
||||
|
||||
#include "Recording/InputRecordingFile.h"
|
||||
#include "Recording/VirtualPad/VirtualPad.h"
|
||||
|
@ -145,4 +146,126 @@ private:
|
|||
|
||||
extern InputRecording g_InputRecording;
|
||||
|
||||
#endif
|
||||
#else
|
||||
|
||||
#include "Recording/InputRecordingFile.h"
|
||||
|
||||
class InputRecording
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
POWER_ON,
|
||||
FROM_SAVESTATE
|
||||
};
|
||||
|
||||
// Save or load PCSX2's global frame counter (g_FrameCount) along with each full/fast boot
|
||||
//
|
||||
// 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 RecordingReset();
|
||||
|
||||
// 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[]);
|
||||
|
||||
// The running frame counter for the input recording
|
||||
s32 GetFrameCounter();
|
||||
|
||||
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();
|
||||
|
||||
// If there is currently an input recording being played back or actively being recorded
|
||||
bool IsActive();
|
||||
|
||||
// Whether or not the recording's initial 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 IsInitialLoad();
|
||||
|
||||
// If there is currently an input recording being played back
|
||||
bool IsReplaying();
|
||||
|
||||
// If there are inputs currently being recorded to a file
|
||||
bool IsRecording();
|
||||
|
||||
// Sets input recording to Record Mode
|
||||
void SetToRecordMode();
|
||||
|
||||
// Sets input recording to Replay Mode
|
||||
void SetToReplayMode();
|
||||
|
||||
// Set the running frame counter for the input recording to an arbitrary value
|
||||
void SetFrameCounter(u32 newGFrameCount);
|
||||
|
||||
// Sets up all values and prints console logs pertaining to the start of a recording
|
||||
void SetupInitialState(u32 newStartingFrame);
|
||||
|
||||
/// Functions called from GUI
|
||||
|
||||
// Create a new input recording file
|
||||
bool Create(const std::string_view& filename, const bool fromSaveState, const std::string_view& authorName);
|
||||
// Play an existing input recording from a file
|
||||
// TODO - Vaser - Calls a file dialog if it fails to locate the default base savestate
|
||||
bool Play(const fs::path& path);
|
||||
// Stop the active input recording
|
||||
void Stop();
|
||||
// Logs the padData and redraws the virtualPad windows of active pads
|
||||
void LogAndRedraw();
|
||||
// Resets a recording if the base savestate could not be loaded at the start
|
||||
void FailedSavestate();
|
||||
|
||||
private:
|
||||
enum class InputRecordingMode
|
||||
{
|
||||
NotActive,
|
||||
Recording,
|
||||
Replaying,
|
||||
};
|
||||
|
||||
static const int CONTROLLER_PORT_ONE = 0;
|
||||
static const int CONTROLLER_PORT_TWO = 1;
|
||||
|
||||
// 0x42 is the magic number to indicate the default controller read query
|
||||
// See - PAD.cpp::PADpoll - https://github.com/PCSX2/pcsx2/blob/master/pcsx2/PAD/Windows/PAD.cpp#L1255
|
||||
static const u8 READ_DATA_AND_VIBRATE_FIRST_BYTE = 0x42;
|
||||
// 0x5A is always the second byte in the buffer when the normal READ_DATA_AND_VIBRATE (0x42) query is executed.
|
||||
// See - PAD.cpp::PADpoll - https://github.com/PCSX2/pcsx2/blob/master/pcsx2/PAD/Windows/PAD.cpp#L1256
|
||||
static const u8 READ_DATA_AND_VIBRATE_SECOND_BYTE = 0x5A;
|
||||
|
||||
// DEPRECATED: Slated for removal
|
||||
bool fInterruptFrame = false;
|
||||
InputRecordingFile inputRecordingData;
|
||||
bool initialLoad = false;
|
||||
u32 startingFrame = 0;
|
||||
s32 frameCounter = 0;
|
||||
bool incrementUndo = false;
|
||||
InputRecordingMode state = InputRecording::InputRecordingMode::NotActive;
|
||||
std::string savestate;
|
||||
|
||||
// Array of usable pads (currently, only 2)
|
||||
struct InputRecordingPad
|
||||
{
|
||||
// Controller Data
|
||||
PadData* padData;
|
||||
InputRecordingPad();
|
||||
~InputRecordingPad();
|
||||
} pads[2];
|
||||
|
||||
// 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
|
||||
std::string resolveGameName();
|
||||
};
|
||||
|
||||
extern InputRecording g_InputRecording;
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -15,7 +15,8 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
// TODO - Vaser - kill with wxWidgets
|
||||
|
||||
#include "Counters.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
|
@ -46,7 +47,7 @@ void InputRecordingControls::CheckPauseStatus()
|
|||
g_InputRecording.SetToReplayMode();
|
||||
switchToReplay = false;
|
||||
}
|
||||
|
||||
|
||||
if (IsFinishedReplaying() || g_InputRecording.GetFrameCounter() == INT_MAX)
|
||||
{
|
||||
if (!pauseEmulation)
|
||||
|
@ -200,7 +201,7 @@ void InputRecordingControls::Lock(u32 frame)
|
|||
bool InputRecordingControls::IsFinishedReplaying() const
|
||||
{
|
||||
return g_InputRecording.IsReplaying() &&
|
||||
g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames();
|
||||
g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames();
|
||||
}
|
||||
|
||||
void InputRecordingControls::StopCapture() const
|
||||
|
@ -214,4 +215,209 @@ void InputRecordingControls::StopCapture() const
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include "Counters.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
#include "MemoryTypes.h"
|
||||
|
||||
#include "InputRecording.h"
|
||||
#include "InputRecordingControls.h"
|
||||
#include "Utilities/InputRecordingLogger.h"
|
||||
|
||||
#include "VMManager.h"
|
||||
|
||||
InputRecordingControls g_InputRecordingControls;
|
||||
|
||||
void InputRecordingControls::CheckPauseStatus()
|
||||
{
|
||||
frame_advance_frame_counter++;
|
||||
if (frameAdvancing && frame_advance_frame_counter >= frames_per_frame_advance)
|
||||
{
|
||||
frameAdvancing = false;
|
||||
pauseEmulation = true;
|
||||
}
|
||||
|
||||
if (g_InputRecording.IsActive())
|
||||
{
|
||||
g_InputRecording.IncrementFrameCounter();
|
||||
|
||||
if (switchToReplay)
|
||||
{
|
||||
g_InputRecording.SetToReplayMode();
|
||||
switchToReplay = false;
|
||||
}
|
||||
|
||||
if (IsFinishedReplaying() || g_InputRecording.GetFrameCounter() == INT_MAX)
|
||||
{
|
||||
if (!pauseEmulation)
|
||||
pauseEmulation = true;
|
||||
StopCapture();
|
||||
}
|
||||
}
|
||||
g_InputRecording.LogAndRedraw();
|
||||
}
|
||||
|
||||
void InputRecordingControls::HandlePausingAndLocking()
|
||||
{
|
||||
// Explicit frame locking
|
||||
if (frameLock)
|
||||
{
|
||||
if (g_FrameCount == frameLockTracker)
|
||||
{
|
||||
frameLock = false;
|
||||
Resume();
|
||||
}
|
||||
else if (!emulationCurrentlyPaused && (VMManager::GetState() == VMState::Running || VMManager::GetState() != VMState::Paused))
|
||||
{
|
||||
emulationCurrentlyPaused = true;
|
||||
VMManager::SetPaused(true);
|
||||
}
|
||||
}
|
||||
else if (pauseEmulation && (VMManager::GetState() == VMState::Running || VMManager::GetState() != VMState::Paused))
|
||||
{
|
||||
emulationCurrentlyPaused = true;
|
||||
VMManager::SetPaused(true);
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecordingControls::ResumeCoreThreadIfStarted()
|
||||
{
|
||||
if (resumeEmulation && (VMManager::GetState() == VMState::Running || VMManager::GetState() == VMState::Paused))
|
||||
{
|
||||
VMManager::SetPaused(false);
|
||||
resumeEmulation = false;
|
||||
emulationCurrentlyPaused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecordingControls::FrameAdvance()
|
||||
{
|
||||
if (!IsFinishedReplaying())
|
||||
{
|
||||
frameAdvancing = true;
|
||||
frame_advance_frame_counter = 0;
|
||||
Resume();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_InputRecording.SetToRecordMode();
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecordingControls::setFrameAdvanceAmount(int amount)
|
||||
{
|
||||
frames_per_frame_advance = amount;
|
||||
}
|
||||
|
||||
bool InputRecordingControls::IsFrameAdvancing()
|
||||
{
|
||||
return frameAdvancing;
|
||||
}
|
||||
|
||||
bool InputRecordingControls::IsPaused()
|
||||
{
|
||||
return emulationCurrentlyPaused && VMManager::GetState() == VMState::Paused;
|
||||
}
|
||||
|
||||
void InputRecordingControls::Pause()
|
||||
{
|
||||
pauseEmulation = true;
|
||||
resumeEmulation = false;
|
||||
}
|
||||
|
||||
void InputRecordingControls::PauseImmediately()
|
||||
{
|
||||
if (VMManager::GetState() != VMState::Paused)
|
||||
{
|
||||
Pause();
|
||||
if ((VMManager::GetState() == VMState::Running || VMManager::GetState() == VMState::Paused))
|
||||
{
|
||||
emulationCurrentlyPaused = true;
|
||||
VMManager::SetPaused(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecordingControls::Resume()
|
||||
{
|
||||
if (!IsFinishedReplaying())
|
||||
{
|
||||
pauseEmulation = false;
|
||||
resumeEmulation = true;
|
||||
}
|
||||
else
|
||||
g_InputRecording.SetToRecordMode();
|
||||
}
|
||||
|
||||
void InputRecordingControls::ResumeImmediately()
|
||||
{
|
||||
if (VMManager::GetState() == VMState::Paused)
|
||||
{
|
||||
Resume();
|
||||
if ((VMManager::GetState() == VMState::Running || VMManager::GetState() == VMState::Paused))
|
||||
{
|
||||
emulationCurrentlyPaused = false;
|
||||
VMManager::SetPaused(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecordingControls::TogglePause()
|
||||
{
|
||||
if (!pauseEmulation || !IsFinishedReplaying())
|
||||
{
|
||||
resumeEmulation = pauseEmulation;
|
||||
pauseEmulation = !pauseEmulation;
|
||||
inputRec::log(pauseEmulation ? "Paused Emulation" : "Resumed Emulation");
|
||||
}
|
||||
else
|
||||
g_InputRecording.SetToRecordMode();
|
||||
}
|
||||
|
||||
void InputRecordingControls::RecordModeToggle()
|
||||
{
|
||||
if (g_InputRecording.IsReplaying())
|
||||
g_InputRecording.SetToRecordMode();
|
||||
else if (g_InputRecording.IsRecording())
|
||||
{
|
||||
if (IsPaused() || g_InputRecording.GetFrameCounter() < g_InputRecording.GetInputRecordingData().GetTotalFrames())
|
||||
g_InputRecording.SetToReplayMode();
|
||||
else
|
||||
switchToReplay = true;
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecordingControls::Lock(u32 frame)
|
||||
{
|
||||
frameLock = true;
|
||||
frameLockTracker = frame;
|
||||
frameAdvancing = false;
|
||||
// Ensures that g_frameCount can be used to resume emulation after a fast/full boot
|
||||
if (!g_InputRecording.GetInputRecordingData().FromSaveState())
|
||||
{
|
||||
g_FrameCount = frame + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool InputRecordingControls::IsFinishedReplaying() const
|
||||
{
|
||||
return g_InputRecording.IsReplaying() &&
|
||||
g_InputRecording.GetFrameCounter() >= g_InputRecording.GetInputRecordingData().GetTotalFrames();
|
||||
}
|
||||
|
||||
void InputRecordingControls::StopCapture() const
|
||||
{
|
||||
// TODO - Vaser - Is capturing supported in Qt yet - Check
|
||||
/*if (MainEmuFrame* mainFrame = GetMainFramePtr())
|
||||
{
|
||||
if (mainFrame->IsCapturing())
|
||||
{
|
||||
mainFrame->VideoCaptureToggle();
|
||||
inputRec::log("Capture completed");
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -15,7 +15,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
// TODO - Vaser - kill with wxWidgets
|
||||
|
||||
class InputRecordingControls
|
||||
{
|
||||
|
@ -98,4 +99,87 @@ private:
|
|||
|
||||
extern InputRecordingControls g_InputRecordingControls;
|
||||
|
||||
#else
|
||||
|
||||
class InputRecordingControls
|
||||
{
|
||||
public:
|
||||
// Intended to be called at the end of each frame, but will no-op if frame lock is active
|
||||
//
|
||||
// Will set the pausing parameters for 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 CheckPauseStatus();
|
||||
|
||||
// When loading a recording file or booting with a recording active, lock will be enabled.
|
||||
// Emulation will be forced into and remain in a paused state until the transition in progress
|
||||
// has completed - signaled when g_framecount and frameCountTracker are equal
|
||||
//
|
||||
// This function also handles actually pausing emulation when told to
|
||||
void HandlePausingAndLocking();
|
||||
|
||||
// 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();
|
||||
void setFrameAdvanceAmount(int amount);
|
||||
// Returns true if emulation is currently set up to frame advance.
|
||||
bool IsFrameAdvancing();
|
||||
// Returns true if the input recording has been paused, which can occur:
|
||||
// - After a single frame has passed after InputRecordingControls::FrameAdvance
|
||||
// - Explicitly paused via an InputRecordingControls function
|
||||
bool IsPaused();
|
||||
// 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();
|
||||
/**
|
||||
* @brief Resumes emulation immediately, don't wait until the next VSync
|
||||
*/
|
||||
void ResumeImmediately();
|
||||
// Alternates emulation between a paused and unpaused state
|
||||
void TogglePause();
|
||||
// Switches between recording and replaying the active input recording file
|
||||
void RecordModeToggle();
|
||||
// Enables the frame locking mechanism so that when recordings are loaded
|
||||
// or when processing a reboot with a recording active that no frames are
|
||||
// lost in prior emulation
|
||||
void Lock(u32 frame);
|
||||
|
||||
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;
|
||||
u32 frame_advance_frame_counter = 0;
|
||||
u32 frames_per_frame_advance = 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;
|
||||
// Indicates to switch to replay mode after the next vsync
|
||||
bool switchToReplay = false;
|
||||
// Used to stop recording frames from incrementing during a reset
|
||||
bool frameLock = false;
|
||||
// The frame value to use as the frame lock reset point
|
||||
u32 frameLockTracker = 0;
|
||||
|
||||
bool IsFinishedReplaying() const;
|
||||
// Calls mainEmuFrame's videoCaptureToggle to end a capture if active
|
||||
void StopCapture() const;
|
||||
};
|
||||
|
||||
extern InputRecordingControls g_InputRecordingControls;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -15,7 +15,8 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
// TODO - Vaser - kill with wxWidgets
|
||||
|
||||
#include "DebugTools/Debug.h"
|
||||
#include "gui/MainFrame.h"
|
||||
|
@ -66,22 +67,22 @@ bool InputRecordingFile::Close()
|
|||
return true;
|
||||
}
|
||||
|
||||
const wxString &InputRecordingFile::GetFilename()
|
||||
const wxString& InputRecordingFile::GetFilename()
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
InputRecordingFileHeader &InputRecordingFile::GetHeader()
|
||||
InputRecordingFileHeader& InputRecordingFile::GetHeader()
|
||||
{
|
||||
return header;
|
||||
}
|
||||
|
||||
long &InputRecordingFile::GetTotalFrames()
|
||||
long& InputRecordingFile::GetTotalFrames()
|
||||
{
|
||||
return totalFrames;
|
||||
}
|
||||
|
||||
unsigned long &InputRecordingFile::GetUndoCount()
|
||||
unsigned long& InputRecordingFile::GetUndoCount()
|
||||
{
|
||||
return undoCount;
|
||||
}
|
||||
|
@ -143,7 +144,7 @@ bool InputRecordingFile::OpenExisting(const wxString& path)
|
|||
return open(path, false);
|
||||
}
|
||||
|
||||
bool InputRecordingFile::ReadKeyBuffer(u8 &result, const uint &frame, const uint port, const uint bufIndex)
|
||||
bool InputRecordingFile::ReadKeyBuffer(u8& result, const uint& frame, const uint port, const uint bufIndex)
|
||||
{
|
||||
if (recordingFile == nullptr)
|
||||
{
|
||||
|
@ -177,17 +178,14 @@ bool InputRecordingFile::WriteHeader()
|
|||
return false;
|
||||
}
|
||||
rewind(recordingFile);
|
||||
if (fwrite(&header, sizeof(InputRecordingFileHeader), 1, recordingFile) != 1
|
||||
|| fwrite(&totalFrames, 4, 1, recordingFile) != 1
|
||||
|| fwrite(&undoCount, 4, 1, recordingFile) != 1
|
||||
|| fwrite(&savestate, 1, 1, recordingFile) != 1)
|
||||
if (fwrite(&header, sizeof(InputRecordingFileHeader), 1, recordingFile) != 1 || fwrite(&totalFrames, 4, 1, recordingFile) != 1 || fwrite(&undoCount, 4, 1, recordingFile) != 1 || fwrite(&savestate, 1, 1, recordingFile) != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputRecordingFile::WriteKeyBuffer(const uint &frame, const uint port, const uint bufIndex, const u8 &buf)
|
||||
bool InputRecordingFile::WriteKeyBuffer(const uint& frame, const uint port, const uint bufIndex, const u8& buf)
|
||||
{
|
||||
if (recordingFile == nullptr)
|
||||
{
|
||||
|
@ -205,7 +203,7 @@ bool InputRecordingFile::WriteKeyBuffer(const uint &frame, const uint port, cons
|
|||
return true;
|
||||
}
|
||||
|
||||
long InputRecordingFile::getRecordingBlockSeekPoint(const long &frame)
|
||||
long InputRecordingFile::getRecordingBlockSeekPoint(const long& frame)
|
||||
{
|
||||
return headerSize + sizeof(bool) + frame * inputBytesPerFrame;
|
||||
}
|
||||
|
@ -218,14 +216,11 @@ bool InputRecordingFile::verifyRecordingFileHeader()
|
|||
}
|
||||
// Verify header contents
|
||||
rewind(recordingFile);
|
||||
if (fread(&header, sizeof(InputRecordingFileHeader), 1, recordingFile) != 1
|
||||
|| fread(&totalFrames, 4, 1, recordingFile) != 1
|
||||
|| fread(&undoCount, 4, 1, recordingFile) != 1
|
||||
|| fread(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1)
|
||||
if (fread(&header, sizeof(InputRecordingFileHeader), 1, recordingFile) != 1 || fread(&totalFrames, 4, 1, recordingFile) != 1 || fread(&undoCount, 4, 1, recordingFile) != 1 || fread(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check for current verison
|
||||
if (header.version != 1)
|
||||
{
|
||||
|
@ -234,4 +229,220 @@ bool InputRecordingFile::verifyRecordingFileHeader()
|
|||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include "InputRecordingFile.h"
|
||||
|
||||
#include "Utilities/InputRecordingLogger.h"
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
#include "MemoryTypes.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
void InputRecordingFileHeader::Init()
|
||||
{
|
||||
memset(author, 0, std::size(author));
|
||||
memset(gameName, 0, std::size(gameName));
|
||||
}
|
||||
|
||||
void InputRecordingFileHeader::SetEmulatorVersion()
|
||||
{
|
||||
std::string emuVersion = fmt::format("PCSX2-{}.{}.{}", PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo);
|
||||
int max = std::size(emu) - 1;
|
||||
strncpy(emu, emuVersion.c_str(), max);
|
||||
emu[max] = 0;
|
||||
}
|
||||
|
||||
void InputRecordingFileHeader::SetAuthor(const std::string_view& _author)
|
||||
{
|
||||
int max = std::size(author) - 1;
|
||||
strncpy(author, _author.data(), max);
|
||||
author[max] = 0;
|
||||
}
|
||||
|
||||
void InputRecordingFileHeader::SetGameName(const std::string_view& _gameName)
|
||||
{
|
||||
int max = std::size(gameName) - 1;
|
||||
strncpy(gameName, _gameName.data(), max);
|
||||
gameName[max] = 0;
|
||||
}
|
||||
|
||||
bool InputRecordingFile::Close()
|
||||
{
|
||||
if (recordingFile == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
fclose(recordingFile);
|
||||
recordingFile = nullptr;
|
||||
filename = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& InputRecordingFile::GetFilename()
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
InputRecordingFileHeader& InputRecordingFile::GetHeader()
|
||||
{
|
||||
return header;
|
||||
}
|
||||
|
||||
long& InputRecordingFile::GetTotalFrames()
|
||||
{
|
||||
return totalFrames;
|
||||
}
|
||||
|
||||
unsigned long& InputRecordingFile::GetUndoCount()
|
||||
{
|
||||
return undoCount;
|
||||
}
|
||||
|
||||
bool InputRecordingFile::FromSaveState()
|
||||
{
|
||||
return savestate.fromSavestate;
|
||||
}
|
||||
|
||||
void InputRecordingFile::IncrementUndoCount()
|
||||
{
|
||||
undoCount++;
|
||||
if (recordingFile == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
fseek(recordingFile, seekpointUndoCount, SEEK_SET);
|
||||
fwrite(&undoCount, 4, 1, recordingFile);
|
||||
}
|
||||
|
||||
bool InputRecordingFile::open(const fs::path& path, bool newRecording)
|
||||
{
|
||||
if (newRecording)
|
||||
{
|
||||
if ((recordingFile = FileSystem::OpenCFile(path.string().c_str(), "wb+")) != nullptr)
|
||||
{
|
||||
filename = path.string();
|
||||
totalFrames = 0;
|
||||
undoCount = 0;
|
||||
header.Init();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ((recordingFile = FileSystem::OpenCFile(path.string().c_str(), "rb+")) != nullptr)
|
||||
{
|
||||
if (verifyRecordingFileHeader())
|
||||
{
|
||||
filename = path.string();
|
||||
return true;
|
||||
}
|
||||
Close();
|
||||
inputRec::consoleLog("Input recording file header is invalid");
|
||||
return false;
|
||||
}
|
||||
inputRec::consoleLog(fmt::format("Input recording file opening failed. Error - {}", strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputRecordingFile::OpenNew(const fs::path& path, bool fromSavestate)
|
||||
{
|
||||
if (!open(path, true))
|
||||
return false;
|
||||
savestate.fromSavestate = fromSavestate;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputRecordingFile::OpenExisting(const fs::path& path)
|
||||
{
|
||||
return open(path, false);
|
||||
}
|
||||
|
||||
bool InputRecordingFile::ReadKeyBuffer(u8& result, const uint& frame, const uint port, const uint bufIndex)
|
||||
{
|
||||
if (recordingFile == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long seek = getRecordingBlockSeekPoint(frame) + controllerInputBytes * port + bufIndex;
|
||||
if (fseek(recordingFile, seek, SEEK_SET) != 0 || fread(&result, 1, 1, recordingFile) != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputRecordingFile::SetTotalFrames(long frame)
|
||||
{
|
||||
if (recordingFile == nullptr || totalFrames >= frame)
|
||||
{
|
||||
return;
|
||||
}
|
||||
totalFrames = frame;
|
||||
fseek(recordingFile, seekpointTotalFrames, SEEK_SET);
|
||||
fwrite(&totalFrames, 4, 1, recordingFile);
|
||||
}
|
||||
|
||||
bool InputRecordingFile::WriteHeader()
|
||||
{
|
||||
if (recordingFile == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
rewind(recordingFile);
|
||||
if (fwrite(&header, sizeof(InputRecordingFileHeader), 1, recordingFile) != 1 || fwrite(&totalFrames, 4, 1, recordingFile) != 1 || fwrite(&undoCount, 4, 1, recordingFile) != 1 || fwrite(&savestate, 1, 1, recordingFile) != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputRecordingFile::WriteKeyBuffer(const uint& frame, const uint port, const uint bufIndex, const u8& buf)
|
||||
{
|
||||
if (recordingFile == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long seek = getRecordingBlockSeekPoint(frame) + 18 * port + bufIndex;
|
||||
|
||||
if (fseek(recordingFile, seek, SEEK_SET) != 0 || fwrite(&buf, 1, 1, recordingFile) != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fflush(recordingFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
long InputRecordingFile::getRecordingBlockSeekPoint(const long& frame)
|
||||
{
|
||||
return headerSize + sizeof(bool) + frame * inputBytesPerFrame;
|
||||
}
|
||||
|
||||
bool InputRecordingFile::verifyRecordingFileHeader()
|
||||
{
|
||||
if (recordingFile == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Verify header contents
|
||||
rewind(recordingFile);
|
||||
if (fread(&header, sizeof(InputRecordingFileHeader), 1, recordingFile) != 1 || fread(&totalFrames, 4, 1, recordingFile) != 1 || fread(&undoCount, 4, 1, recordingFile) != 1 || fread(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for current verison
|
||||
if (header.version != 1)
|
||||
{
|
||||
inputRec::consoleLog(fmt::format("Input recording file is not a supported version - {}", header.version));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -15,10 +15,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
// TODO - Vaser - kill with wxWidgets
|
||||
|
||||
#include "System.h"
|
||||
|
||||
#include "PadData.h"
|
||||
|
||||
// NOTE / TODOs for Version 2
|
||||
|
@ -52,18 +52,18 @@ class InputRecordingFile
|
|||
public:
|
||||
~InputRecordingFile() { Close(); }
|
||||
|
||||
// Closes the underlying input recording file, writing the header and
|
||||
// Closes the underlying input recording file, writing the header and
|
||||
// prepares for a possible new recording to be started
|
||||
bool Close();
|
||||
// Retrieve the input recording's filename (not the path)
|
||||
const wxString &GetFilename();
|
||||
const wxString& GetFilename();
|
||||
// Retrieve the input recording's header which contains high-level metadata on the recording
|
||||
InputRecordingFileHeader &GetHeader();
|
||||
InputRecordingFileHeader& GetHeader();
|
||||
// The maximum number of frames, or in other words, the length of the recording
|
||||
long &GetTotalFrames();
|
||||
long& GetTotalFrames();
|
||||
// The number of times a save-state has been loaded while recording this movie
|
||||
// this is also often referred to as a "re-record"
|
||||
unsigned long &GetUndoCount();
|
||||
unsigned long& GetUndoCount();
|
||||
// Whether or not this input recording starts by loading a save-state or by booting the game fresh
|
||||
bool FromSaveState();
|
||||
// Increment the number of undo actions and commit it to the recording file
|
||||
|
@ -75,13 +75,13 @@ public:
|
|||
bool OpenNew(const wxString& path, bool fromSaveState);
|
||||
// Reads the current frame's input data from the file in order to intercept and overwrite
|
||||
// the current frame's value from the emulator
|
||||
bool ReadKeyBuffer(u8 &result, const uint &frame, const uint port, const uint bufIndex);
|
||||
bool ReadKeyBuffer(u8& result, const uint& frame, const uint port, const uint bufIndex);
|
||||
// Updates the total frame counter and commit it to the recording file
|
||||
void SetTotalFrames(long frames);
|
||||
// Persist the input recording file header's current state to the file
|
||||
bool WriteHeader();
|
||||
// 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);
|
||||
bool WriteKeyBuffer(const uint& frame, const uint port, const uint bufIndex, const u8& buf);
|
||||
|
||||
private:
|
||||
static const int controllerPortsSupported = 2;
|
||||
|
@ -111,4 +111,99 @@ private:
|
|||
bool verifyRecordingFileHeader();
|
||||
};
|
||||
|
||||
#endif
|
||||
#else
|
||||
|
||||
#include "System.h"
|
||||
#include "PadData.h"
|
||||
|
||||
// NOTE / TODOs for Version 2
|
||||
// - Move fromSavestate, undoCount, and total frames into the header
|
||||
|
||||
struct InputRecordingFileHeader
|
||||
{
|
||||
u8 version = 1;
|
||||
char emu[50] = "";
|
||||
char author[255] = "";
|
||||
char gameName[255] = "";
|
||||
|
||||
public:
|
||||
void SetEmulatorVersion();
|
||||
void Init();
|
||||
void SetAuthor(const std::string_view& author);
|
||||
void SetGameName(const std::string_view& cdrom);
|
||||
};
|
||||
|
||||
|
||||
// DEPRECATED / Slated for Removal
|
||||
struct InputRecordingSavestate
|
||||
{
|
||||
// Whether we start from the savestate or from power-on
|
||||
bool fromSavestate = false;
|
||||
};
|
||||
|
||||
// Handles all operations on the input recording file
|
||||
class InputRecordingFile
|
||||
{
|
||||
public:
|
||||
~InputRecordingFile() { Close(); }
|
||||
|
||||
// Closes the underlying input recording file, writing the header and
|
||||
// prepares for a possible new recording to be started
|
||||
bool Close();
|
||||
// Retrieve the input recording's filename (not the path)
|
||||
const std::string& GetFilename();
|
||||
// Retrieve the input recording's header which contains high-level metadata on the recording
|
||||
InputRecordingFileHeader& GetHeader();
|
||||
// The maximum number of frames, or in other words, the length of the recording
|
||||
long& GetTotalFrames();
|
||||
// The number of times a save-state has been loaded while recording this movie
|
||||
// this is also often referred to as a "re-record"
|
||||
unsigned long& GetUndoCount();
|
||||
// Whether or not this input recording starts by loading a save-state or by booting the game fresh
|
||||
bool FromSaveState();
|
||||
// Increment the number of undo actions and commit it to the recording file
|
||||
void IncrementUndoCount();
|
||||
// Open an existing recording file
|
||||
bool OpenExisting(const fs::path& path);
|
||||
// Create and open a brand new input recording, either starting from a save-state or from
|
||||
// booting the game
|
||||
bool OpenNew(const fs::path& path, bool fromSaveState);
|
||||
// Reads the current frame's input data from the file in order to intercept and overwrite
|
||||
// the current frame's value from the emulator
|
||||
bool ReadKeyBuffer(u8& result, const uint& frame, const uint port, const uint bufIndex);
|
||||
// Updates the total frame counter and commit it to the recording file
|
||||
void SetTotalFrames(long frames);
|
||||
// Persist the input recording file header's current state to the file
|
||||
bool WriteHeader();
|
||||
// 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);
|
||||
|
||||
private:
|
||||
static const int controllerPortsSupported = 2;
|
||||
static const int controllerInputBytes = 18;
|
||||
static const int inputBytesPerFrame = controllerInputBytes * controllerPortsSupported;
|
||||
// TODO - version 2, this could be greatly simplified if everything was in the header
|
||||
// + 4 + 4 is the totalFrame and undoCount values
|
||||
static const int headerSize = sizeof(InputRecordingFileHeader) + 4 + 4;
|
||||
// DEPRECATED / Slated for Removal
|
||||
static const int recordingSavestateHeaderSize = sizeof(bool);
|
||||
static const int seekpointTotalFrames = sizeof(InputRecordingFileHeader);
|
||||
static const int seekpointUndoCount = sizeof(InputRecordingFileHeader) + 4;
|
||||
static const int seekpointSaveStateHeader = seekpointUndoCount + 4;
|
||||
|
||||
InputRecordingFileHeader header;
|
||||
std::string filename = "";
|
||||
FILE* recordingFile = nullptr;
|
||||
InputRecordingSavestate savestate;
|
||||
|
||||
// An signed 32-bit frame limit is equivalent to 1.13 years of continuous 60fps footage
|
||||
long totalFrames = 0;
|
||||
unsigned long undoCount = 0;
|
||||
|
||||
// Calculates the position of the current frame in the input recording
|
||||
long getRecordingBlockSeekPoint(const long& frame);
|
||||
bool open(const fs::path& path, bool newRecording);
|
||||
bool verifyRecordingFileHeader();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -13,9 +13,10 @@
|
|||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
// This file dies along with wxWidgets
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "NewRecordingFrame.h"
|
||||
|
||||
|
@ -157,4 +158,4 @@ int NewRecordingFrame::GetFrom() const
|
|||
{
|
||||
return m_fromChoice->GetSelection();
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2019 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -15,7 +15,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/filepicker.h>
|
||||
|
@ -58,4 +58,4 @@ private:
|
|||
wxString m_savestate_label;
|
||||
wxStaticText* m_warning_label;
|
||||
};
|
||||
#endif
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -14,13 +14,11 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
|
||||
#include "DebugTools/Debug.h"
|
||||
|
||||
#include "Recording/PadData.h"
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
void PadData::UpdateControllerData(u16 bufIndex, u8 const& bufVal)
|
||||
{
|
||||
const BufferIndex index = static_cast<BufferIndex>(bufIndex);
|
||||
|
@ -100,7 +98,7 @@ void PadData::UpdateControllerData(u16 bufIndex, u8 const& bufVal)
|
|||
u8 PadData::PollControllerData(u16 bufIndex)
|
||||
{
|
||||
u8 byte = 0;
|
||||
BufferIndex index = static_cast<BufferIndex>(bufIndex);
|
||||
const BufferIndex index = static_cast<BufferIndex>(bufIndex);
|
||||
switch (index)
|
||||
{
|
||||
case BufferIndex::PressedFlagsGroupOne:
|
||||
|
@ -177,6 +175,9 @@ u8 PadData::BitmaskOrZero(bool pressed, ButtonResolver buttonInfo)
|
|||
return pressed ? buttonInfo.buttonBitmask : 0;
|
||||
}
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
// TODO - Vaser - kill with wxWidgets
|
||||
// TODO - Vaser - replace with this something better in Qt
|
||||
wxString PadData::RawPadBytesToString(int start, int end)
|
||||
{
|
||||
wxString str;
|
||||
|
@ -203,4 +204,33 @@ void PadData::LogPadData(u8 const& port)
|
|||
controlLog(fullLog.ToUTF8());
|
||||
}
|
||||
|
||||
#endif
|
||||
#else
|
||||
|
||||
std::string PadData::RawPadBytesToString(int start, int end)
|
||||
{
|
||||
std::string str;
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
str += fmt::format("{}", PollControllerData(i));
|
||||
|
||||
if (i != end - 1)
|
||||
str += ", ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void PadData::LogPadData(u8 const& port)
|
||||
{
|
||||
std::string pressedBytes = RawPadBytesToString(0, 2);
|
||||
std::string rightAnalogBytes = RawPadBytesToString(2, 4);
|
||||
std::string leftAnalogBytes = RawPadBytesToString(4, 6);
|
||||
std::string pressureBytes = RawPadBytesToString(6, 17);
|
||||
std::string fullLog =
|
||||
fmt::format("[PAD {}] Raw Bytes: Pressed = [{}]\n", port + 1, pressedBytes) +
|
||||
fmt::format("[PAD {}] Raw Bytes: Right Analog = [{}]\n", port + 1, rightAnalogBytes) +
|
||||
fmt::format("[PAD {}] Raw Bytes: Left Analog = [{}]\n", port + 1, leftAnalogBytes) +
|
||||
fmt::format("[PAD {}] Raw Bytes: Pressure = [{}]\n", port + 1, pressureBytes);
|
||||
controlLog(fullLog);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -15,8 +15,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
|
||||
class PadData
|
||||
{
|
||||
public:
|
||||
|
@ -122,7 +120,9 @@ private:
|
|||
bool IsButtonPressed(ButtonResolver buttonResolver, u8 const& bufVal);
|
||||
u8 BitmaskOrZero(bool pressed, ButtonResolver buttonInfo);
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
wxString RawPadBytesToString(int start, int end);
|
||||
};
|
||||
|
||||
#else
|
||||
std::string RawPadBytesToString(int start, int end);
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
@ -20,8 +20,6 @@
|
|||
#include "DebugTools/Debug.h"
|
||||
#include "common/Console.h"
|
||||
#include "GS.h" // GSosdlog
|
||||
#include "gui/App.h" // GetRGBA
|
||||
#include "gui/ConsoleLogger.h"
|
||||
#include "Host.h"
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2022 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-
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
#include <math.h>
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
#include "Recording/VirtualPad/VirtualPadData.h"
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
#include "common/Pcsx2Types.h"
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
#include <wx/spinctrl.h>
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
#ifndef PCSX2_CORE
|
||||
|
||||
#include <queue>
|
||||
|
||||
|
|
Loading…
Reference in New Issue