#include "stdafx.h" #include "HistoryViewer.h" #include "RewindData.h" #include "Console.h" #include "BaseControlDevice.h" #include "SoundMixer.h" #include "NotificationManager.h" #include "RomData.h" #include "MovieRecorder.h" #include "SaveStateManager.h" HistoryViewer::HistoryViewer(shared_ptr console) { _console = console; _position = 0; _pollCounter = 0; } HistoryViewer::~HistoryViewer() { } void HistoryViewer::SetHistoryData(std::deque &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); } void HistoryViewer::GetHistorySegments(uint32_t *segmentBuffer, uint32_t &bufferSize) { uint32_t segmentIndex = 0; for(size_t i = 0; i < _history.size(); i++) { if(_history[i].EndOfSegment) { segmentBuffer[segmentIndex] = (uint32_t)i; segmentIndex++; if(segmentIndex == bufferSize) { //Reached buffer size, can't return any more values break; } } } bufferSize = segmentIndex; } uint32_t HistoryViewer::GetPosition() { return _position; } void HistoryViewer::SeekTo(uint32_t seekPosition) { //Seek to the specified position if(seekPosition < _history.size()) { _console->Pause(); bool wasPaused = _console->GetSettings()->CheckFlag(EmulationFlags::Paused); _console->GetSettings()->ClearFlags(EmulationFlags::Paused); _position = seekPosition; RewindData rewindData = _history[_position]; rewindData.LoadState(_console); _console->GetSoundMixer()->StopAudio(true); _pollCounter = 0; if(wasPaused) { _console->GetSettings()->SetFlags(EmulationFlags::Paused); } _console->Resume(); } } 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; } 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 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, 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(); } bool HistoryViewer::SetInput(BaseControlDevice *device) { uint8_t port = device->GetPort(); if(_position < _history.size()) { std::deque &stateData = _history[_position].InputLogs[port]; if(_pollCounter < stateData.size()) { ControlDeviceState state = stateData[_pollCounter]; device->SetRawState(state); } } 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 _console->GetSettings()->SetFlags(EmulationFlags::Paused); return; } RewindData rewindData = _history[_position]; rewindData.LoadState(_console); } }