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