lsnes rr2-β24
[lsnes.git] / src / core / moviedata.cpp
blob913dbcb965e0d6b9a626f1ca5b5b1c409606ffed
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) throw(std::bad_alloc, std::runtime_error) {
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) throw(std::bad_alloc, std::runtime_error) {
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) throw(std::bad_alloc,
190 std::runtime_error)
192 std::string _author = author;
193 std::string fullname;
194 std::string nickname;
195 size_t split = _author.find_first_of("|");
196 if(split >= _author.length()) {
197 fullname = _author;
198 } else {
199 fullname = _author.substr(0, split);
200 nickname = _author.substr(split + 1);
202 if(fullname == "" && nickname == "")
203 throw std::runtime_error("Bad author name");
204 return std::make_pair(fullname, nickname);
207 //Resolve relative path.
208 std::string resolve_relative_path(const std::string& path)
210 try {
211 return directory::absolute_path(path);
212 } catch(...) {
213 return path;
217 //Save state.
218 void do_save_state(const std::string& filename, int binary) throw(std::bad_alloc,
219 std::runtime_error)
221 auto& core = CORE();
222 if(!*core.mlogic || !core.mlogic->get_mfile().gametype) {
223 platform::error_message("Can't save movie without a ROM");
224 messages << "Can't save movie without a ROM" << std::endl;
225 return;
227 auto& target = core.mlogic->get_mfile();
228 std::string filename2 = translate_name_mprefix(filename, binary, 1);
229 core.lua2->callback_pre_save(filename2, true);
230 try {
231 uint64_t origtime = framerate_regulator::get_utime();
232 target.dyn.sram = core.rom->save_sram();
233 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
234 auto& img = core.rom->get_rom(i);
235 auto& xml = core.rom->get_markup(i);
236 target.romimg_sha256[i] = img.sha_256.read();
237 target.romxml_sha256[i] = xml.sha_256.read();
238 target.namehint[i] = img.namehint;
240 target.dyn.savestate = core.rom->save_core_state();
241 core.fbuf->get_framebuffer().save(target.dyn.screenshot);
242 core.mlogic->get_movie().save_state(target.projectid, target.dyn.save_frame,
243 target.dyn.lagged_frames, target.dyn.pollcounters);
244 target.dyn.poll_flag = core.rom->get_pflag();
245 auto prj = core.project->get();
246 if(prj) {
247 target.gamename = prj->gamename;
248 target.authors = prj->authors;
250 target.dyn.active_macros = core.controls->get_macro_frames();
251 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
252 core.mlogic->get_rrdata(), true);
253 uint64_t took = framerate_regulator::get_utime() - origtime;
254 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
255 messages << "Saved state " << kind << " '" << filename2 << "' in " << took << " microseconds."
256 << std::endl;
257 core.lua2->callback_post_save(filename2, true);
258 } catch(std::bad_alloc& e) {
259 throw;
260 } catch(std::exception& e) {
261 platform::error_message(std::string("Save failed: ") + e.what());
262 messages << "Save failed: " << e.what() << std::endl;
263 core.lua2->callback_err_save(filename2);
265 last_save = resolve_relative_path(filename2);
266 auto p = core.project->get();
267 if(p) {
268 p->last_save = last_save;
269 p->flush();
273 //Save movie.
274 void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc, std::runtime_error)
276 auto& core = CORE();
277 if(!*core.mlogic || !core.mlogic->get_mfile().gametype) {
278 platform::error_message("Can't save movie without a ROM");
279 messages << "Can't save movie without a ROM" << std::endl;
280 return;
282 auto& target = core.mlogic->get_mfile();
283 std::string filename2 = translate_name_mprefix(filename, binary, 0);
284 core.lua2->callback_pre_save(filename2, false);
285 try {
286 uint64_t origtime = framerate_regulator::get_utime();
287 auto prj = core.project->get();
288 if(prj) {
289 target.gamename = prj->gamename;
290 target.authors = prj->authors;
292 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
293 core.mlogic->get_rrdata(), false);
294 uint64_t took = framerate_regulator::get_utime() - origtime;
295 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
296 messages << "Saved movie " << kind << " '" << filename2 << "' in " << took << " microseconds."
297 << std::endl;
298 core.lua2->callback_post_save(filename2, false);
299 } catch(std::bad_alloc& e) {
300 OOM_panic();
301 } catch(std::exception& e) {
302 platform::error_message(std::string("Save failed: ") + e.what());
303 messages << "Save failed: " << e.what() << std::endl;
304 core.lua2->callback_err_save(filename2);
306 last_save = resolve_relative_path(filename2);
307 auto p = core.project->get();
308 if(p) {
309 p->last_save = last_save;
310 p->flush();
314 namespace
316 void populate_volatile_ram(moviefile& mf, std::list<core_vma_info>& vmas)
318 for(auto i : vmas) {
319 //Only regions that are marked as volatile, readwrite not special are initializable.
320 if(!i.volatile_flag || i.readonly || i.special)
321 continue;
322 if(!mf.ramcontent.count(i.name))
323 continue;
324 uint64_t csize = min((uint64_t)mf.ramcontent[i.name].size(), i.size);
325 if(i.backing_ram)
326 memcpy(i.backing_ram, &mf.ramcontent[i.name][0], csize);
327 else
328 for(uint64_t o = 0; o < csize; o++)
329 i.write(o, mf.ramcontent[i.name][o]);
333 std::string format_length(uint64_t mlength)
335 std::ostringstream x;
336 if(mlength >= 3600000ULL) {
337 x << mlength / 3600000ULL << ":";
338 mlength %= 3600000ULL;
340 x << std::setfill('0') << std::setw(2) << mlength / 60000ULL << ":";
341 mlength %= 60000ULL;
342 x << std::setfill('0') << std::setw(2) << mlength / 1000ULL << ".";
343 mlength %= 1000ULL;
344 x << std::setfill('0') << std::setw(3) << mlength;
345 return x.str();
348 void print_movie_info(moviefile& mov, loaded_rom& rom, rrdata_set& rrd)
350 if(!rom.isnull())
351 messages << "ROM Type " << rom.get_hname() << " region " << rom.region_get_hname()
352 << std::endl;
353 std::string len, rerecs;
354 len = format_length(mov.get_movie_length());
355 std::string rerecords = mov.dyn.save_frame ? (stringfmt() << rrd.count()).str() : mov.rerecords;
356 messages << "Rerecords " << rerecords << " length " << format_length(mov.get_movie_length())
357 << " (" << mov.get_frame_count() << " frames)" << std::endl;
358 if(mov.gamename != "")
359 messages << "Game name: " << mov.gamename << std::endl;
360 for(size_t i = 0; i < mov.authors.size(); i++)
361 if(mov.authors[i].second == "")
362 messages << "Author: " << mov.authors[i].first << std::endl;
363 else if(mov.authors[i].first == "")
364 messages << "Author: (" << mov.authors[i].second << ")" << std::endl;
365 else
366 messages << "Author: " << mov.authors[i].first << " ("
367 << mov.authors[i].second << ")" << std::endl;
370 void warn_coretype(const std::string& mov_core, loaded_rom& against, bool loadstate)
372 if(against.isnull())
373 return;
374 std::string rom_core = against.get_core_identifier();
375 if(mov_core == rom_core)
376 return;
377 std::ostringstream x;
378 x << (loadstate ? "Error: " : "Warning: ");
379 x << "Emulator core version mismatch!" << std::endl
380 << "\tCrurrent: " << rom_core << std::endl
381 << "\tMovie: " << mov_core << std::endl;
382 if(loadstate)
383 throw std::runtime_error(x.str());
384 else
385 messages << x.str();
388 void warn_roms(moviefile& mov, loaded_rom& against, bool loadstate)
390 bool rom_ok = true;
391 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
392 auto& img = against.get_rom(i);
393 auto& xml = against.get_markup(i);
394 if(mov.namehint[i] == "")
395 mov.namehint[i] = img.namehint;
396 if(mov.romimg_sha256[i] == "")
397 mov.romimg_sha256[i] = img.sha_256.read();
398 if(mov.romxml_sha256[i] == "")
399 mov.romxml_sha256[i] = xml.sha_256.read();
400 rom_ok = rom_ok & warn_hash_mismatch(mov.romimg_sha256[i], img,
401 (stringfmt() << "ROM #" << (i + 1)).str(), loadstate);
402 rom_ok = rom_ok & warn_hash_mismatch(mov.romxml_sha256[i], xml,
403 (stringfmt() << "XML #" << (i + 1)).str(), loadstate);
405 if(!rom_ok)
406 throw std::runtime_error("Incorrect ROM");
409 portctrl::type_set& construct_movie_portset(moviefile& mov, loaded_rom& against)
411 auto ctrldata = against.controllerconfig(mov.settings);
412 return portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
415 void handle_load_core(moviefile& _movie, portctrl::type_set& portset, bool will_load_state,
416 bool force = false);
418 void handle_load_core(moviefile& _movie, portctrl::type_set& portset, bool will_load_state, bool force)
420 auto& core = CORE();
421 core.random_seed_value = _movie.movie_rtc_second;
422 if(will_load_state) {
423 //If settings possibly change, reload the ROM.
424 if(force || !*core.mlogic || core.mlogic->get_mfile().projectid != _movie.projectid)
425 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
426 //Load the savestate and movie state.
427 //Set the core ports in order to avoid port state being reinitialized when loading.
428 core.controls->set_ports(portset);
429 core.rom->load_core_state(_movie.dyn.savestate);
430 core.rom->set_pflag(_movie.dyn.poll_flag);
431 core.controls->set_macro_frames(_movie.dyn.active_macros);
432 } else {
433 //If settings possibly change, reload the ROM. Otherwise rewind to beginning.
434 if(force || !*core.mlogic || core.mlogic->get_mfile().projectid != _movie.projectid)
435 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
436 else
437 core.rom->reset_to_load();
438 //Load the SRAM and volatile RAM. Or anchor savestate if any.
439 core.controls->set_ports(portset);
440 _movie.dyn.rtc_second = _movie.movie_rtc_second;
441 _movie.dyn.rtc_subsecond = _movie.movie_rtc_subsecond;
442 if(!_movie.anchor_savestate.empty()) {
443 core.rom->load_core_state(_movie.anchor_savestate);
444 } else {
445 core.rom->load_sram(_movie.movie_sram);
446 std::list<core_vma_info> vmas = core.rom->vma_list();
447 populate_volatile_ram(_movie, vmas);
449 core.rom->set_pflag(0);
450 core.controls->set_macro_frames(std::map<std::string, uint64_t>());
455 void do_load_rom() throw(std::bad_alloc, std::runtime_error)
457 auto& core = CORE();
458 bool load_readwrite = !*core.mlogic || !core.mlogic->get_movie().readonly_mode();
459 if(*core.mlogic) {
460 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
461 //If portset or gametype changes, force readwrite with new movie.
462 if(core.mlogic->get_mfile().input->get_types() != portset) load_readwrite = true;
463 else if(!core.rom->is_of_type(core.mlogic->get_mfile().gametype->get_type())) load_readwrite = true;
464 else if(!core.rom->region_compatible_with(core.mlogic->get_mfile().gametype->get_region()))
465 load_readwrite = true;
468 if(!load_readwrite) {
469 //Read-only load. This is pretty simple.
470 //Force unlazying of rrdata and count a rerecord.
471 if(core.mlogic->get_rrdata().is_lazy())
472 core.mlogic->get_rrdata().read_base(rrdata::filename(
473 core.mlogic->get_mfile().projectid), false);
474 core.mlogic->get_rrdata().add((*core.nrrdata)());
476 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
478 try {
479 //Force game's region to run's region. We already checked this is possible above.
480 core.rom->set_internal_region(core.mlogic->get_mfile().gametype->get_region());
482 handle_load_core(core.mlogic->get_mfile(), portset, false, true);
483 core.mlogic->get_mfile().gametype = &core.rom->get_sysregion();
484 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
485 auto& img = core.rom->get_rom(i);
486 auto& xml = core.rom->get_markup(i);
487 core.mlogic->get_mfile().namehint[i] = img.namehint;
488 core.mlogic->get_mfile().romimg_sha256[i] = img.sha_256.read();
489 core.mlogic->get_mfile().romxml_sha256[i] = xml.sha_256.read();
491 core.mlogic->get_mfile().clear_dynstate();
492 core.mlogic->get_movie().reset_state();
493 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
494 core.lua2->callback_do_rewind();
495 } catch(std::bad_alloc& e) {
496 OOM_panic();
497 } catch(std::exception& e) {
498 core.runmode->set_corrupt();
499 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
500 throw;
502 } else {
503 //The more complicated Read-Write case.
504 //We need to create a new movie and movie file.
505 temporary_handle<moviefile> _movie;
506 _movie.get()->force_corrupt = false;
507 _movie.get()->gametype = NULL; //Not yet known.
508 _movie.get()->coreversion = core.rom->get_core_identifier();
509 _movie.get()->projectid = get_random_hexstring(40);
510 _movie.get()->rerecords = "0";
511 _movie.get()->rerecords_mem = 0;
512 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
513 auto& img = core.rom->get_rom(i);
514 auto& xml = core.rom->get_markup(i);
515 _movie.get()->namehint[i] = img.namehint;
516 _movie.get()->romimg_sha256[i] = img.sha_256.read();
517 _movie.get()->romxml_sha256[i] = xml.sha_256.read();
519 _movie.get()->movie_rtc_second = _movie.get()->dyn.rtc_second = 1000000000ULL;
520 _movie.get()->movie_rtc_subsecond = _movie.get()->dyn.rtc_subsecond = 0;
521 _movie.get()->start_paused = false;
522 _movie.get()->lazy_project_create = true;
523 portctrl::type_set& portset2 = construct_movie_portset(*_movie.get(), *core.rom);
524 _movie.get()->input = NULL;
525 _movie.get()->create_default_branch(portset2);
527 //Wrap the input in movie.
528 temporary_handle<movie> newmovie;
529 newmovie.get()->set_movie_data(_movie.get()->input);
530 newmovie.get()->load(_movie.get()->rerecords, _movie.get()->projectid, *(_movie.get()->input));
531 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
532 newmovie.get()->readonly_mode(false);
534 //Force new lazy rrdata and count a rerecord.
535 temporary_handle<rrdata_set> rrd;
536 rrd.get()->read_base(rrdata::filename(_movie.get()->projectid), true);
537 rrd.get()->add((*core.nrrdata)());
538 //Movie data is lost.
539 core.lua2->callback_movie_lost("reload");
540 try {
541 handle_load_core(*_movie.get(), portset2, false, true);
542 _movie.get()->gametype = &core.rom->get_sysregion();
543 } catch(std::bad_alloc& e) {
544 OOM_panic();
545 } catch(std::exception& e) {
546 core.runmode->set_corrupt();
547 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
548 throw;
551 //Set up stuff.
552 core.mlogic->set_movie(*(newmovie()), true);
553 core.mlogic->set_mfile(*(_movie()), true);
554 core.mlogic->set_rrdata(*(rrd()), true);
555 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
556 set_gameinfo(core.mlogic->get_mfile());
558 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
559 core.lua2->callback_do_rewind();
561 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
562 core.dispatch->mbranch_change();
563 messages << "ROM reloaded." << std::endl;
566 void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
568 auto& core = CORE();
569 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
570 throw std::runtime_error("Can't rewind movie without existing movie");
572 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
574 //Force unlazying of rrdata and count a rerecord.
575 if(core.mlogic->get_rrdata().is_lazy())
576 core.mlogic->get_rrdata().read_base(rrdata::filename(
577 core.mlogic->get_mfile().projectid), false);
578 core.mlogic->get_rrdata().add((*core.nrrdata)());
580 //Enter readonly mode.
581 core.mlogic->get_movie().readonly_mode(true);
582 core.dispatch->mode_change(true);
583 try {
584 handle_load_core(core.mlogic->get_mfile(), portset, false);
585 core.mlogic->get_mfile().clear_dynstate();
586 core.mlogic->get_movie().reset_state();
587 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
588 core.lua2->callback_do_rewind();
589 } catch(std::bad_alloc& e) {
590 OOM_panic();
591 } catch(std::exception& e) {
592 core.runmode->set_corrupt();
593 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
594 throw;
596 messages << "Movie rewound to beginning." << std::endl;
599 //Load state preserving input. Does not do checks.
600 void do_load_state_preserve(struct moviefile& _movie)
602 auto& core = CORE();
603 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
604 throw std::runtime_error("Can't load movie preserving input without previous movie");
605 if(core.mlogic->get_mfile().projectid != _movie.projectid)
606 throw std::runtime_error("Savestate is from different movie");
608 bool will_load_state = _movie.dyn.save_frame;
609 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
611 //Construct a new movie sharing the input data.
612 temporary_handle<movie> newmovie;
613 newmovie.get()->set_movie_data(core.mlogic->get_mfile().input);
614 newmovie.get()->readonly_mode(true);
615 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
617 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
618 if(will_load_state)
619 newmovie.get()->restore_state(_movie.dyn.save_frame, _movie.dyn.lagged_frames,
620 _movie.dyn.pollcounters, true, _movie.input, _movie.projectid);
622 //Count a rerecord.
623 if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
624 core.mlogic->get_rrdata().read_base(rrdata::filename(_movie.projectid), false);
625 core.mlogic->get_rrdata().add((*core.nrrdata)());
627 //Negative return.
628 try {
629 handle_load_core(_movie, portset, will_load_state);
630 } catch(std::bad_alloc& e) {
631 OOM_panic();
632 } catch(std::exception& e) {
633 core.runmode->set_corrupt();
634 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
635 throw;
638 //Set new movie.
639 core.mlogic->set_movie(*(newmovie()), true);
641 //If not loading a state, clear the state, so state swap will swap in state at beginning.
642 if(!will_load_state)
643 _movie.clear_dynstate();
644 //Swap the dynamic state to movie dynamic state (will desync otherwise).
645 core.mlogic->get_mfile().dyn.swap(_movie.dyn);
647 try {
648 //Paint the screen.
649 framebuffer::raw tmp;
650 if(will_load_state) {
651 tmp.load(core.mlogic->get_mfile().dyn.screenshot);
652 core.fbuf->redraw_framebuffer(tmp);
653 } else
654 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
655 } catch(...) {
657 delete &_movie;
658 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
659 messages << "Loadstated at earlier point of movie." << std::endl;
662 //Load state from loaded movie file. _movie is consumed.
663 void do_load_state(struct moviefile& _movie, int lmode, bool& used)
665 auto& core = CORE();
666 //Some basic sanity checks.
667 bool current_mode = *core.mlogic ? core.mlogic->get_movie().readonly_mode() : false;
668 bool will_load_state = _movie.dyn.save_frame && lmode != LOAD_STATE_MOVIE;
670 //Load state all branches and load state initial are the same.
671 if(lmode == LOAD_STATE_ALLBRANCH) lmode = LOAD_STATE_INITIAL;
673 //Various checks.
674 if(_movie.force_corrupt)
675 throw std::runtime_error("Movie file invalid");
676 if(!core.rom->is_of_type(_movie.gametype->get_type()))
677 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
678 if(!core.rom->region_compatible_with(_movie.gametype->get_region()))
679 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
680 warn_coretype(_movie.coreversion, *core.rom, will_load_state);
681 warn_roms(_movie, *core.rom, will_load_state);
683 //In certain conditions, trun LOAD_STATE_CURRENT into LOAD_STATE_PRESERVE.
684 if(lmode == LOAD_STATE_CURRENT && current_mode && SET_readonly_load_preserves(*core.settings))
685 lmode = LOAD_STATE_PRESERVE;
686 //If movie file changes, turn LOAD_STATE_CURRENT into LOAD_STATE_RO
687 if(lmode == LOAD_STATE_CURRENT && core.mlogic->get_mfile().projectid != _movie.projectid)
688 lmode = LOAD_STATE_RO;
690 //Handle preserving load specially.
691 if(lmode == LOAD_STATE_PRESERVE) {
692 do_load_state_preserve(_movie);
693 used = true;
694 return;
697 //Create a new movie, and if needed, restore the movie state.
698 temporary_handle<movie> newmovie;
699 newmovie.get()->set_movie_data(_movie.input);
700 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
701 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
702 if(will_load_state)
703 newmovie.get()->restore_state(_movie.dyn.save_frame, _movie.dyn.lagged_frames,
704 _movie.dyn.pollcounters, true, NULL, _movie.projectid);
706 //Copy the other branches.
707 if(lmode != LOAD_STATE_INITIAL && core.mlogic->get_mfile().projectid == _movie.projectid) {
708 newmovie.get()->set_movie_data(NULL);
709 auto& oldm = core.mlogic->get_mfile().branches;
710 auto& newm = _movie.branches;
711 auto oldd = core.mlogic->get_mfile().input;
712 auto newd = _movie.input;
713 std::string dflt_name;
714 //What was the old default name?
715 for(auto& i : oldm)
716 if(&i.second == oldd)
717 dflt_name = i.first;
718 //Rename the default to match with old movie if the names differ.
719 if(!newm.count(dflt_name) || &newm[dflt_name] != newd)
720 newm[dflt_name] = *newd;
721 //Copy all other branches.
722 for(auto& i : oldm)
723 if(i.first != dflt_name)
724 newm[i.first] = i.second;
725 //Delete branches that didn't exist.
726 for(auto i = newm.begin(); i != newm.end();) {
727 if(oldm.count(i->first))
728 i++;
729 else
730 i = newm.erase(i);
732 _movie.input = &newm[dflt_name];
733 newmovie.get()->set_movie_data(_movie.input);
736 portctrl::type_set& portset = construct_movie_portset(_movie, *core.rom);
738 temporary_handle<rrdata_set> rrd;
739 bool new_rrdata = false;
740 //Count a rerecord (against new or old movie).
741 if(!*core.mlogic || _movie.projectid != core.mlogic->get_mfile().projectid) {
742 rrd.get()->read_base(rrdata::filename(_movie.projectid), _movie.lazy_project_create);
743 rrd.get()->read(_movie.c_rrdata);
744 rrd.get()->add((*core.nrrdata)());
745 new_rrdata = true;
746 } else {
747 //Unlazy rrdata if needed.
748 if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
749 core.mlogic->get_rrdata().read_base(rrdata::filename(_movie.projectid), false);
750 core.mlogic->get_rrdata().add((*core.nrrdata)());
752 //Negative return.
753 try {
754 core.rom->set_internal_region(_movie.gametype->get_region());
755 handle_load_core(_movie, portset, will_load_state);
756 } catch(std::bad_alloc& e) {
757 OOM_panic();
758 } catch(std::exception& e) {
759 core.runmode->set_corrupt();
760 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
761 throw;
764 //If loaded a movie, clear the is savestate and rrdata.
765 if(!will_load_state)
766 _movie.clear_dynstate();
768 core.lua2->callback_movie_lost("load");
770 //Copy the data.
771 if(new_rrdata) core.mlogic->set_rrdata(*(rrd()), true);
772 core.mlogic->set_movie(*newmovie(), true);
773 core.mlogic->set_mfile(_movie, true);
774 used = true;
776 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
778 //Activate RW mode if needed.
779 auto& m = core.mlogic->get_movie();
780 if(lmode == LOAD_STATE_RW)
781 m.readonly_mode(false);
782 if(lmode == LOAD_STATE_DEFAULT && !current_mode && m.get_frame_count() <= m.get_current_frame())
783 m.readonly_mode(false);
784 if(lmode == LOAD_STATE_INITIAL && m.get_frame_count() <= m.get_current_frame())
785 m.readonly_mode(false);
786 if(lmode == LOAD_STATE_CURRENT && !current_mode)
787 m.readonly_mode(false);
789 //Paint the screen.
791 framebuffer::raw tmp;
792 if(will_load_state) {
793 tmp.load(_movie.dyn.screenshot);
794 core.fbuf->redraw_framebuffer(tmp);
795 } else
796 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
799 core.dispatch->mode_change(m.readonly_mode());
800 print_movie_info(_movie, *core.rom, core.mlogic->get_rrdata());
801 core.dispatch->mbranch_change();
802 set_gameinfo(core.mlogic->get_mfile());
806 void try_request_rom(const std::string& moviefile)
808 auto& core = CORE();
809 moviefile::brief_info info(moviefile);
810 auto sysregs = core_sysregion::find_matching(info.sysregion);
811 rom_request req;
812 req.selected = 0;
813 size_t idx = 0;
814 req.core_guessed = false;
815 for(auto i : sysregs) {
816 //FIXME: Do something with this?
817 //if(i->get_type().get_biosname() != "" && info.hash[1] != "")
818 // has_bios = true;
819 req.cores.push_back(&i->get_type());
820 if(i->get_type().get_core_identifier() == info.corename) {
821 req.selected = idx;
822 req.core_guessed = true;
824 idx++;
826 if(req.cores.empty())
827 throw std::runtime_error("No known core can load movie of type '" + info.sysregion + "'");
828 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
829 req.guessed[i] = false;
830 req.has_slot[i] = (info.hash[i] != "");
831 req.filename[i] = info.hint[i];
832 req.hash[i] = info.hash[i];
833 req.hashxml[i] = info.hashxml[i];
835 try_guess_roms(req);
836 req.canceled = true;
837 graphics_driver_request_rom(req);
838 if(req.canceled)
839 throw std::runtime_error("Canceled loading ROM");
840 //Try to load the ROM using specified core.
841 if(req.selected >= req.cores.size())
842 throw std::runtime_error("Invalid ROM type selected");
843 core_type* selected_core = req.cores[req.selected];
844 loaded_rom newrom(new rom_image(req.filename, selected_core->get_core_identifier(),
845 selected_core->get_iname(), ""));
846 *core.rom = newrom;
847 core.dispatch->core_change();
850 //Load state
851 bool do_load_state(const std::string& filename, int lmode)
853 auto& core = CORE();
854 int tmp = -1;
855 std::string filename2 = translate_name_mprefix(filename, tmp, -1);
856 uint64_t origtime = framerate_regulator::get_utime();
857 core.lua2->callback_pre_load(filename2);
858 struct moviefile* mfile = NULL;
859 bool used = false;
860 try {
861 if(core.rom->isnull())
862 try_request_rom(filename2);
863 mfile = new moviefile(filename2, core.rom->get_internal_rom_type());
864 } catch(std::bad_alloc& e) {
865 OOM_panic();
866 } catch(std::exception& e) {
867 platform::error_message(std::string("Can't read movie/savestate: ") + e.what());
868 messages << "Can't read movie/savestate '" << filename2 << "': " << e.what() << std::endl;
869 core.lua2->callback_err_load(filename2);
870 return false;
872 try {
873 do_load_state(*mfile, lmode, used);
874 uint64_t took = framerate_regulator::get_utime() - origtime;
875 messages << "Loaded '" << filename2 << "' in " << took << " microseconds." << std::endl;
876 core.lua2->callback_post_load(filename2, core.mlogic->get_mfile().dyn.save_frame);
877 } catch(std::bad_alloc& e) {
878 OOM_panic();
879 } catch(std::exception& e) {
880 if(!used)
881 delete mfile;
882 platform::error_message(std::string("Can't load movie/savestate: ") + e.what());
883 messages << "Can't load movie/savestate '" << filename2 << "': " << e.what() << std::endl;
884 core.lua2->callback_err_load(filename2);
885 return false;
887 return true;
890 void mainloop_restore_state(const dynamic_state& state)
892 auto& core = CORE();
893 //Force unlazy rrdata.
894 core.mlogic->get_rrdata().read_base(rrdata::filename(core.mlogic->get_mfile().projectid),
895 false);
896 core.mlogic->get_rrdata().add((*core.nrrdata)());
897 core.rom->load_core_state(state.savestate, true);
900 rrdata::rrdata()
901 : init(false)
905 rrdata_set::instance rrdata::operator()()
907 if(!init)
908 next = rrdata_set::instance(get_random_hexstring(2 * RRDATA_BYTES));
909 init = true;
910 return next++;
913 std::string rrdata::filename(const std::string& projectid)
915 return get_config_path() + "/" + projectid + ".rr";