mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
4d0650baa0
commit
4d66818746
|
@ -37,7 +37,7 @@ void SaveStateBase::InputRecordingFreeze()
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
if (g_FrameCount > 0 && IsLoading())
|
if (g_FrameCount > 0 && IsLoading())
|
||||||
{
|
{
|
||||||
g_InputRecordingData.AddUndoCount();
|
g_InputRecordingData.IncrementUndoCount();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -50,11 +50,6 @@ InputRecording g_InputRecording;
|
||||||
void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 buf[])
|
void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 buf[])
|
||||||
{
|
{
|
||||||
// TODO - Multi-Tap Support
|
// 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
|
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];
|
const u8& nowBuf = buf[bufCount];
|
||||||
if (state == INPUT_RECORDING_MODE_RECORD)
|
if (state == INPUT_RECORDING_MODE_RECORD)
|
||||||
{
|
{
|
||||||
InputRecordingData.UpdateFrameMax(g_FrameCount);
|
InputRecordingData.SetTotalFrames(g_FrameCount);
|
||||||
InputRecordingData.WriteKeyBuf(g_FrameCount, port, bufCount - 3, nowBuf);
|
InputRecordingData.WriteKeyBuffer(g_FrameCount, port, bufCount - 3, nowBuf);
|
||||||
}
|
}
|
||||||
else if (state == INPUT_RECORDING_MODE_REPLAY)
|
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
|
// Pause the emulation but the movie is not closed
|
||||||
g_RecordingControls.Pause();
|
g_RecordingControls.Pause();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
u8 tmp = 0;
|
u8 tmp = 0;
|
||||||
if (InputRecordingData.ReadKeyBuf(tmp, g_FrameCount, port, bufCount - 3))
|
if (InputRecordingData.ReadKeyBuffer(tmp, g_FrameCount, port, bufCount - 3))
|
||||||
{
|
{
|
||||||
buf[bufCount] = tmp;
|
buf[bufCount] = tmp;
|
||||||
}
|
}
|
||||||
|
@ -130,15 +125,11 @@ void InputRecording::Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GUI Handler - Start recording
|
// 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();
|
if (!InputRecordingData.OpenNew(FileName, fromSaveState))
|
||||||
Stop();
|
|
||||||
|
|
||||||
// create
|
|
||||||
if (!InputRecordingData.Open(FileName, true, fromSaveState))
|
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// Set emulator version
|
// Set emulator version
|
||||||
InputRecordingData.GetHeader().SetEmulatorVersion();
|
InputRecordingData.GetHeader().SetEmulatorVersion();
|
||||||
|
@ -157,40 +148,62 @@ void InputRecording::Create(wxString FileName, bool fromSaveState, wxString auth
|
||||||
|
|
||||||
// In every case, we reset the g_FrameCount
|
// In every case, we reset the g_FrameCount
|
||||||
g_FrameCount = 0;
|
g_FrameCount = 0;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GUI Handler - Play a recording
|
// GUI Handler - Play a recording
|
||||||
void InputRecording::Play(wxString FileName, bool fromSaveState)
|
bool InputRecording::Play(wxString fileName)
|
||||||
{
|
{
|
||||||
g_RecordingControls.Pause();
|
if (state != INPUT_RECORDING_MODE_NONE)
|
||||||
Stop();
|
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");
|
if (!CoreThread.IsOpen())
|
||||||
|
{
|
||||||
|
recordingConLog(L"[REC]: Game is not open, aborting playing input recording which starts on a save-state.\n");
|
||||||
InputRecordingData.Close();
|
InputRecordingData.Close();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// Check author name
|
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");
|
||||||
|
}
|
||||||
|
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 (!g_Conf->CurrentIso.IsEmpty())
|
||||||
{
|
{
|
||||||
if (resolveGameName() != InputRecordingData.GetHeader().gameName)
|
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;
|
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]: 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]: 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]: 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]: Author: %s\n", InputRecordingData.GetHeader().author));
|
||||||
recordingConLog(wxString::Format(L"[REC]: MaxFrame: %d\n", InputRecordingData.GetMaxFrame()));
|
recordingConLog(wxString::Format(L"[REC]: Total Frames: %d\n", InputRecordingData.GetTotalFrames()));
|
||||||
recordingConLog(wxString::Format(L"[REC]: UndoCount: %d\n", InputRecordingData.GetUndoCount()));
|
recordingConLog(wxString::Format(L"[REC]: Undo Count: %d\n", InputRecordingData.GetUndoCount()));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString InputRecording::resolveGameName()
|
wxString InputRecording::resolveGameName()
|
||||||
|
|
|
@ -41,8 +41,8 @@ public:
|
||||||
bool IsInterruptFrame();
|
bool IsInterruptFrame();
|
||||||
|
|
||||||
void Stop();
|
void Stop();
|
||||||
void Create(wxString filename, bool fromSaveState, wxString authorName);
|
bool Create(wxString filename, bool fromSaveState, wxString authorName);
|
||||||
void Play(wxString filename, bool fromSaveState);
|
bool Play(wxString filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InputRecordingFile InputRecordingData;
|
InputRecordingFile InputRecordingData;
|
||||||
|
@ -55,5 +55,5 @@ private:
|
||||||
|
|
||||||
extern InputRecording g_InputRecording;
|
extern InputRecording g_InputRecording;
|
||||||
static InputRecordingFile& g_InputRecordingData = g_InputRecording.GetInputRecordingData();
|
static InputRecordingFile& g_InputRecordingData = g_InputRecording.GetInputRecordingData();
|
||||||
static InputRecordingHeader& g_InputRecordingHeader = g_InputRecording.GetInputRecordingData().GetHeader();
|
static InputRecordingFileHeader& g_InputRecordingHeader = g_InputRecording.GetInputRecordingData().GetHeader();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,119 +24,154 @@
|
||||||
#include "InputRecordingFile.h"
|
#include "InputRecordingFile.h"
|
||||||
|
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
long InputRecordingFile::GetBlockSeekPoint(const long& frame)
|
|
||||||
|
void InputRecordingFileHeader::Init()
|
||||||
{
|
{
|
||||||
if (savestate.fromSavestate)
|
memset(author, 0, ArraySize(author));
|
||||||
{
|
memset(gameName, 0, ArraySize(gameName));
|
||||||
return RecordingHeaderSize + RecordingSavestateHeaderSize + frame * RecordingBlockSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return RecordingHeaderSize + sizeof(bool) + (frame)*RecordingBlockSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inits the new (or existing) input recording file
|
void InputRecordingFileHeader::SetEmulatorVersion()
|
||||||
bool InputRecordingFile::Open(const wxString path, bool fNewOpen, bool fromSaveState)
|
|
||||||
{
|
{
|
||||||
Close();
|
wxString emuVersion = wxString::Format("%s-%d.%d.%d", pxGetAppName().c_str(), PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo);
|
||||||
wxString mode = L"rb+";
|
int max = ArraySize(emu) - 1;
|
||||||
if (fNewOpen)
|
strncpy(emu, emuVersion.c_str(), max);
|
||||||
{
|
emu[max] = 0;
|
||||||
mode = L"wb+";
|
}
|
||||||
MaxFrame = 0;
|
|
||||||
UndoCount = 0;
|
void InputRecordingFileHeader::SetAuthor(wxString _author)
|
||||||
header.Init();
|
{
|
||||||
}
|
int max = ArraySize(author) - 1;
|
||||||
recordingFile = wxFopen(path, mode);
|
strncpy(author, _author.c_str(), max);
|
||||||
if (recordingFile == NULL)
|
author[max] = 0;
|
||||||
{
|
}
|
||||||
recordingConLog(wxString::Format("[REC]: Movie file opening failed. Error - %s\n", strerror(errno)));
|
|
||||||
return false;
|
void InputRecordingFileHeader::SetGameName(wxString _gameName)
|
||||||
}
|
{
|
||||||
filename = path;
|
int max = ArraySize(gameName) - 1;
|
||||||
|
strncpy(gameName, _gameName.c_str(), max);
|
||||||
if (fNewOpen)
|
gameName[max] = 0;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gracefully close the current recording file
|
|
||||||
bool InputRecordingFile::Close()
|
bool InputRecordingFile::Close()
|
||||||
{
|
{
|
||||||
if (recordingFile == NULL)
|
if (recordingFile == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
WriteHeader();
|
|
||||||
WriteSaveState();
|
|
||||||
fclose(recordingFile);
|
fclose(recordingFile);
|
||||||
recordingFile = NULL;
|
recordingFile = NULL;
|
||||||
filename = "";
|
filename = "";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write savestate flag to file
|
const wxString &InputRecordingFile::GetFilename()
|
||||||
bool InputRecordingFile::WriteSaveState()
|
|
||||||
{
|
{
|
||||||
if (recordingFile == NULL)
|
return filename;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek(recordingFile, RecordingSeekpointSaveState, SEEK_SET);
|
|
||||||
if (fwrite(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write controller input buffer to file (per frame)
|
InputRecordingFileHeader &InputRecordingFile::GetHeader()
|
||||||
bool InputRecordingFile::WriteKeyBuf(const uint& frame, const uint port, const uint bufIndex, const u8& buf)
|
|
||||||
{
|
{
|
||||||
if (recordingFile == NULL)
|
return header;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read controller input buffer from file (per frame)
|
unsigned long &InputRecordingFile::GetTotalFrames()
|
||||||
bool InputRecordingFile::ReadKeyBuf(u8& result, const uint& frame, const uint port, const uint bufIndex)
|
{
|
||||||
|
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)
|
if (recordingFile == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
long seek = GetBlockSeekPoint(frame) + RecordingBlockHeaderSize + 18 * port + bufIndex;
|
long seek = getRecordingBlockSeekPoint(frame) + controllerInputBytes * port + bufIndex;
|
||||||
if (fseek(recordingFile, seek, SEEK_SET) != 0)
|
if (fseek(recordingFile, seek, SEEK_SET) != 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -149,150 +184,72 @@ bool InputRecordingFile::ReadKeyBuf(u8& result, const uint& frame, const uint po
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputRecordingFile::SetTotalFrames(unsigned long frame)
|
||||||
void InputRecordingFile::GetPadData(PadData& result, unsigned long frame)
|
|
||||||
{
|
{
|
||||||
result.fExistKey = false;
|
if (recordingFile == NULL || totalFrames >= frame)
|
||||||
if (recordingFile == NULL)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
totalFrames = frame;
|
||||||
long seek = GetBlockSeekPoint(frame) + RecordingBlockHeaderSize;
|
fseek(recordingFile, seekpointTotalFrames, SEEK_SET);
|
||||||
if (fseek(recordingFile, seek, SEEK_SET) != 0 || fread(result.buf, 1, RecordingBlockDataSize, recordingFile) == 0)
|
fwrite(&totalFrames, 4, 1, recordingFile);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.fExistKey = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputRecordingFile::DeletePadData(unsigned long frame)
|
bool InputRecordingFile::WriteHeader()
|
||||||
{
|
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
if (recordingFile == NULL)
|
if (recordingFile == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
rewind(recordingFile);
|
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;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
fclose(ssFileCheck);
|
|
||||||
StateCopy_LoadFromFile(filename + "_SaveState.p2s");
|
long seek = getRecordingBlockSeekPoint(frame) + 18 * port + bufIndex;
|
||||||
}
|
|
||||||
else
|
if (fseek(recordingFile, seek, SEEK_SET) != 0
|
||||||
|
|| fwrite(&buf, 1, 1, recordingFile) != 1)
|
||||||
{
|
{
|
||||||
sApp.SysExecute();
|
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
|
// Check for current verison
|
||||||
|
@ -303,101 +260,19 @@ bool InputRecordingFile::ReadHeaderAndCheck()
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool InputRecordingFile::WriteHeader()
|
|
||||||
{
|
bool InputRecordingFile::writeSaveState() {
|
||||||
if (recordingFile == NULL)
|
if (recordingFile == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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
|
#endif
|
||||||
|
|
|
@ -15,12 +15,15 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PadData.h"
|
|
||||||
#include "System.h"
|
#include "System.h"
|
||||||
|
|
||||||
|
#include "PadData.h"
|
||||||
|
|
||||||
|
// NOTE / TODOs for Version 2
|
||||||
|
// - Move fromSavestate, undoCount, and total frames into the header
|
||||||
|
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
struct InputRecordingHeader
|
struct InputRecordingFileHeader
|
||||||
{
|
{
|
||||||
u8 version = 1;
|
u8 version = 1;
|
||||||
char emu[50] = "";
|
char emu[50] = "";
|
||||||
|
@ -29,70 +32,80 @@ struct InputRecordingHeader
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void SetEmulatorVersion();
|
void SetEmulatorVersion();
|
||||||
|
void Init();
|
||||||
void SetAuthor(wxString author);
|
void SetAuthor(wxString author);
|
||||||
void SetGameName(wxString cdrom);
|
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
|
struct InputRecordingSavestate
|
||||||
{
|
{
|
||||||
// Whether we start from the savestate or from power-on
|
// Whether we start from the savestate or from power-on
|
||||||
bool fromSavestate = false;
|
bool fromSavestate = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handles all operations on the input recording file
|
||||||
class InputRecordingFile
|
class InputRecordingFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InputRecordingFile() {}
|
|
||||||
~InputRecordingFile() { Close(); }
|
~InputRecordingFile() { Close(); }
|
||||||
|
|
||||||
// Movie File Manipulation
|
// Closes the underlying input recording file, writing the header and
|
||||||
bool Open(const wxString fn, bool fNewOpen, bool fromSaveState);
|
// prepares for a possible new recording to be started
|
||||||
bool Close();
|
bool Close();
|
||||||
bool WriteKeyBuf(const uint& frame, const uint port, const uint bufIndex, const u8& buf);
|
// Retrieve the input recording's filename (not the path)
|
||||||
bool ReadKeyBuf(u8& result, const uint& frame, const uint port, const uint bufIndex);
|
const wxString &GetFilename();
|
||||||
|
// Retrieve the input recording's header which contains high-level metadata on the recording
|
||||||
// Controller Data
|
InputRecordingFileHeader &GetHeader();
|
||||||
void GetPadData(PadData& result_pad, unsigned long frame);
|
// The maximum number of frames, or in other words, the length of the recording
|
||||||
bool DeletePadData(unsigned long frame);
|
unsigned long &GetTotalFrames();
|
||||||
bool InsertPadData(unsigned long frame, const PadData& key);
|
// The number of times a save-state has been loaded while recording this movie
|
||||||
bool UpdatePadData(unsigned long frame, const PadData& key);
|
// this is also often referred to as a "re-record"
|
||||||
|
unsigned long &GetUndoCount();
|
||||||
// Header
|
// Whether or not this input recording starts by loading a save-state or by booting the game fresh
|
||||||
InputRecordingHeader& GetHeader();
|
bool FromSaveState();
|
||||||
unsigned long& GetMaxFrame();
|
// Increment the number of undo actions and commit it to the recording file
|
||||||
unsigned long& GetUndoCount();
|
void IncrementUndoCount();
|
||||||
const wxString& GetFilename();
|
// 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 WriteHeader();
|
||||||
bool WriteMaxFrame();
|
// Writes the current frame's input data to the file so it can be replayed
|
||||||
bool WriteSaveState();
|
bool WriteKeyBuffer(const uint &frame, const uint port, const uint bufIndex, const u8 &buf);
|
||||||
|
|
||||||
bool ReadHeaderAndCheck();
|
|
||||||
void UpdateFrameMax(unsigned long frame);
|
|
||||||
void AddUndoCount();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int RecordingSavestateHeaderSize = sizeof(bool);
|
static const int controllerPortsSupported = 2;
|
||||||
static const int RecordingBlockHeaderSize = 0;
|
static const int controllerInputBytes = 18;
|
||||||
static const int RecordingBlockDataSize = 18 * 2;
|
static const int inputBytesPerFrame = controllerInputBytes * controllerPortsSupported;
|
||||||
static const int RecordingBlockSize = RecordingBlockHeaderSize + RecordingBlockDataSize;
|
// TODO - version 2, this could be greatly simplified if everything was in the header
|
||||||
static const int RecordingSeekpointFrameMax = sizeof(InputRecordingHeader);
|
// + 4 + 4 is the totalFrame and undoCount values
|
||||||
static const int RecordingSeekpointUndoCount = sizeof(InputRecordingHeader) + 4;
|
static const int headerSize = sizeof(InputRecordingFileHeader) + 4 + 4;
|
||||||
static const int RecordingSeekpointSaveState = RecordingSeekpointUndoCount + 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
|
InputRecordingFileHeader header;
|
||||||
FILE* recordingFile = NULL;
|
|
||||||
wxString filename = "";
|
wxString filename = "";
|
||||||
long GetBlockSeekPoint(const long& frame);
|
FILE * recordingFile = NULL;
|
||||||
|
|
||||||
// Header
|
|
||||||
InputRecordingHeader header;
|
|
||||||
InputRecordingSavestate savestate;
|
InputRecordingSavestate savestate;
|
||||||
unsigned long MaxFrame = 0;
|
unsigned long totalFrames = 0;
|
||||||
unsigned long UndoCount = 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
|
#endif
|
||||||
|
|
|
@ -76,10 +76,6 @@ void PadData::SetNormalButtons(int port, std::vector<int> buttons)
|
||||||
|
|
||||||
void PadData::SetNormalButton(int port, PadData_NormalButton button, int fpushed)
|
void PadData::SetNormalButton(int port, PadData_NormalButton button, int fpushed)
|
||||||
{
|
{
|
||||||
if (port < 0 || 1 < port)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wxByte keybit[2];
|
wxByte keybit[2];
|
||||||
GetKeyBit(keybit, button);
|
GetKeyBit(keybit, button);
|
||||||
int pressureByteIndex = GetPressureByte(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
|
int PadData::GetNormalButton(int port, PadData_NormalButton button) const
|
||||||
{
|
{
|
||||||
if (port < 0 || 1 < port)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
wxByte keybit[2];
|
wxByte keybit[2];
|
||||||
GetKeyBit(keybit, button);
|
GetKeyBit(keybit, button);
|
||||||
int pressureByteIndex = GetPressureByte(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)
|
void PadData::SetAnalogVector(int port, PadData_AnalogVector vector, int val)
|
||||||
{
|
{
|
||||||
if (port < 0 || 1 < port)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (val < 0)
|
if (val < 0)
|
||||||
{
|
{
|
||||||
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
|
int PadData::GetAnalogVector(int port, PadData_AnalogVector vector) const
|
||||||
{
|
{
|
||||||
if (port < 0 || 1 < port)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf[port][GetAnalogVectorByte(vector)];
|
return buf[port][GetAnalogVectorByte(vector)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,13 +110,11 @@ void RecordingControls::TogglePause()
|
||||||
void RecordingControls::Pause()
|
void RecordingControls::Pause()
|
||||||
{
|
{
|
||||||
fStop = true;
|
fStop = true;
|
||||||
fFrameAdvance = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordingControls::Unpause()
|
void RecordingControls::Unpause()
|
||||||
{
|
{
|
||||||
fStop = false;
|
fStop = false;
|
||||||
fStart = true;
|
fStart = true;
|
||||||
fFrameAdvance = true;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,10 +32,6 @@ RecordingInputManager::RecordingInputManager()
|
||||||
|
|
||||||
void RecordingInputManager::ControllerInterrupt(u8 & data, u8 & port, u16 & BufCount, u8 buf[])
|
void RecordingInputManager::ControllerInterrupt(u8 & data, u8 & port, u16 & BufCount, u8 buf[])
|
||||||
{
|
{
|
||||||
if (port >= 2)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virtualPad[port])
|
if (virtualPad[port])
|
||||||
{
|
{
|
||||||
|
|
|
@ -217,6 +217,9 @@ SIO_WRITE sioWriteController(u8 data)
|
||||||
|
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
if (g_Conf->EmuOptions.EnableRecordingTools)
|
if (g_Conf->EmuOptions.EnableRecordingTools)
|
||||||
|
{
|
||||||
|
// Only examine controllers 1 / 2
|
||||||
|
if (sio.slot[sio.port] == 0)
|
||||||
{
|
{
|
||||||
g_InputRecording.ControllerInterrupt(data, sio.port, sio.bufCount, sio.buf);
|
g_InputRecording.ControllerInterrupt(data, sio.port, sio.bufCount, sio.buf);
|
||||||
if (g_InputRecording.IsInterruptFrame())
|
if (g_InputRecording.IsInterruptFrame())
|
||||||
|
@ -226,6 +229,7 @@ SIO_WRITE sioWriteController(u8 data)
|
||||||
|
|
||||||
PadData::LogPadData(sio.port, sio.bufCount, sio.buf);
|
PadData::LogPadData(sio.port, sio.bufCount, sio.buf);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ void Pcsx2App::OpenMainFrame()
|
||||||
VirtualPad* virtualPad0 = new VirtualPad(mainFrame, wxID_ANY, wxEmptyString, 0);
|
VirtualPad* virtualPad0 = new VirtualPad(mainFrame, wxID_ANY, wxEmptyString, 0);
|
||||||
m_id_VirtualPad[0] = virtualPad0->GetId();
|
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();
|
m_id_VirtualPad[1] = virtualPad1->GetId();
|
||||||
|
|
||||||
NewRecordingFrame* newRecordingFrame = new NewRecordingFrame(mainFrame);
|
NewRecordingFrame* newRecordingFrame = new NewRecordingFrame(mainFrame);
|
||||||
|
|
|
@ -1059,7 +1059,7 @@ void Pcsx2App::OnProgramLogClosed( wxWindowID id )
|
||||||
void Pcsx2App::OnMainFrameClosed( wxWindowID id )
|
void Pcsx2App::OnMainFrameClosed( wxWindowID id )
|
||||||
{
|
{
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
if (g_Conf->EmuOptions.EnableRecordingTools)
|
if (g_InputRecording.GetModeState() == INPUT_RECORDING_MODE_NONE)
|
||||||
{
|
{
|
||||||
g_InputRecording.Stop();
|
g_InputRecording.Stop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -749,7 +749,7 @@ void GSFrame::OnUpdateTitle( wxTimerEvent& evt )
|
||||||
}
|
}
|
||||||
|
|
||||||
title.Replace(L"${frame}", pxsFmt(L"%d", g_FrameCount));
|
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);
|
title.Replace(L"${mode}", movieMode);
|
||||||
#else
|
#else
|
||||||
wxString title = templates.TitleTemplate;
|
wxString title = templates.TitleTemplate;
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
# include "Recording/InputRecording.h"
|
# include "Recording/InputRecording.h"
|
||||||
# include "Recording/RecordingControls.h"
|
# include "Recording/RecordingControls.h"
|
||||||
# include "Recording/VirtualPad.h"
|
# include "Recording/VirtualPad.h"
|
||||||
|
# include "Recording/RecordingControls.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -499,7 +500,7 @@ void MainEmuFrame::Menu_EnableWideScreenPatches_Click( wxCommandEvent& )
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent&)
|
void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
bool checked = GetMenuBar()->IsChecked(MenuId_EnableInputRecording);
|
bool checked = GetMenuBar()->IsChecked(MenuId_EnableInputRecording);
|
||||||
// Confirm with User
|
// Confirm with User
|
||||||
|
@ -529,6 +530,9 @@ void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent&)
|
||||||
}
|
}
|
||||||
else
|
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);
|
GetMenuBar()->Remove(TopLevelMenu_InputRecording);
|
||||||
// Always turn controller logs off, but never turn it on by default
|
// Always turn controller logs off, but never turn it on by default
|
||||||
SysConsole.controlInfo.Enabled = checked;
|
SysConsole.controlInfo.Enabled = checked;
|
||||||
|
@ -540,6 +544,8 @@ void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent&)
|
||||||
viewport->InitDefaultAccelerators();
|
viewport->InitDefaultAccelerators();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (g_RecordingControls.IsEmulationAndRecordingPaused())
|
||||||
|
g_RecordingControls.Unpause();
|
||||||
}
|
}
|
||||||
|
|
||||||
g_Conf->EmuOptions.EnableRecordingTools = checked;
|
g_Conf->EmuOptions.EnableRecordingTools = checked;
|
||||||
|
@ -868,49 +874,61 @@ void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_Click(wxCommandEvent & eve
|
||||||
#ifndef DISABLE_RECORDING
|
#ifndef DISABLE_RECORDING
|
||||||
void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event)
|
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();
|
NewRecordingFrame* NewRecordingFrame = wxGetApp().GetNewRecordingFramePtr();
|
||||||
if (NewRecordingFrame)
|
if (NewRecordingFrame)
|
||||||
{
|
{
|
||||||
if (NewRecordingFrame->ShowModal() == wxID_CANCEL)
|
if (NewRecordingFrame->ShowModal() == wxID_CANCEL)
|
||||||
{
|
{
|
||||||
|
if (!initiallyPaused)
|
||||||
|
g_RecordingControls.Unpause();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// From Current Frame
|
if (!g_InputRecording.Create(NewRecordingFrame->GetFile(), !NewRecordingFrame->GetFrom(), NewRecordingFrame->GetAuthor()))
|
||||||
if (NewRecordingFrame->GetFrom() == 0)
|
|
||||||
{
|
{
|
||||||
if (!CoreThread.IsOpen())
|
if (!initiallyPaused)
|
||||||
{
|
g_RecordingControls.Unpause();
|
||||||
recordingConLog(L"[REC]: Game is not open, aborting new input recording.\n");
|
|
||||||
return;
|
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false);
|
m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false);
|
||||||
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
|
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
|
||||||
|
if (!g_InputRecordingData.FromSaveState())
|
||||||
|
g_RecordingControls.Unpause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainEmuFrame::Menu_Recording_Play_Click(wxCommandEvent &event)
|
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"",
|
wxFileDialog openFileDialog(this, _("Select P2M2 record file."), L"", L"",
|
||||||
L"p2m2 file(*.p2m2)|*.p2m2", wxFD_OPEN);
|
L"p2m2 file(*.p2m2)|*.p2m2", wxFD_OPEN);
|
||||||
if (openFileDialog.ShowModal() == wxID_CANCEL)
|
if (openFileDialog.ShowModal() == wxID_CANCEL)
|
||||||
{
|
{
|
||||||
|
if (!initiallyPaused)
|
||||||
|
g_RecordingControls.Unpause();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString path = openFileDialog.GetPath();
|
wxString path = openFileDialog.GetPath();
|
||||||
g_InputRecording.Play(path, 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_New)->Enable(false);
|
||||||
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
|
m_menuRecording.FindChildItem(MenuId_Recording_Stop)->Enable(true);
|
||||||
|
}
|
||||||
|
g_RecordingControls.Unpause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainEmuFrame::Menu_Recording_Stop_Click(wxCommandEvent &event)
|
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)
|
void MainEmuFrame::Menu_Recording_VirtualPad_Open_Click(wxCommandEvent &event)
|
||||||
{
|
{
|
||||||
VirtualPad *vp = NULL;
|
wxGetApp().GetVirtualPadPtr(event.GetId() - MenuId_Recording_VirtualPad_Port0)->Show();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue