GUI/Recording: Resolve issues around playing and creating input recordings under certain scenarios (#3669)

* recording: Only stop the current recording once the menuing hasnt been cancelled

* recording: Avoid changing menu option's state if the recording couldn't be played

* recording: Don't play a recording that uses a savestate, if no game is running

* recording: Don't modify `frameAdvance` flag when explicitly [un]pausing

These functions are only called when creating/playing a recording, and modifying the frameAdvance flag leads to unexpected behaviour (such as the game starting paused if the recording was made from "power-on")

* recording: Refactor and simplify `InputRecordingFile`

InputRecordingFile will no longer be concerned with loading the save-state when playing back an existing recording.  This makes it much easier to only load the save-state if the file is valid and manipulate the emulation state correctly.

* recording: Update play logic with new refactor, resume emulation in the event of a failure

* recording/lint: spaces to tabs

* recording: Properly only examine controller 1A & 2A

The previous controller port checks in place never actually succeeded in their designated task. A new slot check in sio.cpp will perform this task instead.

* recording: Save the savestate in OpenNew() instead of open()

Ensures that the savesate could be saved before trying to create the actual input recording file. It will overwrite any previous backup savestate.
Also, allows for a simplified & easier to read code struture of open().

* Refactor and simplify `InputRecording`

Changes the return type of Play/Create from void to bool.
Optimizes Stop(), Pause(), and Unpause() call placements

Improved handling of emulation pause state, the recording menu on failures, and the conditioning of when a recording file should actually be unloaded.
For example, a currently loaded recording should not get unloaded if a user presses Play *but* chooses cancel in the file browser. However, the emulation should be paused during the duration of this action.
On the flipside, a loaded recording *should* get unloaded if the tools get disabled in settings AND emulation should resume if not already playing.

* recording: Simplify VirtualPad_Open_Click

Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
This commit is contained in:
sonicfind 2020-09-11 15:30:56 -05:00 committed by GitHub
parent 4d0650baa0
commit 4d66818746
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 330 additions and 441 deletions

View File

@ -37,7 +37,7 @@ void SaveStateBase::InputRecordingFreeze()
#ifndef DISABLE_RECORDING
if (g_FrameCount > 0 && IsLoading())
{
g_InputRecordingData.AddUndoCount();
g_InputRecordingData.IncrementUndoCount();
}
#endif
}
@ -50,11 +50,6 @@ InputRecording g_InputRecording;
void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 buf[])
{
// TODO - Multi-Tap Support
// Only examine controllers 1 / 2
if (port != 0 && port != 1)
{
return;
}
/*
This appears to try to ensure that we are only paying attention
@ -99,19 +94,19 @@ void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 b
const u8& nowBuf = buf[bufCount];
if (state == INPUT_RECORDING_MODE_RECORD)
{
InputRecordingData.UpdateFrameMax(g_FrameCount);
InputRecordingData.WriteKeyBuf(g_FrameCount, port, bufCount - 3, nowBuf);
InputRecordingData.SetTotalFrames(g_FrameCount);
InputRecordingData.WriteKeyBuffer(g_FrameCount, port, bufCount - 3, nowBuf);
}
else if (state == INPUT_RECORDING_MODE_REPLAY)
{
if (InputRecordingData.GetMaxFrame() <= g_FrameCount)
if (InputRecordingData.GetTotalFrames() <= g_FrameCount)
{
// Pause the emulation but the movie is not closed
g_RecordingControls.Pause();
return;
}
u8 tmp = 0;
if (InputRecordingData.ReadKeyBuf(tmp, g_FrameCount, port, bufCount - 3))
if (InputRecordingData.ReadKeyBuffer(tmp, g_FrameCount, port, bufCount - 3))
{
buf[bufCount] = tmp;
}
@ -130,15 +125,11 @@ void InputRecording::Stop()
}
// GUI Handler - Start recording
void InputRecording::Create(wxString FileName, bool fromSaveState, wxString authorName)
bool InputRecording::Create(wxString FileName, bool fromSaveState, wxString authorName)
{
g_RecordingControls.Pause();
Stop();
// create
if (!InputRecordingData.Open(FileName, true, fromSaveState))
if (!InputRecordingData.OpenNew(FileName, fromSaveState))
{
return;
return false;
}
// Set emulator version
InputRecordingData.GetHeader().SetEmulatorVersion();
@ -157,40 +148,62 @@ void InputRecording::Create(wxString FileName, bool fromSaveState, wxString auth
// In every case, we reset the g_FrameCount
g_FrameCount = 0;
return true;
}
// GUI Handler - Play a recording
void InputRecording::Play(wxString FileName, bool fromSaveState)
bool InputRecording::Play(wxString fileName)
{
g_RecordingControls.Pause();
Stop();
if (state != INPUT_RECORDING_MODE_NONE)
Stop();
if (!InputRecordingData.Open(FileName, false, false))
// Open the file and verify if it can be played
if (!InputRecordingData.OpenExisting(fileName))
{
return;
return false;
}
if (!InputRecordingData.ReadHeaderAndCheck())
// Either load the savestate, or restart the game
if (InputRecordingData.FromSaveState())
{
recordingConLog(L"[REC]: This file is not a correct InputRecording file.\n");
InputRecordingData.Close();
return;
if (!CoreThread.IsOpen())
{
recordingConLog(L"[REC]: Game is not open, aborting playing input recording which starts on a save-state.\n");
InputRecordingData.Close();
return false;
}
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();
return false;
}
fclose(ssFileCheck);
StateCopy_LoadFromFile(InputRecordingData.GetFilename() + "_SaveState.p2s");
}
// Check author name
else
{
g_RecordingControls.Unpause();
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)
{
recordingConLog(L"[REC]: Recording was possibly recorded on a different game.\n");
recordingConLog(L"[REC]: Recording was possibly constructed for a different game.\n");
}
}
state = INPUT_RECORDING_MODE_REPLAY;
recordingConLog(wxString::Format(L"[REC]: Replaying movie - [%s]\n", FileName));
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]: MaxFrame: %d\n", InputRecordingData.GetMaxFrame()));
recordingConLog(wxString::Format(L"[REC]: UndoCount: %d\n", InputRecordingData.GetUndoCount()));
recordingConLog(wxString::Format(L"[REC]: Total Frames: %d\n", InputRecordingData.GetTotalFrames()));
recordingConLog(wxString::Format(L"[REC]: Undo Count: %d\n", InputRecordingData.GetUndoCount()));
return true;
}
wxString InputRecording::resolveGameName()

View File

@ -41,8 +41,8 @@ public:
bool IsInterruptFrame();
void Stop();
void Create(wxString filename, bool fromSaveState, wxString authorName);
void Play(wxString filename, bool fromSaveState);
bool Create(wxString filename, bool fromSaveState, wxString authorName);
bool Play(wxString filename);
private:
InputRecordingFile InputRecordingData;
@ -55,5 +55,5 @@ private:
extern InputRecording g_InputRecording;
static InputRecordingFile& g_InputRecordingData = g_InputRecording.GetInputRecordingData();
static InputRecordingHeader& g_InputRecordingHeader = g_InputRecording.GetInputRecordingData().GetHeader();
static InputRecordingFileHeader& g_InputRecordingHeader = g_InputRecording.GetInputRecordingData().GetHeader();
#endif

View File

@ -24,119 +24,154 @@
#include "InputRecordingFile.h"
#ifndef DISABLE_RECORDING
long InputRecordingFile::GetBlockSeekPoint(const long& frame)
void InputRecordingFileHeader::Init()
{
if (savestate.fromSavestate)
{
return RecordingHeaderSize + RecordingSavestateHeaderSize + frame * RecordingBlockSize;
}
else
{
return RecordingHeaderSize + sizeof(bool) + (frame)*RecordingBlockSize;
}
memset(author, 0, ArraySize(author));
memset(gameName, 0, ArraySize(gameName));
}
// Inits the new (or existing) input recording file
bool InputRecordingFile::Open(const wxString path, bool fNewOpen, bool fromSaveState)
void InputRecordingFileHeader::SetEmulatorVersion()
{
Close();
wxString mode = L"rb+";
if (fNewOpen)
{
mode = L"wb+";
MaxFrame = 0;
UndoCount = 0;
header.Init();
}
recordingFile = wxFopen(path, mode);
if (recordingFile == NULL)
{
recordingConLog(wxString::Format("[REC]: Movie file opening failed. Error - %s\n", strerror(errno)));
return false;
}
filename = path;
if (fNewOpen)
{
if (fromSaveState)
{
savestate.fromSavestate = true;
FILE* ssFileCheck = wxFopen(path + "_SaveState.p2s", "r");
if (ssFileCheck != NULL)
{
wxCopyFile(path + "_SaveState.p2s", path + "_SaveState.p2s.bak", false);
fclose(ssFileCheck);
}
StateCopy_SaveToFile(path + "_SaveState.p2s");
}
else
{
sApp.SysExecute();
}
}
return true;
wxString emuVersion = wxString::Format("%s-%d.%d.%d", pxGetAppName().c_str(), PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo);
int max = ArraySize(emu) - 1;
strncpy(emu, emuVersion.c_str(), max);
emu[max] = 0;
}
void InputRecordingFileHeader::SetAuthor(wxString _author)
{
int max = ArraySize(author) - 1;
strncpy(author, _author.c_str(), max);
author[max] = 0;
}
void InputRecordingFileHeader::SetGameName(wxString _gameName)
{
int max = ArraySize(gameName) - 1;
strncpy(gameName, _gameName.c_str(), max);
gameName[max] = 0;
}
// Gracefully close the current recording file
bool InputRecordingFile::Close()
{
if (recordingFile == NULL)
{
return false;
}
WriteHeader();
WriteSaveState();
fclose(recordingFile);
recordingFile = NULL;
filename = "";
return true;
}
// Write savestate flag to file
bool InputRecordingFile::WriteSaveState()
const wxString &InputRecordingFile::GetFilename()
{
if (recordingFile == NULL)
{
return false;
}
fseek(recordingFile, RecordingSeekpointSaveState, SEEK_SET);
if (fwrite(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1)
{
return false;
}
return true;
return filename;
}
// Write controller input buffer to file (per frame)
bool InputRecordingFile::WriteKeyBuf(const uint& frame, const uint port, const uint bufIndex, const u8& buf)
InputRecordingFileHeader &InputRecordingFile::GetHeader()
{
if (recordingFile == NULL)
{
return false;
}
long seek = GetBlockSeekPoint(frame) + RecordingBlockHeaderSize + 18 * port + bufIndex;
if (fseek(recordingFile, seek, SEEK_SET) != 0 || fwrite(&buf, 1, 1, recordingFile) != 1)
{
return false;
}
fflush(recordingFile);
return true;
return header;
}
// Read controller input buffer from file (per frame)
bool InputRecordingFile::ReadKeyBuf(u8& result, const uint& frame, const uint port, const uint bufIndex)
unsigned long &InputRecordingFile::GetTotalFrames()
{
return totalFrames;
}
unsigned long &InputRecordingFile::GetUndoCount()
{
return undoCount;
}
bool InputRecordingFile::FromSaveState()
{
return savestate.fromSavestate;
}
void InputRecordingFile::IncrementUndoCount()
{
undoCount++;
if (recordingFile == NULL)
{
return;
}
fseek(recordingFile, seekpointUndoCount, SEEK_SET);
fwrite(&undoCount, 4, 1, recordingFile);
}
bool InputRecordingFile::open(const wxString path, bool newRecording)
{
if (newRecording)
{
if ((recordingFile = wxFopen(path, L"wb+")) != nullptr)
{
filename = path;
totalFrames = 0;
undoCount = 0;
header.Init();
return true;
}
}
else if ((recordingFile = wxFopen(path, L"rb+")) != nullptr)
{
if (verifyRecordingFileHeader())
{
filename = path;
return true;
}
Close();
recordingConLog(wxString::Format("[REC]: Input recording file header is invalid\n"));
return false;
}
recordingConLog(wxString::Format("[REC]: Input recording file opening failed. Error - %s\n", strerror(errno)));
return false;
}
bool InputRecordingFile::OpenNew(const wxString path, bool fromSavestate)
{
if (fromSavestate)
{
if (CoreThread.IsOpen())
{
if (open(path, true))
{
savestate.fromSavestate = true;
if (wxFileExists(path + "_SaveState.p2s"))
{
wxCopyFile(path + "_SaveState.p2s", path + "_SaveState.p2s.bak", true);
}
StateCopy_SaveToFile(path + "_SaveState.p2s");
return true;
}
}
else
recordingConLog(L"[REC]: Game is not open, aborting playing input recording which starts on a save-state.\n");
return false;
}
else if (open(path, true))
{
savestate.fromSavestate = false;
sApp.SysExecute();
return true;
}
return open(path, true);
}
bool InputRecordingFile::OpenExisting(const wxString path)
{
return open(path, false);
}
bool InputRecordingFile::ReadKeyBuffer(u8 &result, const uint &frame, const uint port, const uint bufIndex)
{
if (recordingFile == NULL)
{
return false;
}
long seek = GetBlockSeekPoint(frame) + RecordingBlockHeaderSize + 18 * port + bufIndex;
long seek = getRecordingBlockSeekPoint(frame) + controllerInputBytes * port + bufIndex;
if (fseek(recordingFile, seek, SEEK_SET) != 0)
{
return false;
@ -149,152 +184,74 @@ bool InputRecordingFile::ReadKeyBuf(u8& result, const uint& frame, const uint po
return true;
}
void InputRecordingFile::GetPadData(PadData& result, unsigned long frame)
void InputRecordingFile::SetTotalFrames(unsigned long frame)
{
result.fExistKey = false;
if (recordingFile == NULL)
if (recordingFile == NULL || totalFrames >= frame)
{
return;
}
long seek = GetBlockSeekPoint(frame) + RecordingBlockHeaderSize;
if (fseek(recordingFile, seek, SEEK_SET) != 0 || fread(result.buf, 1, RecordingBlockDataSize, recordingFile) == 0)
{
return;
}
result.fExistKey = true;
totalFrames = frame;
fseek(recordingFile, seekpointTotalFrames, SEEK_SET);
fwrite(&totalFrames, 4, 1, recordingFile);
}
bool InputRecordingFile::DeletePadData(unsigned long frame)
{
if (recordingFile == NULL)
{
return false;
}
for (unsigned long i = frame; i < MaxFrame - 1; i++)
{
long seek1 = GetBlockSeekPoint(i + 1) + RecordingBlockHeaderSize;
long seek2 = GetBlockSeekPoint(i) + RecordingBlockHeaderSize;
u8 buf[2][18];
fseek(recordingFile, seek1, SEEK_SET);
int rSize = fread(buf, 1, RecordingBlockDataSize, recordingFile);
if (rSize != RecordingBlockDataSize)
{
recordingConLog(wxString::Format("[REC]: Error encountered when reading from file: Expected %d bytes, read %d instead.\n", RecordingBlockDataSize, rSize));
return false;
}
fseek(recordingFile, seek2, SEEK_SET);
rSize = fwrite(buf, 1, RecordingBlockDataSize, recordingFile);
if (rSize != RecordingBlockDataSize)
{
recordingConLog(wxString::Format("[REC]: Error encountered when writing to file: Expected %d bytes, read %d instead.\n", RecordingBlockDataSize, rSize));
return false;
}
}
MaxFrame--;
WriteMaxFrame();
fflush(recordingFile);
return true;
}
bool InputRecordingFile::InsertPadData(unsigned long frame, const PadData& key)
{
if (recordingFile == NULL || !key.fExistKey)
{
return false;
}
for (unsigned long i = MaxFrame - 1; i >= frame; i--)
{
long seek1 = GetBlockSeekPoint(i) + RecordingBlockHeaderSize;
long seek2 = GetBlockSeekPoint(i + 1) + RecordingBlockHeaderSize;
u8 buf[2][18];
fseek(recordingFile, seek1, SEEK_SET);
int rSize = fread(buf, 1, RecordingBlockDataSize, recordingFile);
if (rSize != RecordingBlockDataSize)
{
recordingConLog(wxString::Format("[REC]: Error encountered when reading from file: Expected %d bytes, read %d instead.\n", RecordingBlockDataSize, rSize));
return false;
}
fseek(recordingFile, seek2, SEEK_SET);
rSize = fwrite(buf, 1, RecordingBlockDataSize, recordingFile);
if (rSize != RecordingBlockDataSize)
{
recordingConLog(wxString::Format("[REC]: Error encountered when writing to file: Expected %d bytes, wrote %d instead.\n", RecordingBlockDataSize, rSize));
return false;
}
}
long seek = GetBlockSeekPoint(frame) + RecordingBlockHeaderSize;
fseek(recordingFile, seek, SEEK_SET);
int rSize = fwrite(key.buf, 1, RecordingBlockDataSize, recordingFile);
if (rSize != RecordingBlockDataSize)
{
recordingConLog(wxString::Format("[REC]: Error encountered when writing to file: Expected %d bytes, wrote %d instead.\n", RecordingBlockDataSize, rSize));
return false;
}
MaxFrame++;
WriteMaxFrame();
fflush(recordingFile);
return true;
}
bool InputRecordingFile::UpdatePadData(unsigned long frame, const PadData& key)
{
if (recordingFile == NULL)
{
return false;
}
if (!key.fExistKey)
{
return false;
}
long seek = GetBlockSeekPoint(frame) + RecordingBlockHeaderSize;
fseek(recordingFile, seek, SEEK_SET);
if (fwrite(key.buf, 1, RecordingBlockDataSize, recordingFile) == 0)
{
return false;
}
fflush(recordingFile);
return true;
}
// Verify header of recording file
bool InputRecordingFile::ReadHeaderAndCheck()
bool InputRecordingFile::WriteHeader()
{
if (recordingFile == NULL)
{
return false;
}
rewind(recordingFile);
if (fread(&header, sizeof(InputRecordingHeader), 1, recordingFile) != 1 || fread(&MaxFrame, 4, 1, recordingFile) != 1 || fread(&UndoCount, 4, 1, recordingFile) != 1 || fread(&savestate.fromSavestate, sizeof(bool), 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;
}
if (savestate.fromSavestate)
return true;
}
bool InputRecordingFile::WriteKeyBuffer(const uint &frame, const uint port, const uint bufIndex, const u8 &buf)
{
if (recordingFile == NULL)
{
FILE* ssFileCheck = wxFopen(filename + "_SaveState.p2s", "r");
if (ssFileCheck == NULL)
{
recordingConLog(wxString::Format("[REC]: Could not locate savestate file at location - %s\n", filename + "_SaveState.p2s"));
return false;
}
fclose(ssFileCheck);
StateCopy_LoadFromFile(filename + "_SaveState.p2s");
}
else
{
sApp.SysExecute();
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 == NULL)
{
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)
{
@ -303,101 +260,19 @@ bool InputRecordingFile::ReadHeaderAndCheck()
}
return true;
}
bool InputRecordingFile::WriteHeader()
{
bool InputRecordingFile::writeSaveState() {
if (recordingFile == NULL)
{
return false;
}
rewind(recordingFile);
if (fwrite(&header, sizeof(InputRecordingHeader), 1, recordingFile) != 1)
fseek(recordingFile, seekpointSaveStateHeader, SEEK_SET);
if (fwrite(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1)
{
return false;
}
return true;
}
bool InputRecordingFile::WriteMaxFrame()
{
if (recordingFile == NULL)
{
return false;
}
fseek(recordingFile, RecordingSeekpointFrameMax, SEEK_SET);
if (fwrite(&MaxFrame, 4, 1, recordingFile) != 1)
{
return false;
}
return true;
}
void InputRecordingFile::UpdateFrameMax(unsigned long frame)
{
if (recordingFile == NULL || MaxFrame >= frame)
{
return;
}
MaxFrame = frame;
fseek(recordingFile, RecordingSeekpointFrameMax, SEEK_SET);
fwrite(&MaxFrame, 4, 1, recordingFile);
}
void InputRecordingFile::AddUndoCount()
{
UndoCount++;
if (recordingFile == NULL)
{
return;
}
fseek(recordingFile, RecordingSeekpointUndoCount, SEEK_SET);
fwrite(&UndoCount, 4, 1, recordingFile);
}
void InputRecordingHeader::SetEmulatorVersion()
{
wxString emuVersion = wxString::Format("%s-%d.%d.%d", pxGetAppName().c_str(), PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo);
int max = ArraySize(emu) - 1;
strncpy(emu, emuVersion.c_str(), max);
emu[max] = 0;
}
void InputRecordingHeader::SetAuthor(wxString _author)
{
int max = ArraySize(author) - 1;
strncpy(author, _author.c_str(), max);
author[max] = 0;
}
void InputRecordingHeader::SetGameName(wxString _gameName)
{
int max = ArraySize(gameName) - 1;
strncpy(gameName, _gameName.c_str(), max);
gameName[max] = 0;
}
void InputRecordingHeader::Init()
{
memset(author, 0, ArraySize(author));
memset(gameName, 0, ArraySize(gameName));
}
InputRecordingHeader& InputRecordingFile::GetHeader()
{
return header;
}
unsigned long& InputRecordingFile::GetMaxFrame()
{
return MaxFrame;
}
unsigned long& InputRecordingFile::GetUndoCount()
{
return UndoCount;
}
const wxString& InputRecordingFile::GetFilename()
{
return filename;
}
#endif

View File

@ -15,12 +15,15 @@
#pragma once
#include "PadData.h"
#include "System.h"
#include "PadData.h"
// NOTE / TODOs for Version 2
// - Move fromSavestate, undoCount, and total frames into the header
#ifndef DISABLE_RECORDING
struct InputRecordingHeader
struct InputRecordingFileHeader
{
u8 version = 1;
char emu[50] = "";
@ -29,70 +32,80 @@ struct InputRecordingHeader
public:
void SetEmulatorVersion();
void Init();
void SetAuthor(wxString author);
void SetGameName(wxString cdrom);
void Init();
};
static const int RecordingHeaderSize = sizeof(InputRecordingHeader) + 4 + 4;
// Contains info about the starting point of the movie
// 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() {}
~InputRecordingFile() { Close(); }
// Movie File Manipulation
bool Open(const wxString fn, bool fNewOpen, bool fromSaveState);
// Closes the underlying input recording file, writing the header and
// prepares for a possible new recording to be started
bool Close();
bool WriteKeyBuf(const uint& frame, const uint port, const uint bufIndex, const u8& buf);
bool ReadKeyBuf(u8& result, const uint& frame, const uint port, const uint bufIndex);
// Controller Data
void GetPadData(PadData& result_pad, unsigned long frame);
bool DeletePadData(unsigned long frame);
bool InsertPadData(unsigned long frame, const PadData& key);
bool UpdatePadData(unsigned long frame, const PadData& key);
// Header
InputRecordingHeader& GetHeader();
unsigned long& GetMaxFrame();
unsigned long& GetUndoCount();
const wxString& GetFilename();
// Retrieve the input recording's filename (not the path)
const wxString &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
unsigned 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 wxString path);
// Create and open a brand new input recording, either starting from a save-state or from
// booting the game
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);
// Updates the total frame counter and commit it to the recording file
void SetTotalFrames(unsigned long frames);
// Persist the input recording file header's current state to the file
bool WriteHeader();
bool WriteMaxFrame();
bool WriteSaveState();
bool ReadHeaderAndCheck();
void UpdateFrameMax(unsigned long frame);
void AddUndoCount();
// 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 RecordingSavestateHeaderSize = sizeof(bool);
static const int RecordingBlockHeaderSize = 0;
static const int RecordingBlockDataSize = 18 * 2;
static const int RecordingBlockSize = RecordingBlockHeaderSize + RecordingBlockDataSize;
static const int RecordingSeekpointFrameMax = sizeof(InputRecordingHeader);
static const int RecordingSeekpointUndoCount = sizeof(InputRecordingHeader) + 4;
static const int RecordingSeekpointSaveState = RecordingSeekpointUndoCount + 4;
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;
// Movie File
FILE* recordingFile = NULL;
InputRecordingFileHeader header;
wxString filename = "";
long GetBlockSeekPoint(const long& frame);
// Header
InputRecordingHeader header;
FILE * recordingFile = NULL;
InputRecordingSavestate savestate;
unsigned long MaxFrame = 0;
unsigned long UndoCount = 0;
unsigned 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 wxString path, bool newRecording);
bool verifyRecordingFileHeader();
bool writeSaveState();
};
#endif

View File

@ -76,10 +76,6 @@ void PadData::SetNormalButtons(int port, std::vector<int> buttons)
void PadData::SetNormalButton(int port, PadData_NormalButton button, int fpushed)
{
if (port < 0 || 1 < port)
{
return;
}
wxByte keybit[2];
GetKeyBit(keybit, button);
int pressureByteIndex = GetPressureByte(button);
@ -111,10 +107,6 @@ void PadData::SetNormalButton(int port, PadData_NormalButton button, int fpushed
int PadData::GetNormalButton(int port, PadData_NormalButton button) const
{
if (port < 0 || 1 < port)
{
return false;
}
wxByte keybit[2];
GetKeyBit(keybit, button);
int pressureByteIndex = GetPressureByte(button);
@ -282,10 +274,6 @@ void PadData::SetAnalogVectors(int port, std::vector<int> vectors)
void PadData::SetAnalogVector(int port, PadData_AnalogVector vector, int val)
{
if (port < 0 || 1 < port)
{
return;
}
if (val < 0)
{
val = 0;
@ -300,10 +288,6 @@ void PadData::SetAnalogVector(int port, PadData_AnalogVector vector, int val)
int PadData::GetAnalogVector(int port, PadData_AnalogVector vector) const
{
if (port < 0 || 1 < port)
{
return 0;
}
return buf[port][GetAnalogVectorByte(vector)];
}

View File

@ -110,13 +110,11 @@ void RecordingControls::TogglePause()
void RecordingControls::Pause()
{
fStop = true;
fFrameAdvance = true;
}
void RecordingControls::Unpause()
{
fStop = false;
fStart = true;
fFrameAdvance = true;
}
#endif

View File

@ -32,10 +32,6 @@ RecordingInputManager::RecordingInputManager()
void RecordingInputManager::ControllerInterrupt(u8 & data, u8 & port, u16 & BufCount, u8 buf[])
{
if (port >= 2)
{
return;
}
if (virtualPad[port])
{

View File

@ -218,13 +218,17 @@ SIO_WRITE sioWriteController(u8 data)
#ifndef DISABLE_RECORDING
if (g_Conf->EmuOptions.EnableRecordingTools)
{
g_InputRecording.ControllerInterrupt(data, sio.port, sio.bufCount, sio.buf);
if (g_InputRecording.IsInterruptFrame())
// Only examine controllers 1 / 2
if (sio.slot[sio.port] == 0)
{
g_RecordingInput.ControllerInterrupt(data, sio.port, sio.bufCount, sio.buf);
}
g_InputRecording.ControllerInterrupt(data, sio.port, sio.bufCount, sio.buf);
if (g_InputRecording.IsInterruptFrame())
{
g_RecordingInput.ControllerInterrupt(data, sio.port, sio.bufCount, sio.buf);
}
PadData::LogPadData(sio.port, sio.bufCount, sio.buf);
PadData::LogPadData(sio.port, sio.bufCount, sio.buf);
}
}
#endif
break;

View File

@ -79,8 +79,8 @@ void Pcsx2App::OpenMainFrame()
#ifndef DISABLE_RECORDING
VirtualPad* virtualPad0 = new VirtualPad(mainFrame, wxID_ANY, wxEmptyString, 0);
m_id_VirtualPad[0] = virtualPad0->GetId();
VirtualPad *virtualPad1 = new VirtualPad(mainFrame, wxID_ANY, wxEmptyString, 1);
VirtualPad* virtualPad1 = new VirtualPad(mainFrame, wxID_ANY, wxEmptyString, 1);
m_id_VirtualPad[1] = virtualPad1->GetId();
NewRecordingFrame* newRecordingFrame = new NewRecordingFrame(mainFrame);

View File

@ -1059,7 +1059,7 @@ void Pcsx2App::OnProgramLogClosed( wxWindowID id )
void Pcsx2App::OnMainFrameClosed( wxWindowID id )
{
#ifndef DISABLE_RECORDING
if (g_Conf->EmuOptions.EnableRecordingTools)
if (g_InputRecording.GetModeState() == INPUT_RECORDING_MODE_NONE)
{
g_InputRecording.Stop();
}

View File

@ -749,7 +749,7 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
}
title.Replace(L"${frame}", pxsFmt(L"%d", g_FrameCount));
title.Replace(L"${maxFrame}", pxsFmt(L"%d", g_InputRecording.GetInputRecordingData().GetMaxFrame()));
title.Replace(L"${maxFrame}", pxsFmt(L"%d", g_InputRecording.GetInputRecordingData().GetTotalFrames()));
title.Replace(L"${mode}", movieMode);
#else
wxString title = templates.TitleTemplate;

View File

@ -34,6 +34,7 @@
# include "Recording/InputRecording.h"
# include "Recording/RecordingControls.h"
# include "Recording/VirtualPad.h"
# include "Recording/RecordingControls.h"
#endif
@ -499,7 +500,7 @@ void MainEmuFrame::Menu_EnableWideScreenPatches_Click( wxCommandEvent& )
}
#ifndef DISABLE_RECORDING
void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent&)
void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent& event)
{
bool checked = GetMenuBar()->IsChecked(MenuId_EnableInputRecording);
// Confirm with User
@ -529,6 +530,9 @@ void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent&)
}
else
{
//Properly close any currently loaded recording file before disabling
if (g_InputRecording.GetModeState() != INPUT_RECORDING_MODE_NONE)
Menu_Recording_Stop_Click(event);
GetMenuBar()->Remove(TopLevelMenu_InputRecording);
// Always turn controller logs off, but never turn it on by default
SysConsole.controlInfo.Enabled = checked;
@ -540,6 +544,8 @@ void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent&)
viewport->InitDefaultAccelerators();
}
}
if (g_RecordingControls.IsEmulationAndRecordingPaused())
g_RecordingControls.Unpause();
}
g_Conf->EmuOptions.EnableRecordingTools = checked;
@ -868,49 +874,61 @@ void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_Click(wxCommandEvent & eve
#ifndef DISABLE_RECORDING
void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event)
{
g_InputRecording.Stop();
const bool initiallyPaused = g_RecordingControls.IsEmulationAndRecordingPaused();
if (!initiallyPaused)
g_RecordingControls.Pause();
NewRecordingFrame* NewRecordingFrame = wxGetApp().GetNewRecordingFramePtr();
if (NewRecordingFrame)
{
if (NewRecordingFrame->ShowModal() == wxID_CANCEL)
{
if (!initiallyPaused)
g_RecordingControls.Unpause();
return;
}
// From Current Frame
if (NewRecordingFrame->GetFrom() == 0)
if (!g_InputRecording.Create(NewRecordingFrame->GetFile(), !NewRecordingFrame->GetFrom(), NewRecordingFrame->GetAuthor()))
{
if (!CoreThread.IsOpen())
{
recordingConLog(L"[REC]: Game is not open, aborting new input recording.\n");
return;
}
g_InputRecording.Create(NewRecordingFrame->GetFile(), true, NewRecordingFrame->GetAuthor());
}
// From Power-On
else if (NewRecordingFrame->GetFrom() == 1)
{
g_InputRecording.Create(NewRecordingFrame->GetFile(), false, NewRecordingFrame->GetAuthor());
if (!initiallyPaused)
g_RecordingControls.Unpause();
return;
}
}
m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false);
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
if (!g_InputRecordingData.FromSaveState())
g_RecordingControls.Unpause();
}
void MainEmuFrame::Menu_Recording_Play_Click(wxCommandEvent &event)
{
g_InputRecording.Stop();
const bool initiallyPaused = g_RecordingControls.IsEmulationAndRecordingPaused();
if (!initiallyPaused)
g_RecordingControls.Pause();
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();
return;
}
wxString path = openFileDialog.GetPath();
g_InputRecording.Play(path, true);
m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false);
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
const bool recordingLoaded = g_InputRecording.GetModeState() != INPUT_RECORDING_MODE_NONE;
if (!g_InputRecording.Play(path))
{
if (recordingLoaded)
Menu_Recording_Stop_Click(event);
if (!initiallyPaused)
g_RecordingControls.Unpause();
return;
}
if (!recordingLoaded)
{
m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false);
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
}
g_RecordingControls.Unpause();
}
void MainEmuFrame::Menu_Recording_Stop_Click(wxCommandEvent &event)
@ -922,18 +940,6 @@ void MainEmuFrame::Menu_Recording_Stop_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_Recording_VirtualPad_Open_Click(wxCommandEvent &event)
{
VirtualPad *vp = NULL;
if (event.GetId() == MenuId_Recording_VirtualPad_Port0)
{
vp = wxGetApp().GetVirtualPadPtr(0);
}
else if (event.GetId() == MenuId_Recording_VirtualPad_Port1)
{
vp = wxGetApp().GetVirtualPadPtr(1);
}
if (vp != NULL)
{
vp->Show();
}
wxGetApp().GetVirtualPadPtr(event.GetId() - MenuId_Recording_VirtualPad_Port0)->Show();
}
#endif