Refactor loaded_rom to make public fields private
[lsnes.git] / src / core / moviedata.cpp
blob7058d20b4d577e6f1afcac0c177bfad3573ee647
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 = core.setcache->get("slotpath") + "/";
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.is_savestate = true;
233 target.sram = core.rom->save_sram();
234 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
235 auto& img = core.rom->get_rom(i);
236 auto& xml = core.rom->get_markup(i);
237 target.romimg_sha256[i] = img.sha_256.read();
238 target.romxml_sha256[i] = xml.sha_256.read();
239 target.namehint[i] = img.namehint;
241 target.savestate = core.rom->save_core_state();
242 core.fbuf->get_framebuffer().save(target.screenshot);
243 core.mlogic->get_movie().save_state(target.projectid, target.save_frame,
244 target.lagged_frames, target.pollcounters);
245 target.poll_flag = core.rom->get_pflag();
246 auto prj = core.project->get();
247 if(prj) {
248 target.gamename = prj->gamename;
249 target.authors = prj->authors;
251 target.active_macros = core.controls->get_macro_frames();
252 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
253 core.mlogic->get_rrdata());
254 uint64_t took = framerate_regulator::get_utime() - origtime;
255 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
256 messages << "Saved state " << kind << " '" << filename2 << "' in " << took << " microseconds."
257 << std::endl;
258 core.lua2->callback_post_save(filename2, true);
259 } catch(std::bad_alloc& e) {
260 throw;
261 } catch(std::exception& e) {
262 platform::error_message(std::string("Save failed: ") + e.what());
263 messages << "Save failed: " << e.what() << std::endl;
264 core.lua2->callback_err_save(filename2);
266 last_save = resolve_relative_path(filename2);
267 auto p = core.project->get();
268 if(p) {
269 p->last_save = last_save;
270 p->flush();
274 //Save movie.
275 void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc, std::runtime_error)
277 auto& core = CORE();
278 if(!*core.mlogic || !core.mlogic->get_mfile().gametype) {
279 platform::error_message("Can't save movie without a ROM");
280 messages << "Can't save movie without a ROM" << std::endl;
281 return;
283 auto& target = core.mlogic->get_mfile();
284 std::string filename2 = translate_name_mprefix(filename, binary, 0);
285 core.lua2->callback_pre_save(filename2, false);
286 try {
287 uint64_t origtime = framerate_regulator::get_utime();
288 target.is_savestate = false;
289 auto prj = core.project->get();
290 if(prj) {
291 target.gamename = prj->gamename;
292 target.authors = prj->authors;
294 target.active_macros.clear();
295 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
296 core.mlogic->get_rrdata());
297 uint64_t took = framerate_regulator::get_utime() - origtime;
298 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
299 messages << "Saved movie " << kind << " '" << filename2 << "' in " << took << " microseconds."
300 << std::endl;
301 core.lua2->callback_post_save(filename2, false);
302 } catch(std::bad_alloc& e) {
303 OOM_panic();
304 } catch(std::exception& e) {
305 platform::error_message(std::string("Save failed: ") + e.what());
306 messages << "Save failed: " << e.what() << std::endl;
307 core.lua2->callback_err_save(filename2);
309 last_save = resolve_relative_path(filename2);
310 auto p = core.project->get();
311 if(p) {
312 p->last_save = last_save;
313 p->flush();
317 namespace
319 void populate_volatile_ram(moviefile& mf, std::list<core_vma_info>& vmas)
321 for(auto i : vmas) {
322 //Only regions that are marked as volatile, readwrite not special are initializable.
323 if(!i.volatile_flag || i.readonly || i.special)
324 continue;
325 if(!mf.ramcontent.count(i.name))
326 continue;
327 uint64_t csize = min((uint64_t)mf.ramcontent[i.name].size(), i.size);
328 if(i.backing_ram)
329 memcpy(i.backing_ram, &mf.ramcontent[i.name][0], csize);
330 else
331 for(uint64_t o = 0; o < csize; o++)
332 i.write(o, mf.ramcontent[i.name][o]);
336 std::string format_length(uint64_t mlength)
338 std::ostringstream x;
339 if(mlength >= 3600000ULL) {
340 x << mlength / 3600000ULL << ":";
341 mlength %= 3600000ULL;
343 x << std::setfill('0') << std::setw(2) << mlength / 60000ULL << ":";
344 mlength %= 60000ULL;
345 x << std::setfill('0') << std::setw(2) << mlength / 1000ULL << ".";
346 mlength %= 1000ULL;
347 x << std::setfill('0') << std::setw(3) << mlength;
348 return x.str();
351 void print_movie_info(moviefile& mov, loaded_rom& rom, rrdata_set& rrd)
353 if(!rom.isnull())
354 messages << "ROM Type " << rom.get_hname() << " region " << rom.region_get_hname()
355 << std::endl;
356 std::string len, rerecs;
357 len = format_length(mov.get_movie_length());
358 std::string rerecords = mov.is_savestate ? (stringfmt() << rrd.count()).str() : mov.rerecords;
359 messages << "Rerecords " << rerecords << " length " << format_length(mov.get_movie_length())
360 << " (" << mov.get_frame_count() << " frames)" << std::endl;
361 if(mov.gamename != "")
362 messages << "Game name: " << mov.gamename << std::endl;
363 for(size_t i = 0; i < mov.authors.size(); i++)
364 if(mov.authors[i].second == "")
365 messages << "Author: " << mov.authors[i].first << std::endl;
366 else if(mov.authors[i].first == "")
367 messages << "Author: (" << mov.authors[i].second << ")" << std::endl;
368 else
369 messages << "Author: " << mov.authors[i].first << " ("
370 << mov.authors[i].second << ")" << std::endl;
373 void warn_coretype(const std::string& mov_core, loaded_rom& against, bool loadstate)
375 if(against.isnull())
376 return;
377 std::string rom_core = against.get_core_identifier();
378 if(mov_core == rom_core)
379 return;
380 std::ostringstream x;
381 x << (loadstate ? "Error: " : "Warning: ");
382 x << "Emulator core version mismatch!" << std::endl
383 << "\tCrurrent: " << rom_core << std::endl
384 << "\tMovie: " << mov_core << std::endl;
385 if(loadstate)
386 throw std::runtime_error(x.str());
387 else
388 messages << x.str();
391 void warn_roms(moviefile& mov, loaded_rom& against, bool loadstate)
393 bool rom_ok = true;
394 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
395 auto& img = against.get_rom(i);
396 auto& xml = against.get_markup(i);
397 if(mov.namehint[i] == "")
398 mov.namehint[i] = img.namehint;
399 if(mov.romimg_sha256[i] == "")
400 mov.romimg_sha256[i] = img.sha_256.read();
401 if(mov.romxml_sha256[i] == "")
402 mov.romxml_sha256[i] = xml.sha_256.read();
403 rom_ok = rom_ok & warn_hash_mismatch(mov.romimg_sha256[i], img,
404 (stringfmt() << "ROM #" << (i + 1)).str(), loadstate);
405 rom_ok = rom_ok & warn_hash_mismatch(mov.romxml_sha256[i], xml,
406 (stringfmt() << "XML #" << (i + 1)).str(), loadstate);
408 if(!rom_ok)
409 throw std::runtime_error("Incorrect ROM");
412 portctrl::type_set& construct_movie_portset(moviefile& mov, loaded_rom& against)
414 auto ctrldata = against.controllerconfig(mov.settings);
415 return portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
418 void handle_load_core(moviefile& _movie, portctrl::type_set& portset, bool will_load_state)
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(!*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.savestate);
430 core.rom->set_pflag(_movie.poll_flag);
431 core.controls->set_macro_frames(_movie.active_macros);
432 } else {
433 //Reload the ROM in order to rewind to the beginning.
434 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
435 //Load the SRAM and volatile RAM. Or anchor savestate if any.
436 core.controls->set_ports(portset);
437 _movie.rtc_second = _movie.movie_rtc_second;
438 _movie.rtc_subsecond = _movie.movie_rtc_subsecond;
439 if(!_movie.anchor_savestate.empty()) {
440 core.rom->load_core_state(_movie.anchor_savestate);
441 } else {
442 core.rom->load_sram(_movie.movie_sram);
443 std::list<core_vma_info> vmas = core.rom->vma_list();
444 populate_volatile_ram(_movie, vmas);
446 core.rom->set_pflag(0);
447 core.controls->set_macro_frames(std::map<std::string, uint64_t>());
452 void do_load_rom() throw(std::bad_alloc, std::runtime_error)
454 auto& core = CORE();
455 bool load_readwrite = !*core.mlogic || !core.mlogic->get_movie().readonly_mode();
456 if(*core.mlogic) {
457 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
458 //If portset or gametype changes, force readwrite with new movie.
459 if(core.mlogic->get_mfile().input->get_types() != portset) load_readwrite = true;
460 if(!core.rom->is_of_type(core.mlogic->get_mfile().gametype->get_type())) load_readwrite = true;
463 if(!load_readwrite) {
464 //Read-only load. This is pretty simple.
465 //Force unlazying of rrdata and count a rerecord.
466 if(core.mlogic->get_rrdata().is_lazy())
467 core.mlogic->get_rrdata().read_base(rrdata::filename(
468 core.mlogic->get_mfile().projectid), false);
469 core.mlogic->get_rrdata().add((*core.nrrdata)());
471 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
473 try {
474 handle_load_core(core.mlogic->get_mfile(), portset, false);
475 core.mlogic->get_mfile().gametype = &core.rom->get_sysregion();
476 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
477 auto& img = core.rom->get_rom(i);
478 auto& xml = core.rom->get_markup(i);
479 core.mlogic->get_mfile().namehint[i] = img.namehint;
480 core.mlogic->get_mfile().romimg_sha256[i] = img.sha_256.read();
481 core.mlogic->get_mfile().romxml_sha256[i] = xml.sha_256.read();
483 core.mlogic->get_mfile().is_savestate = false;
484 core.mlogic->get_mfile().host_memory.clear();
485 core.mlogic->get_movie().reset_state();
486 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
487 core.lua2->callback_do_rewind();
488 } catch(std::bad_alloc& e) {
489 OOM_panic();
490 } catch(std::exception& e) {
491 core.runmode->set_corrupt();
492 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
493 throw;
495 } else {
496 //The more complicated Read-Write case.
497 //We need to create a new movie and movie file.
498 temporary_handle<moviefile> _movie;
499 _movie.get()->force_corrupt = false;
500 _movie.get()->gametype = NULL; //Not yet known.
501 _movie.get()->coreversion = core.rom->get_core_identifier();
502 _movie.get()->projectid = get_random_hexstring(40);
503 _movie.get()->rerecords = "0";
504 _movie.get()->rerecords_mem = 0;
505 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
506 auto& img = core.rom->get_rom(i);
507 auto& xml = core.rom->get_markup(i);
508 _movie.get()->namehint[i] = img.namehint;
509 _movie.get()->romimg_sha256[i] = img.sha_256.read();
510 _movie.get()->romxml_sha256[i] = xml.sha_256.read();
512 _movie.get()->is_savestate = false;
513 _movie.get()->save_frame = 0;
514 _movie.get()->lagged_frames = 0;
515 _movie.get()->poll_flag = false;
516 _movie.get()->movie_rtc_second = _movie.get()->rtc_second = 1000000000ULL;
517 _movie.get()->movie_rtc_subsecond = _movie.get()->rtc_subsecond = 0;
518 _movie.get()->start_paused = false;
519 _movie.get()->lazy_project_create = true;
520 portctrl::type_set& portset2 = construct_movie_portset(*_movie.get(), *core.rom);
521 _movie.get()->input = NULL;
522 _movie.get()->create_default_branch(portset2);
524 //Wrap the input in movie.
525 temporary_handle<movie> newmovie;
526 newmovie.get()->set_movie_data(_movie.get()->input);
527 newmovie.get()->load(_movie.get()->rerecords, _movie.get()->projectid, *(_movie.get()->input));
528 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
529 newmovie.get()->readonly_mode(false);
531 //Force new lazy rrdata and count a rerecord.
532 temporary_handle<rrdata_set> rrd;
533 rrd.get()->read_base(rrdata::filename(_movie.get()->projectid), true);
534 rrd.get()->add((*core.nrrdata)());
535 //Movie data is lost.
536 core.lua2->callback_movie_lost("reload");
537 try {
538 handle_load_core(*_movie.get(), portset2, false);
539 _movie.get()->gametype = &core.rom->get_sysregion();
540 } catch(std::bad_alloc& e) {
541 OOM_panic();
542 } catch(std::exception& e) {
543 core.runmode->set_corrupt();
544 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
545 throw;
548 //Set up stuff.
549 core.mlogic->set_movie(*(newmovie()), true);
550 core.mlogic->set_mfile(*(_movie()), true);
551 core.mlogic->set_rrdata(*(rrd()), true);
552 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
553 set_gameinfo(core.mlogic->get_mfile());
555 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
556 core.lua2->callback_do_rewind();
558 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
559 core.dispatch->mbranch_change();
560 messages << "ROM reloaded." << std::endl;
563 void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
565 auto& core = CORE();
566 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
567 throw std::runtime_error("Can't rewind movie without existing movie");
569 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
571 //Force unlazying of rrdata and count a rerecord.
572 if(core.mlogic->get_rrdata().is_lazy())
573 core.mlogic->get_rrdata().read_base(rrdata::filename(
574 core.mlogic->get_mfile().projectid), false);
575 core.mlogic->get_rrdata().add((*core.nrrdata)());
577 //Enter readonly mode.
578 core.mlogic->get_movie().readonly_mode(true);
579 core.dispatch->mode_change(true);
580 try {
581 handle_load_core(core.mlogic->get_mfile(), portset, false);
582 core.mlogic->get_mfile().is_savestate = false;
583 core.mlogic->get_mfile().host_memory.clear();
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.is_savestate;
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.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
618 _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 //Some fields MUST be taken from movie or one gets desyncs.
640 core.mlogic->get_mfile().is_savestate = _movie.is_savestate;
641 core.mlogic->get_mfile().rtc_second = _movie.rtc_second;
642 core.mlogic->get_mfile().rtc_subsecond = _movie.rtc_subsecond;
643 std::swap(core.mlogic->get_mfile().host_memory, _movie.host_memory);
644 if(!will_load_state)
645 core.mlogic->get_mfile().host_memory.clear();
647 try {
648 //Paint the screen.
649 framebuffer::raw tmp;
650 if(will_load_state) {
651 tmp.load(_movie.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.is_savestate && 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.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
704 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.is_savestate = false;
767 _movie.host_memory.clear();
770 core.lua2->callback_movie_lost("load");
772 //Copy the data.
773 if(new_rrdata) core.mlogic->set_rrdata(*(rrd()), true);
774 core.mlogic->set_movie(*newmovie(), true);
775 core.mlogic->set_mfile(_movie, true);
776 used = true;
778 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
780 //Activate RW mode if needed.
781 auto& m = core.mlogic->get_movie();
782 if(lmode == LOAD_STATE_RW)
783 m.readonly_mode(false);
784 if(lmode == LOAD_STATE_DEFAULT && !current_mode && m.get_frame_count() <= m.get_current_frame())
785 m.readonly_mode(false);
786 if(lmode == LOAD_STATE_INITIAL && m.get_frame_count() <= m.get_current_frame())
787 m.readonly_mode(false);
788 if(lmode == LOAD_STATE_CURRENT && !current_mode)
789 m.readonly_mode(false);
791 //Paint the screen.
793 framebuffer::raw tmp;
794 if(will_load_state) {
795 tmp.load(_movie.screenshot);
796 core.fbuf->redraw_framebuffer(tmp);
797 } else
798 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
801 core.dispatch->mode_change(m.readonly_mode());
802 print_movie_info(_movie, *core.rom, core.mlogic->get_rrdata());
803 core.dispatch->mbranch_change();
804 set_gameinfo(core.mlogic->get_mfile());
808 void try_request_rom(const std::string& moviefile)
810 auto& core = CORE();
811 moviefile::brief_info info(moviefile);
812 auto sysregs = core_sysregion::find_matching(info.sysregion);
813 rom_request req;
814 req.selected = 0;
815 size_t idx = 0;
816 req.core_guessed = false;
817 for(auto i : sysregs) {
818 //FIXME: Do something with this?
819 //if(i->get_type().get_biosname() != "" && info.hash[1] != "")
820 // has_bios = true;
821 req.cores.push_back(&i->get_type());
822 if(i->get_type().get_core_identifier() == info.corename) {
823 req.selected = idx;
824 req.core_guessed = true;
826 idx++;
828 if(req.cores.empty())
829 throw std::runtime_error("No known core can load movie of type '" + info.sysregion + "'");
830 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
831 req.guessed[i] = false;
832 req.has_slot[i] = (info.hash[i] != "");
833 req.filename[i] = info.hint[i];
834 req.hash[i] = info.hash[i];
835 req.hashxml[i] = info.hashxml[i];
837 try_guess_roms(req);
838 req.canceled = true;
839 graphics_driver_request_rom(req);
840 if(req.canceled)
841 throw std::runtime_error("Canceled loading ROM");
842 //Try to load the ROM using specified core.
843 if(req.selected >= req.cores.size())
844 throw std::runtime_error("Invalid ROM type selected");
845 core_type* selected_core = req.cores[req.selected];
846 loaded_rom newrom(req.filename, selected_core->get_core_identifier(), selected_core->get_iname(), "");
847 *core.rom = newrom;
848 core.dispatch->core_change();
851 //Load state
852 bool do_load_state(const std::string& filename, int lmode)
854 auto& core = CORE();
855 int tmp = -1;
856 std::string filename2 = translate_name_mprefix(filename, tmp, -1);
857 uint64_t origtime = framerate_regulator::get_utime();
858 core.lua2->callback_pre_load(filename2);
859 struct moviefile* mfile = NULL;
860 bool used = false;
861 try {
862 if(core.rom->isnull())
863 try_request_rom(filename2);
864 mfile = new moviefile(filename2, core.rom->get_internal_rom_type());
865 } catch(std::bad_alloc& e) {
866 OOM_panic();
867 } catch(std::exception& e) {
868 platform::error_message(std::string("Can't read movie/savestate: ") + e.what());
869 messages << "Can't read movie/savestate '" << filename2 << "': " << e.what() << std::endl;
870 core.lua2->callback_err_load(filename2);
871 return false;
873 try {
874 do_load_state(*mfile, lmode, used);
875 uint64_t took = framerate_regulator::get_utime() - origtime;
876 messages << "Loaded '" << filename2 << "' in " << took << " microseconds." << std::endl;
877 core.lua2->callback_post_load(filename2, core.mlogic->get_mfile().is_savestate);
878 } catch(std::bad_alloc& e) {
879 OOM_panic();
880 } catch(std::exception& e) {
881 if(!used)
882 delete mfile;
883 platform::error_message(std::string("Can't load movie/savestate: ") + e.what());
884 messages << "Can't load movie/savestate '" << filename2 << "': " << e.what() << std::endl;
885 core.lua2->callback_err_load(filename2);
886 return false;
888 return true;
891 void mainloop_restore_state(const std::vector<char>& state, uint64_t secs, uint64_t ssecs)
893 auto& core = CORE();
894 //Force unlazy rrdata.
895 core.mlogic->get_rrdata().read_base(rrdata::filename(core.mlogic->get_mfile().projectid),
896 false);
897 core.mlogic->get_rrdata().add((*core.nrrdata)());
898 core.mlogic->get_mfile().rtc_second = secs;
899 core.mlogic->get_mfile().rtc_subsecond = ssecs;
900 core.rom->load_core_state(state, true);
903 rrdata::rrdata()
904 : init(false)
908 rrdata_set::instance rrdata::operator()()
910 if(!init)
911 next = rrdata_set::instance(get_random_hexstring(2 * RRDATA_BYTES));
912 init = true;
913 return next++;
916 std::string rrdata::filename(const std::string& projectid)
918 return get_config_path() + "/" + projectid + ".rr";