1 ////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // Nestopia - NES/Famicom emulator written in C++
4 //
5 // Copyright (C) 2003-2008 Martin Freij
6 //
7 // This file is part of Nestopia.
8 //
9 // Nestopia is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Nestopia is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Nestopia; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 //
23 ////////////////////////////////////////////////////////////////////////////////////////
24 
25 #include <new>
26 #include "NstMachine.hpp"
27 #include "NstTrackerMovie.hpp"
28 #include "NstTrackerRewinder.hpp"
29 #include "NstImage.hpp"
30 #include "api/NstApiMachine.hpp"
31 
32 namespace Nes
33 {
34 	namespace Core
35 	{
36 		#ifdef NST_MSVC_OPTIMIZE
37 		#pragma optimize("s", on)
38 		#endif
39 
Tracker()40 		Tracker::Tracker()
41 		:
42 		frame           (0),
43 		rewinderSound   (false),
44 		rewinderEnabled (NULL),
45 		rewinder        (NULL),
46 		movie           (NULL)
47 		{}
48 
~Tracker()49 		Tracker::~Tracker()
50 		{
51 			delete rewinder;
52 			delete movie;
53 		}
54 
Unload()55 		void Tracker::Unload()
56 		{
57 			frame = 0;
58 
59 			if (rewinder)
60 				rewinder->Unload();
61 			else
62 				StopMovie();
63 		}
64 
Reset()65 		void Tracker::Reset()
66 		{
67 			frame = 0;
68 
69 			if (rewinder)
70 			{
71 				rewinder->Reset();
72 			}
73 			else if (movie)
74 			{
75 				movie->Reset();
76 			}
77 		}
78 
PowerOff()79 		void Tracker::PowerOff()
80 		{
81 			StopMovie();
82 		}
83 
Resync(bool excludeFrame) const84 		void Tracker::Resync(bool excludeFrame) const
85 		{
86 			if (rewinder)
87 			{
88 				rewinder->Reset();
89 			}
90 			else if (movie && !excludeFrame)
91 			{
92 				movie->Resync();
93 			}
94 		}
95 
TryResync(Result lastResult,bool excludeFrame) const96 		Result Tracker::TryResync(Result lastResult,bool excludeFrame) const
97 		{
98 			NST_VERIFY( NES_SUCCEEDED(lastResult) );
99 
100 			if (NES_SUCCEEDED(lastResult) && lastResult != RESULT_NOP)
101 				Resync( excludeFrame );
102 
103 			return lastResult;
104 		}
105 
EnableRewinder(Machine * const emulator)106 		Result Tracker::EnableRewinder(Machine* const emulator)
107 		{
108 			if (rewinderEnabled == emulator)
109 				return RESULT_NOP;
110 
111 			rewinderEnabled = emulator;
112 			UpdateRewinderState( true );
113 
114 			return RESULT_OK;
115 		}
116 
EnableRewinderSound(bool enable)117 		void Tracker::EnableRewinderSound(bool enable)
118 		{
119 			rewinderSound = enable;
120 
121 			if (rewinder)
122 				rewinder->EnableSound( enable );
123 		}
124 
ResetRewinder() const125 		void Tracker::ResetRewinder() const
126 		{
127 			if (rewinder)
128 				rewinder->Reset();
129 		}
130 
UpdateRewinderState(bool enable)131 		void Tracker::UpdateRewinderState(bool enable)
132 		{
133 			if (enable && rewinderEnabled && !movie)
134 			{
135 				if (!rewinder)
136 				{
137 					rewinder = new Rewinder
138 					(
139 						*rewinderEnabled,
140 						&Machine::Execute,
141 						&Machine::LoadState,
142 						&Machine::SaveState,
143 						rewinderEnabled->cpu,
144 						rewinderEnabled->cpu.GetApu(),
145 						rewinderEnabled->ppu,
146 						rewinderSound
147 					);
148 				}
149 			}
150 			else
151 			{
152 				delete rewinder;
153 				rewinder = NULL;
154 			}
155 		}
156 
PlayMovie(Machine & emulator,std::istream & stream)157 		Result Tracker::PlayMovie(Machine& emulator,std::istream& stream)
158 		{
159 			if (!emulator.Is(Api::Machine::GAME))
160 				return RESULT_ERR_NOT_READY;
161 
162 			UpdateRewinderState( false );
163 
164 			Result result;
165 
166 			try
167 			{
168 				if (movie == NULL)
169 				{
170 					movie = new Movie
171 					(
172 						emulator,
173 						&Machine::LoadState,
174 						&Machine::SaveState,
175 						emulator.cpu,
176 						emulator.Is(Api::Machine::CARTRIDGE) ? emulator.image->GetPrgCrc() : 0
177 					);
178 				}
179 
180 				if (movie->Play( stream ))
181 				{
182 					if (emulator.Is(Api::Machine::ON))
183 						emulator.Reset( true );
184 
185 					return RESULT_OK;
186 				}
187 				else
188 				{
189 					return RESULT_NOP;
190 				}
191 			}
192 			catch (Result r)
193 			{
194 				result = r;
195 			}
196 			catch (const std::bad_alloc&)
197 			{
198 				result = RESULT_ERR_OUT_OF_MEMORY;
199 			}
200 			catch (...)
201 			{
202 				result = RESULT_ERR_GENERIC;
203 			}
204 
205 			StopMovie();
206 
207 			return result;
208 		}
209 
RecordMovie(Machine & emulator,std::iostream & stream,const bool append)210 		Result Tracker::RecordMovie(Machine& emulator,std::iostream& stream,const bool append)
211 		{
212 			if (!emulator.Is(Api::Machine::GAME))
213 				return RESULT_ERR_NOT_READY;
214 
215 			UpdateRewinderState( false );
216 
217 			Result result;
218 
219 			try
220 			{
221 				if (movie == NULL)
222 				{
223 					movie = new Movie
224 					(
225 						emulator,
226 						&Machine::LoadState,
227 						&Machine::SaveState,
228 						emulator.cpu,
229 						emulator.image->GetPrgCrc()
230 					);
231 				}
232 
233 				return movie->Record( stream, append ) ? RESULT_OK : RESULT_NOP;
234 			}
235 			catch (Result r)
236 			{
237 				result = r;
238 			}
239 			catch (const std::bad_alloc&)
240 			{
241 				result = RESULT_ERR_OUT_OF_MEMORY;
242 			}
243 			catch (...)
244 			{
245 				result = RESULT_ERR_GENERIC;
246 			}
247 
248 			StopMovie();
249 
250 			return result;
251 		}
252 
StopMovie()253 		void Tracker::StopMovie()
254 		{
255 			delete movie;
256 			movie = NULL;
257 
258 			UpdateRewinderState( true );
259 		}
260 
261 		#ifdef NST_MSVC_OPTIMIZE
262 		#pragma optimize("", on)
263 		#endif
264 
StartRewinding() const265 		Result Tracker::StartRewinding() const
266 		{
267 			return rewinder ? rewinder->Start() : RESULT_ERR_NOT_READY;
268 		}
269 
StopRewinding() const270 		Result Tracker::StopRewinding() const
271 		{
272 			return rewinder ? rewinder->Stop() : RESULT_NOP;
273 		}
274 
IsRewinding() const275 		bool Tracker::IsRewinding() const
276 		{
277 			return rewinder && rewinder->IsRewinding();
278 		}
279 
IsMoviePlaying() const280 		bool Tracker::IsMoviePlaying() const
281 		{
282 			return movie && movie->IsPlaying();
283 		}
284 
IsMovieRecording() const285 		bool Tracker::IsMovieRecording() const
286 		{
287 			return movie && movie->IsRecording();
288 		}
289 
IsLocked(bool excludeFrame) const290 		bool Tracker::IsLocked(bool excludeFrame) const
291 		{
292 			return IsRewinding() || (!excludeFrame && IsMoviePlaying());
293 		}
294 
IsActive() const295 		bool Tracker::IsActive() const
296 		{
297 			return IsRewinding() || movie;
298 		}
299 
Execute(Machine & machine,Video::Output * const video,Sound::Output * const sound,Input::Controllers * input)300 		Result Tracker::Execute
301 		(
302 			Machine& machine,
303 			Video::Output* const video,
304 			Sound::Output* const sound,
305 			Input::Controllers* input
306 		)
307 		{
308 			if (machine.Is(Api::Machine::ON))
309 			{
310 				++frame;
311 
312 				try
313 				{
314 					if (machine.Is(Api::Machine::GAME))
315 					{
316 						if (rewinder)
317 						{
318 							rewinder->Execute( video, sound, input );
319 							return RESULT_OK;
320 						}
321 						else if (movie)
322 						{
323 							if (!movie->Execute())
324 							{
325 								StopMovie();
326 							}
327 							else if (movie->IsPlaying())
328 							{
329 								input = NULL;
330 							}
331 						}
332 					}
333 
334 					machine.Execute( video, sound, input );
335 					return RESULT_OK;
336 				}
337 				catch (Result result)
338 				{
339 					return machine.PowerOff( result );
340 				}
341 				catch (const std::bad_alloc&)
342 				{
343 					return machine.PowerOff( RESULT_ERR_OUT_OF_MEMORY );
344 				}
345 				catch (...)
346 				{
347 					return machine.PowerOff( RESULT_ERR_GENERIC );
348 				}
349 			}
350 			else
351 			{
352 				return RESULT_ERR_NOT_READY;
353 			}
354 		}
355 	}
356 }
357