diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index c6ee3bb57b..7221633eae 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -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() diff --git a/pcsx2/Recording/InputRecording.h b/pcsx2/Recording/InputRecording.h index 06a9e9ce06..d3feb4000d 100644 --- a/pcsx2/Recording/InputRecording.h +++ b/pcsx2/Recording/InputRecording.h @@ -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 diff --git a/pcsx2/Recording/InputRecordingFile.cpp b/pcsx2/Recording/InputRecordingFile.cpp index 464356f601..3f03f521d1 100644 --- a/pcsx2/Recording/InputRecordingFile.cpp +++ b/pcsx2/Recording/InputRecordingFile.cpp @@ -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 diff --git a/pcsx2/Recording/InputRecordingFile.h b/pcsx2/Recording/InputRecordingFile.h index 22d0e14f2d..4c5bcd8006 100644 --- a/pcsx2/Recording/InputRecordingFile.h +++ b/pcsx2/Recording/InputRecordingFile.h @@ -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 diff --git a/pcsx2/Recording/PadData.cpp b/pcsx2/Recording/PadData.cpp index 165487596e..9820dfb91c 100644 --- a/pcsx2/Recording/PadData.cpp +++ b/pcsx2/Recording/PadData.cpp @@ -76,10 +76,6 @@ void PadData::SetNormalButtons(int port, std::vector 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 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)]; } diff --git a/pcsx2/Recording/RecordingControls.cpp b/pcsx2/Recording/RecordingControls.cpp index c86e346465..516bb192f8 100644 --- a/pcsx2/Recording/RecordingControls.cpp +++ b/pcsx2/Recording/RecordingControls.cpp @@ -110,13 +110,11 @@ void RecordingControls::TogglePause() void RecordingControls::Pause() { fStop = true; - fFrameAdvance = true; } void RecordingControls::Unpause() { fStop = false; fStart = true; - fFrameAdvance = true; } #endif diff --git a/pcsx2/Recording/RecordingInputManager.cpp b/pcsx2/Recording/RecordingInputManager.cpp index d5631a88d8..f896b152a7 100644 --- a/pcsx2/Recording/RecordingInputManager.cpp +++ b/pcsx2/Recording/RecordingInputManager.cpp @@ -32,10 +32,6 @@ RecordingInputManager::RecordingInputManager() void RecordingInputManager::ControllerInterrupt(u8 & data, u8 & port, u16 & BufCount, u8 buf[]) { - if (port >= 2) - { - return; - } if (virtualPad[port]) { diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp index c207f79945..ce00dd2b18 100644 --- a/pcsx2/Sio.cpp +++ b/pcsx2/Sio.cpp @@ -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; diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp index a39c9a586d..a063bf0608 100644 --- a/pcsx2/gui/AppInit.cpp +++ b/pcsx2/gui/AppInit.cpp @@ -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); diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index e492f4ae7b..8e856d3c78 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -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(); } diff --git a/pcsx2/gui/FrameForGS.cpp b/pcsx2/gui/FrameForGS.cpp index 136ea8f1f5..c2a3ad38aa 100644 --- a/pcsx2/gui/FrameForGS.cpp +++ b/pcsx2/gui/FrameForGS.cpp @@ -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; diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index bb6f30d8e8..adf90ed6c9 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -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