recording: Use conventional savestate functions, save save-state in a separate file alongside recording file

Regressions were discovered after merging with master due to way the save state data was saved within the movie file.
This change uses the same functions used in the GUI to create savestates to create a compressed save-state file.  Eventually this could be re-incorporated back into the recording file and could be backwards compatible.
This commit is contained in:
Tyler Wilding 2018-11-22 00:02:56 -05:00 committed by lightningterror
parent 270f7fd905
commit eb7030cf12
5 changed files with 129 additions and 158 deletions

View File

@ -1,6 +1,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "AppSaveStates.h" #include "AppSaveStates.h"
#include "AppGameDatabase.h"
#include "Common.h" #include "Common.h"
#include "Counters.h" #include "Counters.h"
#include "MemoryTypes.h" #include "MemoryTypes.h"
@ -117,54 +118,73 @@ void InputRecording::Stop() {
//---------------------------------- //----------------------------------
// start // start
//---------------------------------- //----------------------------------
void InputRecording::Start(wxString FileName,bool fReadOnly, VmStateBuffer* ss) void InputRecording::Create(wxString FileName, bool fromSaveState, wxString authorName)
{ {
g_RecordingControls.Pause(); g_RecordingControls.Pause();
Stop(); Stop();
if (fReadOnly) // create
if (!InputRecordingData.Open(FileName, true, fromSaveState)) {
return;
}
// Set author name
if (!authorName.IsEmpty())
{ {
if (!InputRecordingData.Open(FileName, false)) { InputRecordingData.getHeader().setAuthor(authorName);
return; }
} // Set Game Name
if (!InputRecordingData.readHeaderAndCheck()) { // Code loosely taken from AppCoreThread.cpp to resolve the Game Name
recordingConLog(L"[REC]: This file is not a correct InputRecording file.\n"); // Fallback is ISO name
InputRecordingData.Close(); wxString gameName;
return; const wxString gameKey(SysGetDiscID());
} if (!gameKey.IsEmpty())
// cdrom {
if (!g_Conf->CurrentIso.IsEmpty()) if (IGameDatabase* GameDB = AppHost_GetGameDatabase())
{ {
if (Path::GetFilename(g_Conf->CurrentIso) != InputRecordingData.getHeader().cdrom) { Game_Data game;
recordingConLog(L"[REC]: Information on CD in Movie file is Different.\n"); if (GameDB->findGame(game, gameKey))
{
gameName = game.getString("Name");
gameName += L" (" + game.getString("Region") + L")";
} }
} }
state = REPLAY;
recordingConLog(wxString::Format(L"[REC]: Replaying movie - [%s]\n",FileName));
recordingConLog(wxString::Format(L"MaxFrame: %d\n", InputRecordingData.getMaxFrame()));
recordingConLog(wxString::Format(L"UndoCount: %d\n", InputRecordingData.getUndoCount()));
} }
else InputRecordingData.getHeader().setGameName(!gameName.IsEmpty() ? gameName : Path::GetFilename(g_Conf->CurrentIso));
{ InputRecordingData.writeHeader();
// create state = RECORD;
if (!InputRecordingData.Open(FileName, true, ss)) { recordingConLog(wxString::Format(L"[REC]: Started new recording - [%s]\n", FileName));
return;
}
// cdrom
if (!g_Conf->CurrentIso.IsEmpty())
{
InputRecordingData.getHeader().setCdrom(Path::GetFilename(g_Conf->CurrentIso));
}
InputRecordingData.writeHeader();
InputRecordingData.writeSavestate();
state = RECORD;
recordingConLog(wxString::Format(L"[REC]: Started new recording - [%s]\n", FileName));
}
// In every case, we reset the g_FrameCount // In every case, we reset the g_FrameCount
g_FrameCount = 0; g_FrameCount = 0;
} }
void InputRecording::Play(wxString FileName, bool fromSaveState)
{
g_RecordingControls.Pause();
Stop();
if (!InputRecordingData.Open(FileName, false, false)) {
return;
}
if (!InputRecordingData.readHeaderAndCheck()) {
recordingConLog(L"[REC]: This file is not a correct InputRecording file.\n");
InputRecordingData.Close();
return;
}
// Check author name
if (!g_Conf->CurrentIso.IsEmpty())
{
if (Path::GetFilename(g_Conf->CurrentIso) != InputRecordingData.getHeader().gameName) {
recordingConLog(L"[REC]: Information on CD in Movie file is Different.\n");
}
}
// TODO - probably output more informatiion on it
state = REPLAY;
recordingConLog(wxString::Format(L"[REC]: Replaying movie - [%s]\n", FileName));
recordingConLog(wxString::Format(L"MaxFrame: %d\n", InputRecordingData.getMaxFrame()));
recordingConLog(wxString::Format(L"UndoCount: %d\n", InputRecordingData.getUndoCount()));
}
//---------------------------------- //----------------------------------
// shortcut key // shortcut key
//---------------------------------- //----------------------------------

