Actually call on_reset callback
[lsnes.git] / src / core / moviedata.cpp
blobdc99b5a20eb263b2e6de2bc4b6f12d08cce0b785
1 #include "lsnes.hpp"
3 #include "cmdhelp/moviedata.hpp"
4 #include "core/advdumper.hpp"
5 #include "core/command.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/framebuffer.hpp"
8 #include "core/framerate.hpp"
9 #include "core/instance.hpp"
10 #include "core/jukebox.hpp"
11 #include "core/mainloop.hpp"
12 #include "core/messages.hpp"
13 #include "core/moviedata.hpp"
14 #include "core/project.hpp"
15 #include "core/random.hpp"
16 #include "core/rom.hpp"
17 #include "core/runmode.hpp"
18 #include "core/settings.hpp"
19 #include "interface/romtype.hpp"
20 #include "library/directory.hpp"
21 #include "library/minmax.hpp"
22 #include "library/string.hpp"
23 #include "library/temporary_handle.hpp"
24 #include "lua/lua.hpp"
26 #include <iomanip>
27 #include <fstream>
29 std::string last_save;
31 namespace
33 settingvar::supervariable<settingvar::model_int<0, 9>> SET_savecompression(lsnes_setgrp, "savecompression",
34 "Movie‣Saving‣Compression", 7);
35 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> SET_readonly_load_preserves(
36 lsnes_setgrp, "preserve_on_readonly_load", "Movie‣Loading‣Preserve on readonly load", true);
37 threads::lock mprefix_lock;
38 std::string mprefix;
39 bool mprefix_valid;
41 std::string get_mprefix()
43 threads::alock h(mprefix_lock);
44 if(!mprefix_valid)
45 return "movieslot";
46 else
47 return mprefix + "-";
50 command::fnptr<const std::string&> test4(lsnes_cmds, CMOVIEDATA::panic,
51 [](const std::string& args) {
52 auto& core = CORE();
53 if(*core.mlogic) emerg_save_movie(core.mlogic->get_mfile(), core.mlogic->get_rrdata());
54 });
56 command::fnptr<const std::string&> CMD_dump_coresave(lsnes_cmds, CMOVIEDATA::dumpcore,
57 [](const std::string& name) {
58 auto& core = CORE();
59 auto x = core.rom->save_core_state();
60 x.resize(x.size() - 32);
61 std::ofstream y(name.c_str(), std::ios::out | std::ios::binary);
62 y.write(&x[0], x.size());
63 y.close();
64 messages << "Saved core state to " << name << std::endl;
65 });
67 bool warn_hash_mismatch(const std::string& mhash, const fileimage::image& slot,
68 const std::string& name, bool fatal)
70 if(mhash == slot.sha_256.read())
71 return true;
72 if(!fatal) {
73 messages << "WARNING: " << name << " hash mismatch!" << std::endl
74 << "\tMovie: " << mhash << std::endl
75 << "\tOur ROM: " << slot.sha_256.read() << std::endl;
76 return true;
77 } else {
78 platform::error_message("Can't load state because hashes mismatch");
79 messages << "ERROR: " << name << " hash mismatch!" << std::endl
80 << "\tMovie: " << mhash << std::endl
81 << "\tOur ROM: " << slot.sha_256.read() << std::endl;
82 return false;
86 void set_mprefix(const std::string& pfx)
88 auto& core = CORE();
90 threads::alock h(mprefix_lock);
91 mprefix_valid = (pfx != "");
92 mprefix = pfx;
94 core.supdater->update();
97 std::string get_mprefix_for_project(const std::string& prjid)
99 std::string filename = get_config_path() + "/" + safe_filename(prjid) + ".pfx";
100 std::ifstream strm(filename);
101 if(!strm)
102 return "";
103 std::string pfx;
104 std::getline(strm, pfx);
105 return strip_CR(pfx);
108 void set_gameinfo(moviefile& mfile)
110 master_dumper::gameinfo gi;
111 gi.gamename = mfile.gamename;
112 gi.length = mfile.get_movie_length() / 1000.0;
113 gi.rerecords = mfile.rerecords;
114 gi.authors = mfile.authors;
115 CORE().mdumper->on_gameinfo_change(gi);
118 class _lsnes_pflag_handler : public movie::poll_flag
120 public:
121 ~_lsnes_pflag_handler()
124 int get_pflag()
126 return CORE().rom->get_pflag();
128 void set_pflag(int flag)
130 CORE().rom->set_pflag(flag);
132 } lsnes_pflag_handler;
135 std::string get_mprefix_for_project()
137 auto& core = CORE();
138 return get_mprefix_for_project(*core.mlogic ? core.mlogic->get_mfile().projectid : "");
141 void set_mprefix_for_project(const std::string& prjid, const std::string& pfx)
143 std::string filename = get_config_path() + "/" + safe_filename(prjid) + ".pfx";
144 std::ofstream strm(filename);
145 strm << pfx << std::endl;
148 void set_mprefix_for_project(const std::string& pfx)
150 auto& core = CORE();
151 set_mprefix_for_project(*core.mlogic ? core.mlogic->get_mfile().projectid : "", pfx);
152 set_mprefix(pfx);
155 std::string translate_name_mprefix(std::string original, int& binary, int save)
157 auto& core = CORE();
158 auto p = core.project->get();
159 regex_results r = regex("\\$SLOT:(.*)", original);
160 if(r) {
161 if(binary < 0)
162 binary = core.jukebox->save_binary() ? 1 : 0;
163 if(p) {
164 uint64_t branch = p->get_current_branch();
165 std::string branch_str;
166 std::string filename;
167 if(branch) branch_str = (stringfmt() << "--" << branch).str();
168 filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
169 while(save < 0 && branch) {
170 if(zip::file_exists(filename))
171 break;
172 branch = p->get_parent_branch(branch);
173 branch_str = branch ? ((stringfmt() << "--" << branch).str()) : "";
174 filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
176 return filename;
177 } else {
178 std::string pprf = SET_slotpath(*core.settings) + "/";
179 return pprf + get_mprefix() + r[1] + ".lsmv";
181 } else {
182 if(binary < 0)
183 binary = (save ? save_dflt_binary(*core.settings) :
184 movie_dflt_binary(*core.settings)) ? 1 : 0;
185 return original;
189 std::pair<std::string, std::string> split_author(const std::string& author)
191 std::string _author = author;
192 std::string fullname;
193 std::string nickname;
194 size_t split = _author.find_first_of("|");
195 if(split >= _author.length()) {
196 fullname = _author;
197 } else {
198 fullname = _author.substr(0, split);
199 nickname = _author.substr(split + 1);
201 if(fullname == "" && nickname == "")
202 throw std::runtime_error("Bad author name");
203 return std::make_pair(fullname, nickname);
206 //Resolve relative path.
207 std::string resolve_relative_path(const std::string& path)
209 try {
210 return directory::absolute_path(path);
211 } catch(...) {
212 return path;
216 //Save state.
217 void do_save_state(const std::string& filename, int binary)
219 auto& core = CORE();
220 if(!*core.mlogic || !core.mlogic->get_mfile().gametype) {
221 platform::error_message("Can't save movie without a ROM");
222 messages << "Can't save movie without a ROM" << std::endl;
223 return;
225 auto& target = core.mlogic->get_mfile();
226 std::string filename2 = translate_name_mprefix(filename, binary, 1);
227 core.lua2->callback_pre_save(filename2, true);
228 try {
229 uint64_t origtime = framerate_regulator::get_utime();
230 target.dyn.sram = core.rom->save_sram();
231 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
232 auto& img = core.rom->get_rom(i);
233 auto& xml = core.rom->get_markup(i);
234 target.romimg_sha256[i] = img.sha_256.read();
235 target.romxml_sha256[i] = xml.sha_256.read();
236 target.namehint[i] = img.namehint;
238 target.dyn.savestate = core.rom->save_core_state();
239 core.fbuf->get_framebuffer().save(target.dyn.screenshot);
240 core.mlogic->get_movie().save_state(target.projectid, target.dyn.save_frame,
241 target.dyn.lagged_frames, target.dyn.pollcounters);
242 target.dyn.poll_flag = core.rom->get_pflag();
243 auto prj = core.project->get();
244 if(prj) {
245 target.gamename = prj->gamename;
246 target.authors = prj->authors;
248 target.dyn.active_macros = core.controls->get_macro_frames();
249 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
250 core.mlogic->get_rrdata(), true);
251 uint64_t took = framerate_regulator::get_utime() - origtime;
252 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
253 messages << "Saved state " << kind << " '" << filename2 << "' in " << took << " microseconds."
254 << std::endl;
255 core.lua2->callback_post_save(filename2, true);
256 } catch(std::bad_alloc& e) {
257 throw;
258 } catch(std::exception& e) {
259 platform::error_message(std::string("Save failed: ") + e.what());
260 messages << "Save failed: " << e.what() << std::endl;
261 core.lua2->callback_err_save(filename2);
263 last_save = resolve_relative_path(filename2);
264 auto p = core.project->get();
265 if(p) {
266 p->last_save = last_save;
267 p->flush();
271 //Save movie.
272 void do_save_movie(const std::string& filename, int binary)
274 auto& core = CORE();
275 if(!*core.mlogic || !core.mlogic->get_mfile().gametype) {
276 platform::error_message("Can't save movie without a ROM");
277 messages << "Can't save movie without a ROM" << std::endl;
278 return;
280 auto& target = core.mlogic->get_mfile();
281 std::string filename2 = translate_name_mprefix(filename, binary, 0);
282 core.lua2->callback_pre_save(filename2, false);
283 try {
284 uint64_t origtime = framerate_regulator::get_utime();
285 auto prj = core.project->get();
286 if(prj) {
287 target.gamename = prj->gamename;
288 target.authors = prj->authors;
290 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
291 core.mlogic->get_rrdata(), false);
292 uint64_t took = framerate_regulator::get_utime() - origtime;
293 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
294 messages << "Saved movie " << kind << " '" << filename2 << "' in " << took << " microseconds."
295 << std::endl;
296 core.lua2->callback_post_save(filename2, false);
297 } catch(std::bad_alloc& e) {
298 OOM_panic();
299 } catch(std::exception& e) {
300 platform::error_message(std::string("Save failed: ") + e.what());
301 messages << "Save failed: " << e.what() << std::endl;
302 core.lua2->callback_err_save(filename2);
304 last_save = resolve_relative_path(filename2);
305 auto p = core.project->get();
306 if(p) {
307 p->last_save = last_save;
308 p->flush();
312 namespace
314 void populate_volatile_ram(moviefile& mf, std::list<core_vma_info>& vmas)
316 for(auto i : vmas) {
317 //Only regions that are marked as volatile, readwrite not special are initializable.
318 if(!i.volatile_flag || i.readonly || i.special)
319 continue;
320 if(!mf.ramcontent.count(i.name))
321 continue;
322 uint64_t csize = min((uint64_t)mf.ramcontent[i.name].size(), i.size);
323 if(i.backing_ram)
324 memcpy(i.backing_ram, &mf.ramcontent[i.name][0], csize);
325 else
326 for(uint64_t o = 0; o < csize; o++)
327 i.write(o, mf.ramcontent[i.name][o]);
331 std::string format_length(uint64_t mlength)
333 std::ostringstream x;
334 if(mlength >= 3600000ULL) {
335 x << mlength / 3600000ULL << ":";
336 mlength %= 3600000ULL;
338 x << std::setfill('0') << std::setw(2) << mlength / 60000ULL << ":";
339 mlength %= 60000ULL;
340 x << std::setfill('0') << std::setw(2) << mlength / 1000ULL << ".";
341 mlength %= 1000ULL;
342 x << std::setfill('0') << std::setw(3) << mlength;
343 return x.str();
346 void print_movie_info(moviefile& mov, loaded_rom& rom, rrdata_set& rrd)
348 if(!rom.isnull())
349 messages << "ROM Type " << rom.get_hname() << " region " << rom.region_get_hname()
350 << std::endl;
351 std::string len, rerecs;
352 len = format_length(mov.get_movie_length());
353 std::string rerecords = mov.dyn.save_frame ? (stringfmt() << rrd.count()).str() : mov.rerecords;
354 messages << "Rerecords " << rerecords << " length " << format_length(mov.get_movie_length())
355 << " (" << mov.get_frame_count() << " frames)" << std::endl;
356 if(mov.gamename != "")
357 messages << "Game name: " << mov.gamename << std::endl;
358 for(size_t i = 0; i < mov.authors.size(); i++)
359 if(mov.authors[i].second == "")
360 messages << "Author: " << mov.authors[i].first << std::endl;
361 else if(mov.authors[i].first == "")
362 messages << "Author: (" << mov.authors[i].second << ")" << std::endl;
363 else
364 messages << "Author: " << mov.authors[i].first << " ("
365 << mov.authors[i].second << ")" << std::endl;
368 void warn_coretype(const std::string& mov_core, loaded_rom& against, bool loadstate)
370 if(against.isnull())
371 return;
372 std::string rom_core = against.get_core_identifier();
373 if(mov_core == rom_core)
374 return;
375 std::ostringstream x;
376 x << (loadstate ? "Error: " : "Warning: ");
377 x << "Emulator core version mismatch!" << std::endl
378 << "\tCrurrent: " << rom_core << std::endl
379 << "\tMovie: " << mov_core << std::endl;
380 if(loadstate)
381 throw std::runtime_error(x.str());
382 else
383 messages << x.str();
386 void warn_roms(moviefile& mov, loaded_rom& against, bool loadstate)
388 bool rom_ok = true;
389 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
390 auto& img = against.get_rom(i);
391 auto& xml = against.get_markup(i);
392 if(mov.namehint[i] == "")
393 mov.namehint[i] = img.namehint;
394 if(mov.romimg_sha256[i] == "")
395 mov.romimg_sha256[i] = img.sha_256.read();
396 if(mov.romxml_sha256[i] == "")
397 mov.romxml_sha256[i] = xml.sha_256.read();
398 rom_ok = rom_ok & warn_hash_mismatch(mov.romimg_sha256[i], img,
399 (stringfmt() << "ROM #" << (i + 1)).str(), loadstate);
400 rom_ok = rom_ok & warn_hash_mismatch(mov.romxml_sha256[i], xml,
401 (stringfmt() << "XML #" << (i + 1)).str(), loadstate);
403 if(!rom_ok)
404 throw std::runtime_error("Incorrect ROM");
407 portctrl::type_set& construct_movie_portset(moviefile& mov, loaded_rom& against)
409 auto ctrldata = against.controllerconfig(mov.settings);
410 return portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
413 void handle_load_core(moviefile& _movie, portctrl::type_set& portset, bool will_load_state,
414 bool force = false);
416 void handle_load_core(moviefile& _movie, portctrl::type_set& portset, bool will_load_state, bool force)
418 auto& core = CORE();
419 core.random_seed_value = _movie.movie_rtc_second;
420 if(will_load_state) {
421 //If settings possibly change, reload the ROM.
422 if(force || !*core.mlogic || core.mlogic->get_mfile().projectid != _movie.projectid)
423 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
424 //Load the savestate and movie state.
425 //Set the core ports in order to avoid port state being reinitialized when loading.
426 core.controls->set_ports(portset);
427 core.rom->load_core_state(_movie.dyn.savestate);
428 core.rom->set_pflag(_movie.dyn.poll_flag);
429 core.controls->set_macro_frames(_movie.dyn.active_macros);
430 } else {
431 //If settings possibly change, reload the ROM. Otherwise rewind to beginning.
432 if(force || !*core.mlogic || core.mlogic->get_mfile().projectid != _movie.projectid)
433 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
434 else
435 core.rom->reset_to_load();
436 //Load the SRAM and volatile RAM. Or anchor savestate if any.
437 core.controls->set_ports(portset);
438 _movie.dyn.rtc_second = _movie.movie_rtc_second;
439 _movie.dyn.rtc_subsecond = _movie.movie_rtc_subsecond;
440 if(!_movie.anchor_savestate.empty()) {
441 core.rom->load_core_state(_movie.anchor_savestate);
442 } else {
443 core.rom->load_sram(_movie.movie_sram);
444 std::list<core_vma_info> vmas = core.rom->vma_list();
445 populate_volatile_ram(_movie, vmas);
447 core.rom->set_pflag(0);
448 core.controls->set_macro_frames(std::map<std::string, uint64_t>());
453 void do_load_rom()
455 auto& core = CORE();
456 bool load_readwrite = !*core.mlogic || !core.mlogic->get_movie().readonly_mode();
457 if(*core.mlogic) {
458 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
459 //If portset or gametype changes, force readwrite with new movie.
460 if(core.mlogic->get_mfile().input->get_types() != portset) load_readwrite = true;
461 else if(!core.rom->is_of_type(core.mlogic->get_mfile().gametype->get_type())) load_readwrite = true;
462 else if(!core.rom->region_compatible_with(core.mlogic->get_mfile().gametype->get_region()))
463 load_readwrite = true;
466 if(!load_readwrite) {
467 //Read-only load. This is pretty simple.
468 //Force unlazying of rrdata and count a rerecord.
469 if(core.mlogic->get_rrdata().is_lazy())
470 core.mlogic->get_rrdata().read_base(rrdata::filename(
471 core.mlogic->get_mfile().projectid), false);
472 core.mlogic->get_rrdata().add((*core.nrrdata)());
474 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
476 try {
477 //Force game's region to run's region. We already checked this is possible above.
478 core.rom->set_internal_region(core.mlogic->get_mfile().gametype->get_region());
480 handle_load_core(core.mlogic->get_mfile(), portset, false, true);
481 core.mlogic->get_mfile().gametype = &core.rom->get_sysregion();
482 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
483 auto& img = core.rom->get_rom(i);
484 auto& xml = core.rom->get_markup(i);
485 core.mlogic->get_mfile().namehint[i] = img.namehint;
486 core.mlogic->get_mfile().romimg_sha256[i] = img.sha_256.read();
487 core.mlogic->get_mfile().romxml_sha256[i] = xml.sha_256.read();
489 core.mlogic->get_mfile().clear_dynstate();
490 core.mlogic->get_movie().reset_state();
491 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
492 core.lua2->callback_do_rewind();
493 } catch(std::bad_alloc& e) {
494 OOM_panic();
495 } catch(std::exception& e) {
496 core.runmode->set_corrupt();
497 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
498 throw;
500 } else {
501 //The more complicated Read-Write case.
502 //We need to create a new movie and movie file.
503 temporary_handle<moviefile> _movie;
504 _movie.get()->force_corrupt = false;
505 _movie.get()->gametype = NULL; //Not yet known.
506 _movie.get()->coreversion = core.rom->get_core_identifier();
507 _movie.get()->projectid = get_random_hexstring(40);
508 _movie.get()->rerecords = "0";
509 _movie.get()->rerecords_mem = 0;
510 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
511 auto& img = core.rom->get_rom(i);
512 auto& xml = core.rom->get_markup(i);
513 _movie.get()->namehint[i] = img.namehint;
514 _movie.get()->romimg_sha256[i] = img.sha_256.read();
515 _movie.get()->romxml_sha256[i] = xml.sha_256.read();
517 _movie.get()->movie_rtc_second = _movie.get()->dyn.rtc_second = 1000000000ULL;
518 _movie.get()->movie_rtc_subsecond = _movie.get()->dyn.rtc_subsecond = 0;
519 _movie.get()->start_paused = false;
520 _movie.get()->lazy_project_create = true;
521 portctrl::type_set& portset2 = construct_movie_portset(*_movie.get(), *core.rom);
522 _movie.get()->input = NULL;
523 _movie.get()->create_default_branch(portset2);
525 //Wrap the input in movie.
526 temporary_handle<movie> newmovie;
527 newmovie.get()->set_movie_data(_movie.get()->input);
528 newmovie.get()->load(_movie.get()->rerecords, _movie.get()->projectid, *(_movie.get()->input));
529 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
530 newmovie.get()->readonly_mode(false);
532 //Force new lazy rrdata and count a rerecord.
533 temporary_handle<rrdata_set> rrd;
534 rrd.get()->read_base(rrdata::filename(_movie.get()->projectid), true);
535 rrd.get()->add((*core.nrrdata)());
536 //Movie data is lost.
537 core.lua2->callback_movie_lost("reload");
538 try {
539 handle_load_core(*_movie.get(), portset2, false, true);
540 _movie.get()->gametype = &core.rom->get_sysregion();
541 } catch(std::bad_alloc& e) {
542 OOM_panic();
543 } catch(std::exception& e) {
544 core.runmode->set_corrupt();
545 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
546 throw;
549 //Set up stuff.
550 core.mlogic->set_movie(*(newmovie()), true);
551 core.mlogic->set_mfile(*(_movie()), true);
552 core.mlogic->set_rrdata(*(rrd()), true);
553 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
554 set_gameinfo(core.mlogic->get_mfile());
556 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
557 core.lua2->callback_do_rewind();
559 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
560 core.dispatch->mbranch_change();
561 messages << "ROM reloaded." << std::endl;
564 void do_load_rewind()
566 auto& core = CORE();
567 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
568 throw std::runtime_error("Can't rewind movie without existing movie");
570 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
572 //Force unlazying of rrdata and count a rerecord.
573 if(core.mlogic->get_rrdata().is_lazy())
574 core.mlogic->get_rrdata().read_base(rrdata::filename(
575 core.mlogic->get_mfile().projectid), false);
576 core.mlogic->get_rrdata().add((*core.nrrdata)());
578 //Enter readonly mode.
579 core.mlogic->get_movie().readonly_mode(true);
580 core.dispatch->mode_change(true);
581 try {
582 handle_load_core(core.mlogic->get_mfile(), portset, false);
583 core.mlogic->get_mfile().clear_dynstate();
584 core.mlogic->get_movie().reset_state();
585 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
586 core.lua2->callback_do_rewind();
587 } catch(std::bad_alloc& e) {
588 OOM_panic();
589 } catch(std::exception& e) {
590 core.runmode->set_corrupt();
591 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
592 throw;
594 messages << "Movie rewound to beginning." << std::endl;
597 //Load state preserving input. Does not do checks.
598 void do_load_state_preserve(struct moviefile& _movie)
600 auto& core = CORE();
601 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
602 throw std::runtime_error("Can't load movie preserving input without previous movie");
603 if(core.mlogic->get_mfile().projectid != _movie.projectid)
604 throw std::runtime_error("Savestate is from different movie");
606 bool will_load_state = _movie.dyn.save_frame;
607 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
609 //Construct a new movie sharing the input data.
610 temporary_handle<movie> newmovie;
611 newmovie.get()->set_movie_data(core.mlogic->get_mfile().input);
612 newmovie.get()->readonly_mode(true);
613 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
615 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
616 if(will_load_state)
617 newmovie.get()->restore_state(_movie.dyn.save_frame, _movie.dyn.lagged_frames,
618 _movie.dyn.pollcounters, true, _movie.input, _movie.projectid);
620 //Count a rerecord.
621 if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
622 core.mlogic->get_rrdata().read_base(rrdata::filename(_movie.projectid), false);
623 core.mlogic->get_rrdata().add((*core.nrrdata)());
625 //Negative return.
626 try {
627 handle_load_core(_movie, portset, will_load_state);
628 } catch(std::bad_alloc& e) {
629 OOM_panic();
630 } catch(std::exception& e) {
631 core.runmode->set_corrupt();
632 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
633 throw;
636 //Set new movie.
637 core.mlogic->set_movie(*(newmovie()), true);
639 //If not loading a state, clear the state, so state swap will swap in state at beginning.
640 if(!will_load_state)
641 _movie.clear_dynstate();
642 //Swap the dynamic state to movie dynamic state (will desync otherwise).
643 core.mlogic->get_mfile().dyn.swap(_movie.dyn);
645 try {
646 //Paint the screen.
647 framebuffer::raw tmp;
648 if(will_load_state) {
649 tmp.load(core.mlogic->get_mfile().dyn.screenshot);
650 core.fbuf->redraw_framebuffer(tmp);
651 } else
652 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
653 } catch(...) {
655 delete &_movie;
656 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
657 messages << "Loadstated at earlier point of movie." << std::endl;
660 //Load state from loaded movie file. _movie is consumed.
661 void do_load_state(struct moviefile& _movie, int lmode, bool& used)
663 auto& core = CORE();
664 //Some basic sanity checks.
665 bool current_mode = *core.mlogic ? core.mlogic->get_movie().readonly_mode() : false;
666 bool will_load_state = _movie.dyn.save_frame && lmode != LOAD_STATE_MOVIE;
668 //Load state all branches and load state initial are the same.
669 if(lmode == LOAD_STATE_ALLBRANCH) lmode = LOAD_STATE_INITIAL;
671 //Various checks.
672 if(_movie.force_corrupt)
673 throw std::runtime_error("Movie file invalid");
674 if(!core.rom->is_of_type(_movie.gametype->get_type()))
675 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
676 if(!core.rom->region_compatible_with(_movie.gametype->get_region()))
677 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
678 warn_coretype(_movie.coreversion, *core.rom, will_load_state);
679 warn_roms(_movie, *core.rom, will_load_state);
681 //In certain conditions, trun LOAD_STATE_CURRENT into LOAD_STATE_PRESERVE.
682 if(lmode == LOAD_STATE_CURRENT && current_mode && SET_readonly_load_preserves(*core.settings))
683 lmode = LOAD_STATE_PRESERVE;
684 //If movie file changes, turn LOAD_STATE_CURRENT into LOAD_STATE_RO
685 if(lmode == LOAD_STATE_CURRENT && core.mlogic->get_mfile().projectid != _movie.projectid)
686 lmode = LOAD_STATE_RO;
688 //Handle preserving load specially.
689 if(lmode == LOAD_STATE_PRESERVE) {
690 do_load_state_preserve(_movie);
691 used = true;
692 return;
695 //Create a new movie, and if needed, restore the movie state.
696 temporary_handle<movie> newmovie;
697 newmovie.get()->set_movie_data(_movie.input);
698 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
699 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
700 if(will_load_state)
701 newmovie.get()->restore_state(_movie.dyn.save_frame, _movie.dyn.lagged_frames,
702 _movie.dyn.pollcounters, true, NULL, _movie.projectid);
704 //Copy the other branches.
705 if(lmode != LOAD_STATE_INITIAL && core.mlogic->get_mfile().projectid == _movie.projectid) {
706 newmovie.get()->set_movie_data(NULL);
707 auto& oldm = core.mlogic->get_mfile().branches;
708 auto& newm = _movie.branches;
709 auto oldd = core.mlogic->get_mfile().input;
710 auto newd = _movie.input;
711 std::string dflt_name;
712 //What was the old default name?
713 for(auto& i : oldm)
714 if(&i.second == oldd)
715 dflt_name = i.first;
716 //Rename the default to match with old movie if the names differ.
717 if(!newm.count(dflt_name) || &newm[dflt_name] != newd)
718 newm[dflt_name] = *newd;
719 //Copy all other branches.
720 for(auto& i : oldm)
721 if(i.first != dflt_name)
722 newm[i.first] = i.second;
723 //Delete branches that didn't exist.
724 for(auto i = newm.begin(); i != newm.end();) {
725 if(oldm.count(i->first))
726 i++;
727 else
728 i = newm.erase(i);
730 _movie.input = &newm[dflt_name];
731 newmovie.get()->set_movie_data(_movie.input);
734 portctrl::type_set& portset = construct_movie_portset(_movie, *core.rom);
736 temporary_handle<rrdata_set> rrd;
737 bool new_rrdata = false;
738 //Count a rerecord (against new or old movie).
739 if(!*core.mlogic || _movie.projectid != core.mlogic->get_mfile().projectid) {
740 rrd.get()->read_base(rrdata::filename(_movie.projectid), _movie.lazy_project_create);
741 rrd.get()->read(_movie.c_rrdata);
742 rrd.get()->add((*core.nrrdata)());
743 new_rrdata = true;
744 } else {
745 //Unlazy rrdata if needed.
746 if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
747 core.mlogic->get_rrdata().read_base(rrdata::filename(_movie.projectid), false);
748 core.mlogic->get_rrdata().add((*core.nrrdata)());
750 //Negative return.
751 try {
752 core.rom->set_internal_region(_movie.gametype->get_region());
753 handle_load_core(_movie, portset, will_load_state);
754 } catch(std::bad_alloc& e) {
755 OOM_panic();
756 } catch(std::exception& e) {
757 core.runmode->set_corrupt();
758 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
759 throw;
762 //If loaded a movie, clear the is savestate and rrdata.
763 if(!will_load_state)
764 _movie.clear_dynstate();
766 core.lua2->callback_movie_lost("load");
768 //Copy the data.
769 if(new_rrdata) core.mlogic->set_rrdata(*(rrd()), true);
770 core.mlogic->set_movie(*newmovie(), true);
771 core.mlogic->set_mfile(_movie, true);
772 used = true;
774 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
776 //Activate RW mode if needed.
777 auto& m = core.mlogic->get_movie();
778 if(lmode == LOAD_STATE_RW)
779 m.readonly_mode(false);
780 if(lmode == LOAD_STATE_DEFAULT && !current_mode && m.get_frame_count() <= m.get_current_frame())
781 m.readonly_mode(false);
782 if(lmode == LOAD_STATE_INITIAL && m.get_frame_count() <= m.get_current_frame())
783 m.readonly_mode(false);
784 if(lmode == LOAD_STATE_CURRENT && !current_mode)
785 m.readonly_mode(false);
787 //Paint the screen.
789 framebuffer::raw tmp;
790 if(will_load_state) {
791 tmp.load(_movie.dyn.screenshot);
792 core.fbuf->redraw_framebuffer(tmp);
793 } else
794 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
797 core.dispatch->mode_change(m.readonly_mode());
798 print_movie_info(_movie, *core.rom, core.mlogic->get_rrdata());
799 core.dispatch->mbranch_change();
800 set_gameinfo(core.mlogic->get_mfile());
804 void try_request_rom(const std::string& moviefile)
806 auto& core = CORE();
807 moviefile::brief_info info(moviefile);
808 auto sysregs = core_sysregion::find_matching(info.sysregion);
809 rom_request req;
810 req.selected = 0;
811 size_t idx = 0;
812 req.core_guessed = false;
813 for(auto i : sysregs) {
814 //FIXME: Do something with this?
815 //if(i->get_type().get_biosname() != "" && info.hash[1] != "")
816 // has_bios = true;
817 req.cores.push_back(&i->get_type());
818 if(i->get_type().get_core_identifier() == info.corename) {
819 req.selected = idx;
820 req.core_guessed = true;
822 idx++;
824 if(req.cores.empty())
825 throw std::runtime_error("No known core can load movie of type '" + info.sysregion + "'");
826 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
827 req.guessed[i] = false;
828 req.has_slot[i] = (info.hash[i] != "");
829 req.filename[i] = info.hint[i];
830 req.hash[i] = info.hash[i];
831 req.hashxml[i] = info.hashxml[i];
833 try_guess_roms(req);
834 req.canceled = true;
835 graphics_driver_request_rom(req);
836 if(req.canceled)
837 throw std::runtime_error("Canceled loading ROM");
838 //Try to load the ROM using specified core.
839 if(req.selected >= req.cores.size())
840 throw std::runtime_error("Invalid ROM type selected");
841 core_type* selected_core = req.cores[req.selected];
842 loaded_rom newrom(new rom_image(req.filename, selected_core->get_core_identifier(),
843 selected_core->get_iname(), ""));
844 *core.rom = newrom;
845 core.dispatch->core_change();
848 //Load state
849 bool do_load_state(const std::string& filename, int lmode)
851 auto& core = CORE();
852 int tmp = -1;
853 std::string filename2 = translate_name_mprefix(filename, tmp, -1);
854 uint64_t origtime = framerate_regulator::get_utime();
855 core.lua2->callback_pre_load(filename2);
856 struct moviefile* mfile = NULL;
857 bool used = false;
858 try {
859 if(core.rom->isnull())
860 try_request_rom(filename2);
861 mfile = new moviefile(filename2, core.rom->get_internal_rom_type());
862 } catch(std::bad_alloc& e) {
863 OOM_panic();
864 } catch(std::exception& e) {
865 platform::error_message(std::string("Can't read movie/savestate: ") + e.what());
866 messages << "Can't read movie/savestate '" << filename2 << "': " << e.what() << std::endl;
867 core.lua2->callback_err_load(filename2);
868 return false;
870 try {
871 do_load_state(*mfile, lmode, used);
872 uint64_t took = framerate_regulator::get_utime() - origtime;
873 messages << "Loaded '" << filename2 << "' in " << took << " microseconds." << std::endl;
874 core.lua2->callback_post_load(filename2, core.mlogic->get_mfile().dyn.save_frame);
875 } catch(std::bad_alloc& e) {
876 OOM_panic();
877 } catch(std::exception& e) {
878 if(!used)
879 delete mfile;
880 platform::error_message(std::string("Can't load movie/savestate: ") + e.what());
881 messages << "Can't load movie/savestate '" << filename2 << "': " << e.what() << std::endl;
882 core.lua2->callback_err_load(filename2);
883 return false;
885 return true;
888 void mainloop_restore_state(const dynamic_state& state)
890 auto& core = CORE();
891 //Force unlazy rrdata.
892 core.mlogic->get_rrdata().read_base(rrdata::filename(core.mlogic->get_mfile().projectid),
893 false);
894 core.mlogic->get_rrdata().add((*core.nrrdata)());
895 core.rom->load_core_state(state.savestate, true);
898 rrdata::rrdata()
899 : init(false)
903 rrdata_set::instance rrdata::operator()()
905 if(!init)
906 next = rrdata_set::instance(get_random_hexstring(2 * RRDATA_BYTES));
907 init = true;
908 return next++;
911 std::string rrdata::filename(const std::string& projectid)
913 return get_config_path() + "/" + projectid + ".rr";