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

View File

@ -16,7 +16,8 @@ public:
// menu bar
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
void RecordModeToggle();

View File

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

View File

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

View File

@ -788,7 +788,6 @@ void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event)
{
return;
}
g_InputRecordingHeader.setAuthor(NewRecordingFrame->getAuthor());
// From Current Frame
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");
return;
}
VmStateBuffer savestate;
memSavingState memSS(savestate);
memSS.FreezeAll();
g_InputRecording.Start(NewRecordingFrame->getFile(), false, &savestate);
g_InputRecording.Create(NewRecordingFrame->getFile(), true, NewRecordingFrame->getAuthor());
}
// From Power-On
else if (NewRecordingFrame->getFrom() == 1)
{
// 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);
@ -815,12 +811,12 @@ void MainEmuFrame::Menu_Recording_New_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_Recording_Play_Click(wxCommandEvent &event)
{
g_InputRecording.Stop();
wxFileDialog openFileDialog(this, _("Select P2M2 record file."), L"", L"",
L"p2m2 file(*.p2m2)|*.p2m2", wxFD_OPEN);
if (openFileDialog.ShowModal() == wxID_CANCEL)return; // cancel
if (openFileDialog.ShowModal() == wxID_CANCEL) return;
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_Stop)->Enable(true);
}