View File

@ -16,7 +16,8 @@ public:
// menu bar // menu bar
void Stop(); void Stop();
void Start(wxString filename, bool fReadOnly, VmStateBuffer* ss = nullptr); void Create(wxString filename, bool fromSaveState, wxString authorName);
void Play(wxString filename, bool fromSaveState);
// shortcut key // shortcut key
void RecordModeToggle(); void RecordModeToggle();

View File

@ -1,14 +1,15 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "MemoryTypes.h"
#include "App.h" #include "App.h"
#include "Common.h" #include "Common.h"
#include "Counters.h" #include "Counters.h"
#include "MainFrame.h"
#include "MemoryTypes.h"
#include "InputRecordingFile.h" #include "InputRecordingFile.h"
#define HEADER_SIZE (sizeof(InputRecordingHeader)+4+4) #define HEADER_SIZE (sizeof(InputRecordingHeader)+4+4)
#define SAVESTATE_HEADER_SIZE (sizeof(bool) + sizeof(savestate.savestatesize) + sizeof(savestate.savestate[0]) * savestate.savestatesize) #define SAVESTATE_HEADER_SIZE (sizeof(bool))
#define BLOCK_HEADER_SIZE (0) #define BLOCK_HEADER_SIZE (0)
#define BLOCK_DATA_SIZE (18*2) #define BLOCK_DATA_SIZE (18*2)
#define BLOCK_SIZE (BLOCK_HEADER_SIZE+BLOCK_DATA_SIZE) #define BLOCK_SIZE (BLOCK_HEADER_SIZE+BLOCK_DATA_SIZE)
@ -29,10 +30,8 @@ long InputRecordingFile::_getBlockSeekPoint(const long & frame)
} }
} }
//---------------------------------- // Inits the new (or existing) input recording file
// file bool InputRecordingFile::Open(const wxString path, bool fNewOpen, bool fromSaveState)
//----------------------------------
bool InputRecordingFile::Open(const wxString path, bool fNewOpen, VmStateBuffer *ss)
{ {
Close(); Close();
wxString mode = L"rb+"; wxString mode = L"rb+";
@ -50,14 +49,13 @@ bool InputRecordingFile::Open(const wxString path, bool fNewOpen, VmStateBuffer
} }
filename = path; filename = path;
// TODO - from power on its fine
// problems seem to be be based in how we are saving the savestate
if (fNewOpen) { if (fNewOpen) {
if (ss) { if (fromSaveState) {
savestate.fromSavestate = true; savestate.fromSavestate = true;
savestate.savestatesize = ss->GetLength(); // TODO - Check if existing, if so rename
savestate.savestate.MakeRoomFor(ss->GetLength()); StateCopy_SaveToFile(path + "_SaveState.p2s");
for (size_t i = 0; i < ss->GetLength(); i++) {
savestate.savestate[i] = (*ss)[i];
}
} }
else { else {
sApp.SysExecute(); sApp.SysExecute();
@ -65,17 +63,28 @@ bool InputRecordingFile::Open(const wxString path, bool fNewOpen, VmStateBuffer
} }
return true; return true;
} }
bool InputRecordingFile::Close() bool InputRecordingFile::Close()
{ {
if (recordingFile == NULL)return false; if (recordingFile == NULL)return false;
writeHeader(); writeHeader();
writeSavestate(); writeSaveState();
fclose(recordingFile); fclose(recordingFile);
recordingFile = NULL; recordingFile = NULL;
filename = ""; filename = "";
return true; return true;
} }
bool InputRecordingFile::writeSaveState() {
if (recordingFile == NULL)
{
return false;
}
fseek(recordingFile, SEEKPOINT_SAVESTATE, SEEK_SET);
if (fwrite(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1) return false;
return true;
}
//---------------------------------- //----------------------------------
// write frame // write frame
//---------------------------------- //----------------------------------
@ -111,9 +120,6 @@ bool InputRecordingFile::readKeyBuf(u8 & result,const uint & frame, const uint p
return true; return true;
} }
//=================================== //===================================
// pad // pad
//=================================== //===================================
@ -126,6 +132,7 @@ void InputRecordingFile::getPadData(PadData & result, unsigned long frame)
if (fread(result.buf, 1, BLOCK_DATA_SIZE, recordingFile) == 0)return; if (fread(result.buf, 1, BLOCK_DATA_SIZE, recordingFile) == 0)return;
result.fExistKey = true; result.fExistKey = true;
} }
bool InputRecordingFile::DeletePadData(unsigned long frame) bool InputRecordingFile::DeletePadData(unsigned long frame)
{ {
if (recordingFile == NULL)return false; if (recordingFile == NULL)return false;
@ -147,6 +154,7 @@ bool InputRecordingFile::DeletePadData(unsigned long frame)
return true; return true;
} }
bool InputRecordingFile::InsertPadData(unsigned long frame, const PadData& key) bool InputRecordingFile::InsertPadData(unsigned long frame, const PadData& key)
{ {
if (recordingFile == NULL)return false; if (recordingFile == NULL)return false;
@ -174,88 +182,53 @@ bool InputRecordingFile::InsertPadData(unsigned long frame, const PadData& key)
return true; return true;
} }
bool InputRecordingFile::UpdatePadData(unsigned long frame, const PadData& key) bool InputRecordingFile::UpdatePadData(unsigned long frame, const PadData& key)
{ {
if (recordingFile == NULL)return false; if (recordingFile == NULL) return false;
if (!key.fExistKey)return false; if (!key.fExistKey) return false;
long seek = _getBlockSeekPoint(frame) + BLOCK_HEADER_SIZE; long seek = _getBlockSeekPoint(frame) + BLOCK_HEADER_SIZE;
fseek(recordingFile, seek, SEEK_SET); fseek(recordingFile, seek, SEEK_SET);
if (fwrite(key.buf, 1, BLOCK_DATA_SIZE, recordingFile) == 0)return false; if (fwrite(key.buf, 1, BLOCK_DATA_SIZE, recordingFile) == 0) return false;
fflush(recordingFile); fflush(recordingFile);
return true; return true;
} }
// TODO - see if we can get the actual game name, not just the ISO name
// Verify header of recording file
//===================================
// header
//===================================
bool InputRecordingFile::readHeaderAndCheck() bool InputRecordingFile::readHeaderAndCheck()
{ {
if (recordingFile == NULL)return false; if (recordingFile == NULL) return false;
rewind(recordingFile); rewind(recordingFile);
if (fread(&header, sizeof(InputRecordingHeader), 1, recordingFile) != 1)return false; if (fread(&header, sizeof(InputRecordingHeader), 1, recordingFile) != 1) return false;
if (fread(&MaxFrame, 4, 1, recordingFile) != 1)return false; if (fread(&MaxFrame, 4, 1, recordingFile) != 1) return false;
if (fread(&UndoCount, 4, 1, recordingFile) != 1)return false; if (fread(&UndoCount, 4, 1, recordingFile) != 1) return false;
if (fread(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1) return false; if (fread(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1) return false;
if (savestate.fromSavestate) { if (savestate.fromSavestate) {
// We read the size (and the savestate) only if we must // TODO - check to see if the file is there, if it AINT, return false, throw an error, ETC (SAY WHAT FILE WE ARE LOOKING FOR)
if (fread(&savestate.savestatesize, sizeof(savestate.savestatesize), 1, recordingFile) != 1) return false; StateCopy_LoadFromFile(filename + "_SaveState.p2s");
if (savestate.savestatesize == 0) {
recordingConLog(L"[REC]: Invalid size of the savestate.\n");
return false;
}
savestate.savestate.MakeRoomFor(savestate.savestatesize);
// We read "savestatesize" * the size of a cell
if (fread(savestate.savestate.GetPtr(), sizeof(savestate.savestate[0]), savestate.savestatesize, recordingFile)
!= savestate.savestatesize) return false;
// We load the savestate
memLoadingState load(savestate.savestate);
UI_DisableSysActions();
GetCoreThread().Pause();
SysClearExecutionCache();
load.FreezeAll();
GetCoreThread().Resume();
} }
else { else {
sApp.SysExecute(); sApp.SysExecute();
} }
// ID // Check for current verison
if (header.ID != 0xCC) { // TODO - more specific log if fails for this reason
return false; if (header.version != 1) {
}
// ver
if (header.version != 3) {
return false; return false;
} }
return true; return true;
} }
bool InputRecordingFile::writeHeader() bool InputRecordingFile::writeHeader()
{ {
if (recordingFile == NULL)return false; if (recordingFile == NULL) return false;
rewind(recordingFile); rewind(recordingFile);
if (fwrite(&header, sizeof(InputRecordingHeader), 1, recordingFile) != 1) return false; if (fwrite(&header, sizeof(InputRecordingHeader), 1, recordingFile) != 1) return false;
return true; return true;
} }
bool InputRecordingFile::writeSavestate()
{
if (recordingFile == NULL) return false;
fseek(recordingFile, SEEKPOINT_SAVESTATE, SEEK_SET);
if (fwrite(&savestate.fromSavestate, sizeof(bool), 1, recordingFile) != 1) return false;
if (savestate.fromSavestate) {
if (fwrite(&savestate.savestatesize, sizeof(savestate.savestatesize), 1, recordingFile) != 1) return false;
if (fwrite(savestate.savestate.GetPtr(), sizeof(savestate.savestate[0]), savestate.savestatesize, recordingFile)
!= savestate.savestatesize) return false;
}
return true;
}
bool InputRecordingFile::writeMaxFrame() bool InputRecordingFile::writeMaxFrame()
{ {
if (recordingFile == NULL)return false; if (recordingFile == NULL)return false;
@ -263,6 +236,7 @@ bool InputRecordingFile::writeMaxFrame()
if (fwrite(&MaxFrame, 4, 1, recordingFile) != 1) return false; if (fwrite(&MaxFrame, 4, 1, recordingFile) != 1) return false;
return true; return true;
} }
void InputRecordingFile::updateFrameMax(unsigned long frame) void InputRecordingFile::updateFrameMax(unsigned long frame)
{ {
if (MaxFrame >= frame) { if (MaxFrame >= frame) {
@ -273,13 +247,13 @@ void InputRecordingFile::updateFrameMax(unsigned long frame)
fseek(recordingFile, SEEKPOINT_FRAMEMAX, SEEK_SET); fseek(recordingFile, SEEKPOINT_FRAMEMAX, SEEK_SET);
fwrite(&MaxFrame, 4, 1, recordingFile); fwrite(&MaxFrame, 4, 1, recordingFile);
} }
void InputRecordingFile::addUndoCount() void InputRecordingFile::addUndoCount()
{ {
UndoCount++; UndoCount++;
if (recordingFile == NULL)return; if (recordingFile == NULL)return;
fseek(recordingFile, SEEKPOINT_UNDOCOUNT, SEEK_SET); fseek(recordingFile, SEEKPOINT_UNDOCOUNT, SEEK_SET);
fwrite(&UndoCount, 4, 1, recordingFile); fwrite(&UndoCount, 4, 1, recordingFile);
} }
void InputRecordingHeader::setAuthor(wxString _author) void InputRecordingHeader::setAuthor(wxString _author)
@ -288,14 +262,16 @@ void InputRecordingHeader::setAuthor(wxString _author)
strncpy(author, _author.c_str(), max); strncpy(author, _author.c_str(), max);
author[max] = 0; author[max] = 0;
} }
void InputRecordingHeader::setCdrom(wxString _cdrom)
void InputRecordingHeader::setGameName(wxString _gameName)
{ {
int max = ArraySize(cdrom) - 1; int max = ArraySize(gameName) - 1;
strncpy(cdrom, _cdrom.c_str(), max); strncpy(gameName, _gameName.c_str(), max);
cdrom[max] = 0; gameName[max] = 0;
} }
void InputRecordingHeader::init() void InputRecordingHeader::init()
{ {
memset(author, 0, ArraySize(author)); memset(author, 0, ArraySize(author));
memset(cdrom, 0, ArraySize(cdrom)); memset(gameName, 0, ArraySize(gameName));
} }

View File

@ -5,87 +5,65 @@
struct InputRecordingHeader struct InputRecordingHeader
{ {
u8 version = 3; u8 version = 1;
u8 ID = 0xCC; char emu[50] = "PCSX2-1.5.X";
char author[255] = "";
char emu[50] = "pcsx2-1.5.X"; char gameName[255] = "";
char author[50] = "";
char cdrom[50] = "";
public: public:
void setAuthor(wxString author); void setAuthor(wxString author);
void setCdrom(wxString cdrom); void setGameName(wxString cdrom);
void init(); void init();
}; };
//----------------------------
// InputRecordingSavestate
// Contains info about the starting point of the movie // Contains info about the starting point of the movie
//----------------------------
struct InputRecordingSavestate struct InputRecordingSavestate
{ {
bool fromSavestate = false; // Whether we start from the savestate or from power-on // Whether we start from the savestate or from power-on
unsigned int savestatesize; // The size of the savestate bool fromSavestate = false;
VmStateBuffer savestate; // The savestate
}; };
//----------------------------
// InputRecordingFile
//----------------------------
class InputRecordingFile { class InputRecordingFile {
public: public:
InputRecordingFile() {} InputRecordingFile() {}
~InputRecordingFile() { Close(); } ~InputRecordingFile() { Close(); }
public:
// file // Movie File Manipulation
bool Open(const wxString fn, bool fNewOpen, VmStateBuffer *ss = nullptr); bool Open(const wxString fn, bool fNewOpen, bool fromSaveState);
bool Close(); bool Close();
// movie
bool writeKeyBuf(const uint & frame, const uint port, const uint bufIndex, const u8 & buf); 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); bool readKeyBuf(u8 & result, const uint & frame, const uint port, const uint bufIndex);
// pad data // Controller Data
void getPadData(PadData & result_pad, unsigned long frame); void getPadData(PadData & result_pad, unsigned long frame);
bool DeletePadData(unsigned long frame); bool DeletePadData(unsigned long frame);
bool InsertPadData(unsigned long frame, const PadData& key); bool InsertPadData(unsigned long frame, const PadData& key);
bool UpdatePadData(unsigned long frame, const PadData& key); bool UpdatePadData(unsigned long frame, const PadData& key);
private: // Header
FILE * recordingFile = NULL;
wxString filename = "";
private:
//--------------------
// block
//--------------------
long _getBlockSeekPoint(const long & frame);
public:
//--------------------
// header
//--------------------
InputRecordingHeader& getHeader() { return header; } InputRecordingHeader& getHeader() { return header; }
unsigned long& getMaxFrame() { return MaxFrame; } unsigned long& getMaxFrame() { return MaxFrame; }
unsigned long& getUndoCount() { return UndoCount; } unsigned long& getUndoCount() { return UndoCount; }
const wxString & getFilename() { return filename; } const wxString & getFilename() { return filename; }
bool writeHeader(); bool writeHeader();
bool writeSavestate();
bool writeMaxFrame(); bool writeMaxFrame();
bool writeSaveState();
bool readHeaderAndCheck(); bool readHeaderAndCheck();
void updateFrameMax(unsigned long frame); void updateFrameMax(unsigned long frame);
void addUndoCount(); void addUndoCount();
private: private:
// Movie File
FILE * recordingFile = NULL;
wxString filename = "";
long _getBlockSeekPoint(const long & frame);
// Header
InputRecordingHeader header; InputRecordingHeader header;
InputRecordingSavestate savestate; InputRecordingSavestate savestate;
unsigned long MaxFrame = 0; unsigned long MaxFrame = 0;
unsigned long UndoCount = 0; unsigned long UndoCount = 0;
}; };

View File

@ -788,7 +788,6 @@ void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event)
{ {
return; return;
} }
g_InputRecordingHeader.setAuthor(NewRecordingFrame->getAuthor());
// From Current Frame // From Current Frame
if (NewRecordingFrame->getFrom() == 0) if (NewRecordingFrame->getFrom() == 0)
{ {
@ -796,16 +795,13 @@ void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event)
recordingConLog(L"[REC]: Game is not open, aborting new input recording.\n"); recordingConLog(L"[REC]: Game is not open, aborting new input recording.\n");
return; return;
} }
VmStateBuffer savestate; g_InputRecording.Create(NewRecordingFrame->getFile(), true, NewRecordingFrame->getAuthor());
memSavingState memSS(savestate);
memSS.FreezeAll();
g_InputRecording.Start(NewRecordingFrame->getFile(), false, &savestate);
} }
// From Power-On // From Power-On
else if (NewRecordingFrame->getFrom() == 1) else if (NewRecordingFrame->getFrom() == 1)
{ {
// TODO extensively test this // TODO extensively test this
g_InputRecording.Start(NewRecordingFrame->getFile(), false); g_InputRecording.Create(NewRecordingFrame->getFile(), false, NewRecordingFrame->getAuthor());
} }
} }
m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false); m_menuRecording.FindChildItem(MenuId_Recording_New)->Enable(false);
@ -815,12 +811,12 @@ void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_Recording_Play_Click(wxCommandEvent &event) void MainEmuFrame::Menu_Recording_Play_Click(wxCommandEvent &event)
{ {
g_InputRecording.Stop(); g_InputRecording.Stop();
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)return; // cancel if (openFileDialog.ShowModal() == wxID_CANCEL) return;
wxString path = openFileDialog.GetPath(); wxString path = openFileDialog.GetPath();
g_InputRecording.Start(path, true); g_InputRecording.Play(path, true);
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);
} }