1 #include "stdafx.h"
2 #include "RewindManager.h"
3 #include "MessageManager.h"
4 #include "Console.h"
5 #include "VideoRenderer.h"
6 #include "SoundMixer.h"
7 #include "BaseControlDevice.h"
8 #include "HistoryViewer.h"
9 
RewindManager(shared_ptr<Console> console)10 RewindManager::RewindManager(shared_ptr<Console> console)
11 {
12 	_console = console;
13 	_settings = console->GetSettings();
14 	_rewindState = RewindState::Stopped;
15 	_framesToFastForward = 0;
16 	_hasHistory = false;
17 	AddHistoryBlock();
18 
19 	_console->GetControlManager()->RegisterInputProvider(this);
20 	_console->GetControlManager()->RegisterInputRecorder(this);
21 }
22 
~RewindManager()23 RewindManager::~RewindManager()
24 {
25 	_console->GetControlManager()->UnregisterInputProvider(this);
26 	_console->GetControlManager()->UnregisterInputRecorder(this);
27 }
28 
ClearBuffer()29 void RewindManager::ClearBuffer()
30 {
31 	_hasHistory = false;
32 	_history.clear();
33 	_historyBackup.clear();
34 	_currentHistory = RewindData();
35 	_framesToFastForward = 0;
36 	_videoHistory.clear();
37 	_videoHistoryBuilder.clear();
38 	_audioHistory.clear();
39 	_audioHistoryBuilder.clear();
40 	_rewindState = RewindState::Stopped;
41 	_currentHistory = RewindData();
42 }
43 
ProcessNotification(ConsoleNotificationType type,void * parameter)44 void RewindManager::ProcessNotification(ConsoleNotificationType type, void * parameter)
45 {
46 	if(type == ConsoleNotificationType::PpuFrameDone) {
47 		_hasHistory = _history.size() >= 2;
48 		if(_settings->GetRewindBufferSize() > 0) {
49 			switch(_rewindState) {
50 				case RewindState::Starting:
51 				case RewindState::Started:
52 				case RewindState::Debugging:
53 					_currentHistory.FrameCount--;
54 					break;
55 
56 				case RewindState::Stopping:
57 					_framesToFastForward--;
58 					_currentHistory.FrameCount++;
59 					if(_framesToFastForward == 0) {
60 						for(int i = 0; i < 4; i++) {
61 							size_t numberToRemove = _currentHistory.InputLogs[i].size();
62 							_currentHistory.InputLogs[i] = _historyBackup.front().InputLogs[i];
63 							for(size_t j = 0; j < numberToRemove; j++) {
64 								_currentHistory.InputLogs[i].pop_back();
65 							}
66 						}
67 						_historyBackup.clear();
68 						_rewindState = RewindState::Stopped;
69 						_settings->ClearFlags(EmulationFlags::Rewind);
70 						_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
71 					}
72 					break;
73 
74 				case RewindState::Stopped:
75 					_currentHistory.FrameCount++;
76 					break;
77 			}
78 		} else {
79 			ClearBuffer();
80 		}
81 	} else if(type == ConsoleNotificationType::StateLoaded) {
82 		if(_rewindState == RewindState::Stopped) {
83 			//A save state was loaded by the user, mark as the end of the current "segment" (for history viewer)
84 			_currentHistory.EndOfSegment = true;
85 		}
86 	}
87 }
88 
AddHistoryBlock()89 void RewindManager::AddHistoryBlock()
90 {
91 	uint32_t maxHistorySize = _settings->GetRewindBufferSize() * 120;
92 	if(maxHistorySize > 0) {
93 		while(_history.size() > maxHistorySize) {
94 			_history.pop_front();
95 		}
96 
97 		if(_currentHistory.FrameCount > 0) {
98 			_history.push_back(_currentHistory);
99 		}
100 		_currentHistory = RewindData();
101 		_currentHistory.SaveState(_console);
102 	}
103 }
104 
PopHistory()105 void RewindManager::PopHistory()
106 {
107 	if(_history.empty() && _currentHistory.FrameCount <= 0) {
108 		StopRewinding();
109 	} else {
110 		if(_currentHistory.FrameCount <= 0) {
111 			_currentHistory = _history.back();
112 			_history.pop_back();
113 		}
114 
115 		_historyBackup.push_front(_currentHistory);
116 		_currentHistory.LoadState(_console);
117 		if(!_audioHistoryBuilder.empty()) {
118 			_audioHistory.insert(_audioHistory.begin(), _audioHistoryBuilder.begin(), _audioHistoryBuilder.end());
119 			_audioHistoryBuilder.clear();
120 		}
121 	}
122 }
123 
Start(bool forDebugger)124 void RewindManager::Start(bool forDebugger)
125 {
126 	if(_rewindState == RewindState::Stopped && _settings->GetRewindBufferSize() > 0) {
127 		_console->Pause();
128 
129 		_rewindState = forDebugger ? RewindState::Debugging : RewindState::Starting;
130 		_videoHistoryBuilder.clear();
131 		_videoHistory.clear();
132 		_audioHistoryBuilder.clear();
133 		_audioHistory.clear();
134 		_historyBackup.clear();
135 
136 		PopHistory();
137 		_console->GetSoundMixer()->StopAudio(true);
138 		_settings->SetFlags(EmulationFlags::ForceMaxSpeed);
139 		_settings->SetFlags(EmulationFlags::Rewind);
140 
141 		_console->Resume();
142 	}
143 }
144 
ForceStop()145 void RewindManager::ForceStop()
146 {
147 	if(_rewindState != RewindState::Stopped) {
148 		while(_historyBackup.size() > 1) {
149 			_history.push_back(_historyBackup.front());
150 			_historyBackup.pop_front();
151 		}
152 		_currentHistory = _historyBackup.front();
153 		_historyBackup.clear();
154 		_rewindState = RewindState::Stopped;
155 		_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
156 		_settings->ClearFlags(EmulationFlags::Rewind);
157 	}
158 }
159 
Stop()160 void RewindManager::Stop()
161 {
162 	if(_rewindState >= RewindState::Starting) {
163 		_console->Pause();
164 		if(_rewindState == RewindState::Started) {
165 			//Move back to the save state containing the frame currently shown on the screen
166 			if(_historyBackup.size() > 1) {
167 				_framesToFastForward = (uint32_t)_videoHistory.size() + _historyBackup.front().FrameCount;
168 				do {
169 					_history.push_back(_historyBackup.front());
170 					_framesToFastForward -= _historyBackup.front().FrameCount;
171 					_historyBackup.pop_front();
172 
173 					_currentHistory = _historyBackup.front();
174 				}
175 				while(_framesToFastForward > RewindManager::BufferSize && _historyBackup.size() > 1);
176 			}
177 		} else {
178 			//We started rewinding, but didn't actually visually rewind anything yet
179 			//Move back to the save state containing the frame currently shown on the screen
180 			while(_historyBackup.size() > 1) {
181 				_history.push_back(_historyBackup.front());
182 				_historyBackup.pop_front();
183 			}
184 			_currentHistory = _historyBackup.front();
185 			_framesToFastForward = _historyBackup.front().FrameCount;
186 		}
187 
188 		_currentHistory.LoadState(_console);
189 		if(_framesToFastForward > 0) {
190 			_rewindState = RewindState::Stopping;
191 			_currentHistory.FrameCount = 0;
192 			_settings->SetFlags(EmulationFlags::ForceMaxSpeed);
193 		} else {
194 			_rewindState = RewindState::Stopped;
195 			_historyBackup.clear();
196 			_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
197 			_settings->ClearFlags(EmulationFlags::Rewind);
198 		}
199 
200 		_videoHistoryBuilder.clear();
201 		_videoHistory.clear();
202 		_audioHistoryBuilder.clear();
203 		_audioHistory.clear();
204 
205 		_console->Resume();
206 	}
207 }
208 
ProcessEndOfFrame()209 void RewindManager::ProcessEndOfFrame()
210 {
211 	if(_rewindState >= RewindState::Starting) {
212 		if(_currentHistory.FrameCount <= 0 && _rewindState != RewindState::Debugging) {
213 			//If we're debugging, we want to keep running the emulation to the end of the next frame (even if it's incomplete)
214 			//Otherwise the emulation might diverge due to missing inputs.
215 			PopHistory();
216 		}
217 	} else if(_currentHistory.FrameCount >= RewindManager::BufferSize) {
218 		AddHistoryBlock();
219 	}
220 }
221 
ProcessFrame(void * frameBuffer,uint32_t width,uint32_t height,bool forRewind)222 void RewindManager::ProcessFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind)
223 {
224 	if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
225 		if(!forRewind) {
226 			//Ignore any frames that occur between start of rewind process & first rewinded frame completed
227 			//These are caused by the fact that VideoDecoder is asynchronous - a previous (extra) frame can end up
228 			//in the rewind queue, which causes display glitches
229 			return;
230 		}
231 
232 		_videoHistoryBuilder.push_back(vector<uint32_t>((uint32_t*)frameBuffer, (uint32_t*)frameBuffer + width*height));
233 
234 		if(_videoHistoryBuilder.size() == (size_t)_historyBackup.front().FrameCount) {
235 			for(int i = (int)_videoHistoryBuilder.size() - 1; i >= 0; i--) {
236 				_videoHistory.push_front(_videoHistoryBuilder[i]);
237 			}
238 			_videoHistoryBuilder.clear();
239 		}
240 
241 		if(_rewindState == RewindState::Started || _videoHistory.size() >= RewindManager::BufferSize) {
242 			_rewindState = RewindState::Started;
243 			_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
244 			if(!_videoHistory.empty()) {
245 				_console->GetVideoRenderer()->UpdateFrame(_videoHistory.back().data(), width, height);
246 				_videoHistory.pop_back();
247 			}
248 		}
249 	} else if(_rewindState == RewindState::Stopping || _rewindState == RewindState::Debugging) {
250 		//Display nothing while resyncing
251 	} else {
252 		_console->GetVideoRenderer()->UpdateFrame(frameBuffer, width, height);
253 	}
254 }
255 
ProcessAudio(int16_t * soundBuffer,uint32_t sampleCount,uint32_t sampleRate)256 bool RewindManager::ProcessAudio(int16_t * soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
257 {
258 	if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
259 		_audioHistoryBuilder.insert(_audioHistoryBuilder.end(), soundBuffer, soundBuffer + sampleCount * 2);
260 
261 		if(_rewindState == RewindState::Started && _audioHistory.size() > sampleCount * 2) {
262 			for(uint32_t i = 0; i < sampleCount * 2; i++) {
263 				soundBuffer[i] = _audioHistory.back();
264 				_audioHistory.pop_back();
265 			}
266 
267 			return true;
268 		} else {
269 			//Mute while we prepare to rewind
270 			return false;
271 		}
272 	} else if(_rewindState == RewindState::Stopping || _rewindState == RewindState::Debugging) {
273 		//Mute while we resync
274 		return false;
275 	} else {
276 		return true;
277 	}
278 }
279 
RecordInput(vector<shared_ptr<BaseControlDevice>> devices)280 void RewindManager::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
281 {
282 	if(_settings->GetRewindBufferSize() > 0 && _rewindState == RewindState::Stopped) {
283 		for(shared_ptr<BaseControlDevice> &device : devices) {
284 			_currentHistory.InputLogs[device->GetPort()].push_back(device->GetRawState());
285 		}
286 	}
287 }
288 
SetInput(BaseControlDevice * device)289 bool RewindManager::SetInput(BaseControlDevice *device)
290 {
291 	uint8_t port = device->GetPort();
292 	if(!_currentHistory.InputLogs[port].empty() && IsRewinding()) {
293 		ControlDeviceState state = _currentHistory.InputLogs[port].front();
294 		_currentHistory.InputLogs[port].pop_front();
295 		device->SetRawState(state);
296 		return true;
297 	} else {
298 		return false;
299 	}
300 }
301 
StartRewinding(bool forDebugger)302 void RewindManager::StartRewinding(bool forDebugger)
303 {
304 	Start(forDebugger);
305 }
306 
StopRewinding(bool forDebugger)307 void RewindManager::StopRewinding(bool forDebugger)
308 {
309 	if(forDebugger) {
310 		ForceStop();
311 	} else {
312 		Stop();
313 	}
314 }
315 
IsRewinding()316 bool RewindManager::IsRewinding()
317 {
318 	return _rewindState != RewindState::Stopped;
319 }
320 
IsStepBack()321 bool RewindManager::IsStepBack()
322 {
323 	return _rewindState == RewindState::Debugging;
324 }
325 
RewindSeconds(uint32_t seconds)326 void RewindManager::RewindSeconds(uint32_t seconds)
327 {
328 	if(_rewindState == RewindState::Stopped) {
329 		uint32_t removeCount = (seconds * 60 / RewindManager::BufferSize) + 1;
330 		_console->Pause();
331 		for(uint32_t i = 0; i < removeCount; i++) {
332 			if(!_history.empty()) {
333 				_currentHistory = _history.back();
334 				_history.pop_back();
335 			} else {
336 				break;
337 			}
338 		}
339 		_currentHistory.LoadState(_console);
340 		_console->Resume();
341 	}
342 }
343 
HasHistory()344 bool RewindManager::HasHistory()
345 {
346 	return _hasHistory;
347 }
348 
CopyHistory(shared_ptr<HistoryViewer> destHistoryViewer)349 void RewindManager::CopyHistory(shared_ptr<HistoryViewer> destHistoryViewer)
350 {
351 	destHistoryViewer->SetHistoryData(_history);
352 }
353 
SendFrame(void * frameBuffer,uint32_t width,uint32_t height,bool forRewind)354 void RewindManager::SendFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind)
355 {
356 	ProcessFrame(frameBuffer, width, height, forRewind);
357 }
358 
SendAudio(int16_t * soundBuffer,uint32_t sampleCount,uint32_t sampleRate)359 bool RewindManager::SendAudio(int16_t * soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
360 {
361 	return ProcessAudio(soundBuffer, sampleCount, sampleRate);
362 }
363