2018-07-11 18:07:13 -04:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include "HistoryViewer.h"
|
|
|
|
#include "RewindData.h"
|
|
|
|
#include "Console.h"
|
|
|
|
#include "BaseControlDevice.h"
|
|
|
|
#include "SoundMixer.h"
|
|
|
|
#include "NotificationManager.h"
|
2018-07-15 18:26:08 -04:00
|
|
|
#include "RomData.h"
|
|
|
|
#include "MovieRecorder.h"
|
2019-02-01 13:05:25 -05:00
|
|
|
#include "SaveStateManager.h"
|
2018-07-11 18:07:13 -04:00
|
|
|
|
|
|
|
HistoryViewer::HistoryViewer(shared_ptr<Console> console)
|
|
|
|
{
|
|
|
|
_console = console;
|
|
|
|
_position = 0;
|
|
|
|
_pollCounter = 0;
|
|
|
|
}
|
|
|
|
|
2018-07-15 19:27:38 -04:00
|
|
|
HistoryViewer::~HistoryViewer()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-07-11 18:07:13 -04:00
|
|
|
void HistoryViewer::SetHistoryData(std::deque<RewindData> &history)
|
|
|
|
{
|
|
|
|
_history = history;
|
|
|
|
|
|
|
|
_console->GetControlManager()->UnregisterInputProvider(this);
|
|
|
|
_console->GetControlManager()->RegisterInputProvider(this);
|
|
|
|
|
|
|
|
SeekTo(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t HistoryViewer::GetHistoryLength()
|
|
|
|
{
|
|
|
|
//Returns history length in number of frames
|
|
|
|
return (uint32_t)(_history.size() * HistoryViewer::BufferSize);
|
|
|
|
}
|
|
|
|
|
2018-07-15 18:26:08 -04:00
|
|
|
void HistoryViewer::GetHistorySegments(uint32_t *segmentBuffer, uint32_t &bufferSize)
|
|
|
|
{
|
2018-07-19 20:17:20 -04:00
|
|
|
uint32_t segmentIndex = 0;
|
2018-07-15 19:27:38 -04:00
|
|
|
for(size_t i = 0; i < _history.size(); i++) {
|
2018-07-15 18:26:08 -04:00
|
|
|
if(_history[i].EndOfSegment) {
|
2018-07-19 20:17:20 -04:00
|
|
|
segmentBuffer[segmentIndex] = (uint32_t)i;
|
2018-07-15 18:26:08 -04:00
|
|
|
segmentIndex++;
|
|
|
|
|
|
|
|
if(segmentIndex == bufferSize) {
|
|
|
|
//Reached buffer size, can't return any more values
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bufferSize = segmentIndex;
|
|
|
|
}
|
|
|
|
|
2018-07-11 18:07:13 -04:00
|
|
|
uint32_t HistoryViewer::GetPosition()
|
|
|
|
{
|
|
|
|
return _position;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryViewer::SeekTo(uint32_t seekPosition)
|
|
|
|
{
|
2018-07-15 18:26:08 -04:00
|
|
|
//Seek to the specified position
|
|
|
|
if(seekPosition < _history.size()) {
|
2018-07-11 18:07:13 -04:00
|
|
|
_console->Pause();
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
bool wasPaused = _console->GetSettings()->CheckFlag(EmulationFlags::Paused);
|
|
|
|
_console->GetSettings()->ClearFlags(EmulationFlags::Paused);
|
2018-07-15 18:26:08 -04:00
|
|
|
_position = seekPosition;
|
2018-07-11 18:07:13 -04:00
|
|
|
RewindData rewindData = _history[_position];
|
|
|
|
rewindData.LoadState(_console);
|
|
|
|
|
|
|
|
_console->GetSoundMixer()->StopAudio(true);
|
|
|
|
_pollCounter = 0;
|
2018-07-13 22:19:26 -04:00
|
|
|
|
|
|
|
if(wasPaused) {
|
|
|
|
_console->GetSettings()->SetFlags(EmulationFlags::Paused);
|
|
|
|
}
|
2018-07-11 18:07:13 -04:00
|
|
|
|
|
|
|
_console->Resume();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-01 13:05:25 -05:00
|
|
|
bool HistoryViewer::CreateSaveState(string outputFile, uint32_t position)
|
|
|
|
{
|
|
|
|
if(position < _history.size()) {
|
|
|
|
std::stringstream stateData;
|
|
|
|
_console->GetSaveStateManager()->GetSaveStateHeader(stateData);
|
|
|
|
_history[position].GetStateData(stateData);
|
|
|
|
|
|
|
|
ofstream output(outputFile, ios::binary);
|
|
|
|
if(output) {
|
|
|
|
output << stateData.rdbuf();
|
|
|
|
output.close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-15 18:26:08 -04:00
|
|
|
bool HistoryViewer::SaveMovie(string movieFile, uint32_t startPosition, uint32_t endPosition)
|
|
|
|
{
|
|
|
|
//Take a savestate to be able to restore it after generating the movie file
|
|
|
|
//(the movie generation uses the console's inputs, which could affect the emulation otherwise)
|
|
|
|
stringstream state;
|
|
|
|
_console->Pause();
|
|
|
|
_console->SaveState(state);
|
|
|
|
|
|
|
|
//Convert the rewind data to a .mmo file
|
|
|
|
unique_ptr<MovieRecorder> recorder(new MovieRecorder(_console));
|
|
|
|
bool result = recorder->CreateMovie(movieFile, _history, startPosition, endPosition);
|
|
|
|
|
|
|
|
//Resume the state and resume
|
|
|
|
_console->LoadState(state);
|
|
|
|
_console->Resume();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryViewer::ResumeGameplay(shared_ptr<Console> console, uint32_t resumePosition)
|
|
|
|
{
|
|
|
|
console->Pause();
|
|
|
|
if(_console->GetRomInfo().Hash.Sha1 != console->GetRomInfo().Hash.Sha1) {
|
|
|
|
//Load game on the main window if they aren't the same
|
|
|
|
console->Initialize(_console->GetRomPath(), _console->GetPatchFile());
|
|
|
|
}
|
|
|
|
if(resumePosition < _history.size()) {
|
|
|
|
_history[resumePosition].LoadState(console);
|
|
|
|
} else {
|
|
|
|
_history[_history.size() - 1].LoadState(console);
|
|
|
|
}
|
|
|
|
console->Resume();
|
|
|
|
}
|
|
|
|
|
2018-07-11 18:07:13 -04:00
|
|
|
bool HistoryViewer::SetInput(BaseControlDevice *device)
|
|
|
|
{
|
|
|
|
uint8_t port = device->GetPort();
|
2018-07-22 18:43:15 -04:00
|
|
|
if(_position < _history.size()) {
|
|
|
|
std::deque<ControlDeviceState> &stateData = _history[_position].InputLogs[port];
|
|
|
|
if(_pollCounter < stateData.size()) {
|
|
|
|
ControlDeviceState state = stateData[_pollCounter];
|
|
|
|
device->SetRawState(state);
|
|
|
|
}
|
2018-07-11 18:07:13 -04:00
|
|
|
}
|
|
|
|
if(port == 0 && _pollCounter < 30) {
|
|
|
|
_pollCounter++;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HistoryViewer::ProcessEndOfFrame()
|
|
|
|
{
|
|
|
|
if(_pollCounter == HistoryViewer::BufferSize) {
|
|
|
|
_pollCounter = 0;
|
|
|
|
_position++;
|
|
|
|
|
|
|
|
if(_position >= _history.size()) {
|
|
|
|
//Reached the end of history data
|
2018-07-13 22:19:26 -04:00
|
|
|
_console->GetSettings()->SetFlags(EmulationFlags::Paused);
|
2018-07-11 18:07:13 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RewindData rewindData = _history[_position];
|
|
|
|
rewindData.LoadState(_console);
|
|
|
|
}
|
|
|
|
}
|