Namespace library port-controller stuff
[lsnes.git] / src / core / moviedata.cpp
blobc80e3701a723f6b81adf0648527e9199fd1fa0a0
1 #include "lsnes.hpp"
3 #include "core/advdumper.hpp"
4 #include "core/command.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/framebuffer.hpp"
7 #include "core/framerate.hpp"
8 #include "core/instance.hpp"
9 #include "core/jukebox.hpp"
10 #include "core/mainloop.hpp"
11 #include "core/messages.hpp"
12 #include "core/moviedata.hpp"
13 #include "core/project.hpp"
14 #include "core/random.hpp"
15 #include "core/rom.hpp"
16 #include "core/runmode.hpp"
17 #include "core/settings.hpp"
18 #include "interface/romtype.hpp"
19 #include "library/directory.hpp"
20 #include "library/minmax.hpp"
21 #include "library/string.hpp"
22 #include "library/temporary_handle.hpp"
23 #include "lua/lua.hpp"
25 #include <iomanip>
26 #include <fstream>
28 std::string last_save;
30 namespace
32 settingvar::supervariable<settingvar::model_int<0, 9>> SET_savecompression(lsnes_setgrp, "savecompression",
33 "Movie‣Saving‣Compression", 7);
34 settingvar::supervariable<settingvar::model_bool<settingvar::yes_no>> SET_readonly_load_preserves(
35 lsnes_setgrp, "preserve_on_readonly_load", "Movie‣Loading‣Preserve on readonly load", true);
36 threads::lock mprefix_lock;
37 std::string mprefix;
38 bool mprefix_valid;
40 std::string get_mprefix()
42 threads::alock h(mprefix_lock);
43 if(!mprefix_valid)
44 return "movieslot";
45 else
46 return mprefix + "-";
49 command::fnptr<const std::string&> CMD_dump_coresave(lsnes_cmds, "dump-coresave", "Dump bsnes core state",
50 "Syntax: dump-coresave <name>\nDumps core save to <name>\n",
51 [](const std::string& name) throw(std::bad_alloc, std::runtime_error) {
52 auto& core = CORE();
53 auto x = core.rom->save_core_state();
54 x.resize(x.size() - 32);
55 std::ofstream y(name.c_str(), std::ios::out | std::ios::binary);
56 y.write(&x[0], x.size());
57 y.close();
58 messages << "Saved core state to " << name << std::endl;
59 });
61 bool warn_hash_mismatch(const std::string& mhash, const fileimage::image& slot,
62 const std::string& name, bool fatal)
64 if(mhash == slot.sha_256.read())
65 return true;
66 if(!fatal) {
67 messages << "WARNING: " << name << " hash mismatch!" << std::endl
68 << "\tMovie: " << mhash << std::endl
69 << "\tOur ROM: " << slot.sha_256.read() << std::endl;
70 return true;
71 } else {
72 platform::error_message("Can't load state because hashes mismatch");
73 messages << "ERROR: " << name << " hash mismatch!" << std::endl
74 << "\tMovie: " << mhash << std::endl
75 << "\tOur ROM: " << slot.sha_256.read() << std::endl;
76 return false;
80 void set_mprefix(const std::string& pfx)
82 auto& core = CORE();
84 threads::alock h(mprefix_lock);
85 mprefix_valid = (pfx != "");
86 mprefix = pfx;
88 core.supdater->update();
91 std::string get_mprefix_for_project(const std::string& prjid)
93 std::string filename = get_config_path() + "/" + safe_filename(prjid) + ".pfx";
94 std::ifstream strm(filename);
95 if(!strm)
96 return "";
97 std::string pfx;
98 std::getline(strm, pfx);
99 return strip_CR(pfx);
102 void set_gameinfo(moviefile& mfile)
104 master_dumper::gameinfo gi;
105 gi.gamename = mfile.gamename;
106 gi.length = mfile.get_movie_length() / 1000.0;
107 gi.rerecords = mfile.rerecords;
108 gi.authors = mfile.authors;
109 CORE().mdumper->on_gameinfo_change(gi);
112 class _lsnes_pflag_handler : public movie::poll_flag
114 public:
115 ~_lsnes_pflag_handler()
118 int get_pflag()
120 return CORE().rom->get_pflag();
122 void set_pflag(int flag)
124 CORE().rom->set_pflag(flag);
126 } lsnes_pflag_handler;
129 std::string get_mprefix_for_project()
131 auto& core = CORE();
132 return get_mprefix_for_project(*core.mlogic ? core.mlogic->get_mfile().projectid : "");
135 void set_mprefix_for_project(const std::string& prjid, const std::string& pfx)
137 std::string filename = get_config_path() + "/" + safe_filename(prjid) + ".pfx";
138 std::ofstream strm(filename);
139 strm << pfx << std::endl;
142 void set_mprefix_for_project(const std::string& pfx)
144 auto& core = CORE();
145 set_mprefix_for_project(*core.mlogic ? core.mlogic->get_mfile().projectid : "", pfx);
146 set_mprefix(pfx);
149 std::string translate_name_mprefix(std::string original, int& binary, int save)
151 auto& core = CORE();
152 auto p = core.project->get();
153 regex_results r = regex("\\$SLOT:(.*)", original);
154 if(r) {
155 if(binary < 0)
156 binary = core.jukebox->save_binary() ? 1 : 0;
157 if(p) {
158 uint64_t branch = p->get_current_branch();
159 std::string branch_str;
160 std::string filename;
161 if(branch) branch_str = (stringfmt() << "--" << branch).str();
162 filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
163 while(save < 0 && branch) {
164 if(zip::file_exists(filename))
165 break;
166 branch = p->get_parent_branch(branch);
167 branch_str = branch ? ((stringfmt() << "--" << branch).str()) : "";
168 filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
170 return filename;
171 } else {
172 std::string pprf = core.setcache->get("slotpath") + "/";
173 return pprf + get_mprefix() + r[1] + ".lsmv";
175 } else {
176 if(binary < 0)
177 binary = (save ? save_dflt_binary(*core.settings) :
178 movie_dflt_binary(*core.settings)) ? 1 : 0;
179 return original;
183 std::pair<std::string, std::string> split_author(const std::string& author) throw(std::bad_alloc,
184 std::runtime_error)
186 std::string _author = author;
187 std::string fullname;
188 std::string nickname;
189 size_t split = _author.find_first_of("|");
190 if(split >= _author.length()) {
191 fullname = _author;
192 } else {
193 fullname = _author.substr(0, split);
194 nickname = _author.substr(split + 1);
196 if(fullname == "" && nickname == "")
197 throw std::runtime_error("Bad author name");
198 return std::make_pair(fullname, nickname);
201 //Resolve relative path.
202 std::string resolve_relative_path(const std::string& path)
204 try {
205 return directory::absolute_path(path);
206 } catch(...) {
207 return path;
211 //Save state.
212 void do_save_state(const std::string& filename, int binary) throw(std::bad_alloc,
213 std::runtime_error)
215 auto& core = CORE();
216 if(!*core.mlogic || !core.mlogic->get_mfile().gametype) {
217 platform::error_message("Can't save movie without a ROM");
218 messages << "Can't save movie without a ROM" << std::endl;
219 return;
221 auto& target = core.mlogic->get_mfile();
222 std::string filename2 = translate_name_mprefix(filename, binary, 1);
223 core.lua2->callback_pre_save(filename2, true);
224 try {
225 uint64_t origtime = framerate_regulator::get_utime();
226 target.is_savestate = true;
227 target.sram = core.rom->save_sram();
228 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
229 target.romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
230 target.romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
231 target.namehint[i] = core.rom->romimg[i].namehint;
233 target.savestate = core.rom->save_core_state();
234 core.fbuf->get_framebuffer().save(target.screenshot);
235 core.mlogic->get_movie().save_state(target.projectid, target.save_frame,
236 target.lagged_frames, target.pollcounters);
237 target.poll_flag = core.rom->get_pflag();
238 auto prj = core.project->get();
239 if(prj) {
240 target.gamename = prj->gamename;
241 target.authors = prj->authors;
243 target.active_macros = core.controls->get_macro_frames();
244 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
245 core.mlogic->get_rrdata());
246 uint64_t took = framerate_regulator::get_utime() - origtime;
247 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
248 messages << "Saved state " << kind << " '" << filename2 << "' in " << took << " microseconds."
249 << std::endl;
250 core.lua2->callback_post_save(filename2, true);
251 } catch(std::bad_alloc& e) {
252 throw;
253 } catch(std::exception& e) {
254 platform::error_message(std::string("Save failed: ") + e.what());
255 messages << "Save failed: " << e.what() << std::endl;
256 core.lua2->callback_err_save(filename2);
258 last_save = resolve_relative_path(filename2);
259 auto p = core.project->get();
260 if(p) {
261 p->last_save = last_save;
262 p->flush();
266 //Save movie.
267 void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc, std::runtime_error)
269 auto& core = CORE();
270 if(!*core.mlogic || !core.mlogic->get_mfile().gametype) {
271 platform::error_message("Can't save movie without a ROM");
272 messages << "Can't save movie without a ROM" << std::endl;
273 return;
275 auto& target = core.mlogic->get_mfile();
276 std::string filename2 = translate_name_mprefix(filename, binary, 0);
277 core.lua2->callback_pre_save(filename2, false);
278 try {
279 uint64_t origtime = framerate_regulator::get_utime();
280 target.is_savestate = false;
281 auto prj = core.project->get();
282 if(prj) {
283 target.gamename = prj->gamename;
284 target.authors = prj->authors;
286 target.active_macros.clear();
287 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
288 core.mlogic->get_rrdata());
289 uint64_t took = framerate_regulator::get_utime() - origtime;
290 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
291 messages << "Saved movie " << kind << " '" << filename2 << "' in " << took << " microseconds."
292 << std::endl;
293 core.lua2->callback_post_save(filename2, false);
294 } catch(std::bad_alloc& e) {
295 OOM_panic();
296 } catch(std::exception& e) {
297 platform::error_message(std::string("Save failed: ") + e.what());
298 messages << "Save failed: " << e.what() << std::endl;
299 core.lua2->callback_err_save(filename2);
301 last_save = resolve_relative_path(filename2);
302 auto p = core.project->get();
303 if(p) {
304 p->last_save = last_save;
305 p->flush();
309 namespace
311 void populate_volatile_ram(moviefile& mf, std::list<core_vma_info>& vmas)
313 for(auto i : vmas) {
314 //Only regions that are marked as volatile, readwrite not special are initializable.
315 if(!i.volatile_flag || i.readonly || i.special)
316 continue;
317 if(!mf.ramcontent.count(i.name))
318 continue;
319 uint64_t csize = min((uint64_t)mf.ramcontent[i.name].size(), i.size);
320 if(i.backing_ram)
321 memcpy(i.backing_ram, &mf.ramcontent[i.name][0], csize);
322 else
323 for(uint64_t o = 0; o < csize; o++)
324 i.write(o, mf.ramcontent[i.name][o]);
328 std::string format_length(uint64_t mlength)
330 std::ostringstream x;
331 if(mlength >= 3600000ULL) {
332 x << mlength / 3600000ULL << ":";
333 mlength %= 3600000ULL;
335 x << std::setfill('0') << std::setw(2) << mlength / 60000ULL << ":";
336 mlength %= 60000ULL;
337 x << std::setfill('0') << std::setw(2) << mlength / 1000ULL << ".";
338 mlength %= 1000ULL;
339 x << std::setfill('0') << std::setw(3) << mlength;
340 return x.str();
343 void print_movie_info(moviefile& mov, loaded_rom& rom, rrdata_set& rrd)
345 if(!rom.isnull())
346 messages << "ROM Type " << rom.get_hname() << " region " << rom.region_get_hname()
347 << std::endl;
348 std::string len, rerecs;
349 len = format_length(mov.get_movie_length());
350 std::string rerecords = mov.is_savestate ? (stringfmt() << rrd.count()).str() : mov.rerecords;
351 messages << "Rerecords " << rerecords << " length " << format_length(mov.get_movie_length())
352 << " (" << mov.get_frame_count() << " frames)" << std::endl;
353 if(mov.gamename != "")
354 messages << "Game name: " << mov.gamename << std::endl;
355 for(size_t i = 0; i < mov.authors.size(); i++)
356 if(mov.authors[i].second == "")
357 messages << "Author: " << mov.authors[i].first << std::endl;
358 else if(mov.authors[i].first == "")
359 messages << "Author: (" << mov.authors[i].second << ")" << std::endl;
360 else
361 messages << "Author: " << mov.authors[i].first << " ("
362 << mov.authors[i].second << ")" << std::endl;
365 void warn_coretype(const std::string& mov_core, loaded_rom& against, bool loadstate)
367 if(against.isnull())
368 return;
369 std::string rom_core = against.get_core_identifier();
370 if(mov_core == rom_core)
371 return;
372 std::ostringstream x;
373 x << (loadstate ? "Error: " : "Warning: ");
374 x << "Emulator core version mismatch!" << std::endl
375 << "\tCrurrent: " << rom_core << std::endl
376 << "\tMovie: " << mov_core << std::endl;
377 if(loadstate)
378 throw std::runtime_error(x.str());
379 else
380 messages << x.str();
383 void warn_roms(moviefile& mov, loaded_rom& against, bool loadstate)
385 bool rom_ok = true;
386 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
387 if(mov.namehint[i] == "")
388 mov.namehint[i] = against.romimg[i].namehint;
389 if(mov.romimg_sha256[i] == "")
390 mov.romimg_sha256[i] = against.romimg[i].sha_256.read();
391 if(mov.romxml_sha256[i] == "")
392 mov.romxml_sha256[i] = against.romxml[i].sha_256.read();
393 rom_ok = rom_ok & warn_hash_mismatch(mov.romimg_sha256[i], against.romimg[i],
394 (stringfmt() << "ROM #" << (i + 1)).str(), loadstate);
395 rom_ok = rom_ok & warn_hash_mismatch(mov.romxml_sha256[i], against.romxml[i],
396 (stringfmt() << "XML #" << (i + 1)).str(), loadstate);
398 if(!rom_ok)
399 throw std::runtime_error("Incorrect ROM");
402 portctrl::type_set& construct_movie_portset(moviefile& mov, loaded_rom& against)
404 auto ctrldata = against.controllerconfig(mov.settings);
405 return portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
408 void handle_load_core(moviefile& _movie, portctrl::type_set& portset, bool will_load_state)
410 auto& core = CORE();
411 core.random_seed_value = _movie.movie_rtc_second;
412 if(will_load_state) {
413 //If settings possibly change, reload the ROM.
414 if(!*core.mlogic || core.mlogic->get_mfile().projectid != _movie.projectid)
415 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
416 //Load the savestate and movie state.
417 //Set the core ports in order to avoid port state being reinitialized when loading.
418 core.controls->set_ports(portset);
419 core.rom->load_core_state(_movie.savestate);
420 core.rom->set_pflag(_movie.poll_flag);
421 core.controls->set_macro_frames(_movie.active_macros);
422 } else {
423 //Reload the ROM in order to rewind to the beginning.
424 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
425 //Load the SRAM and volatile RAM. Or anchor savestate if any.
426 core.controls->set_ports(portset);
427 _movie.rtc_second = _movie.movie_rtc_second;
428 _movie.rtc_subsecond = _movie.movie_rtc_subsecond;
429 if(!_movie.anchor_savestate.empty()) {
430 core.rom->load_core_state(_movie.anchor_savestate);
431 } else {
432 core.rom->load_sram(_movie.movie_sram);
433 std::list<core_vma_info> vmas = core.rom->vma_list();
434 populate_volatile_ram(_movie, vmas);
436 core.rom->set_pflag(0);
437 core.controls->set_macro_frames(std::map<std::string, uint64_t>());
442 void do_load_rom() throw(std::bad_alloc, std::runtime_error)
444 auto& core = CORE();
445 bool load_readwrite = !*core.mlogic || !core.mlogic->get_movie().readonly_mode();
446 if(*core.mlogic) {
447 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
448 //If portset or gametype changes, force readwrite with new movie.
449 if(core.mlogic->get_mfile().input->get_types() != portset) load_readwrite = true;
450 if(!core.rom->is_of_type(core.mlogic->get_mfile().gametype->get_type())) load_readwrite = true;
453 if(!load_readwrite) {
454 //Read-only load. This is pretty simple.
455 //Force unlazying of rrdata and count a rerecord.
456 if(core.mlogic->get_rrdata().is_lazy())
457 core.mlogic->get_rrdata().read_base(rrdata::filename(
458 core.mlogic->get_mfile().projectid), false);
459 core.mlogic->get_rrdata().add((*core.nrrdata)());
461 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
463 try {
464 handle_load_core(core.mlogic->get_mfile(), portset, false);
465 core.mlogic->get_mfile().gametype = &core.rom->get_sysregion();
466 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
467 core.mlogic->get_mfile().namehint[i] = core.rom->romimg[i].namehint;
468 core.mlogic->get_mfile().romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
469 core.mlogic->get_mfile().romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
471 core.mlogic->get_mfile().is_savestate = false;
472 core.mlogic->get_mfile().host_memory.clear();
473 core.mlogic->get_movie().reset_state();
474 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
475 core.lua2->callback_do_rewind();
476 } catch(std::bad_alloc& e) {
477 OOM_panic();
478 } catch(std::exception& e) {
479 core.runmode->set_corrupt();
480 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
481 throw;
483 } else {
484 //The more complicated Read-Write case.
485 //We need to create a new movie and movie file.
486 temporary_handle<moviefile> _movie;
487 _movie.get()->force_corrupt = false;
488 _movie.get()->gametype = NULL; //Not yet known.
489 _movie.get()->coreversion = core.rom->get_core_identifier();
490 _movie.get()->projectid = get_random_hexstring(40);
491 _movie.get()->rerecords = "0";
492 _movie.get()->rerecords_mem = 0;
493 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
494 _movie.get()->namehint[i] = core.rom->romimg[i].namehint;
495 _movie.get()->romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
496 _movie.get()->romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
498 _movie.get()->is_savestate = false;
499 _movie.get()->save_frame = 0;
500 _movie.get()->lagged_frames = 0;
501 _movie.get()->poll_flag = false;
502 _movie.get()->movie_rtc_second = _movie.get()->rtc_second = 1000000000ULL;
503 _movie.get()->movie_rtc_subsecond = _movie.get()->rtc_subsecond = 0;
504 _movie.get()->start_paused = false;
505 _movie.get()->lazy_project_create = true;
506 portctrl::type_set& portset2 = construct_movie_portset(*_movie.get(), *core.rom);
507 _movie.get()->input = NULL;
508 _movie.get()->create_default_branch(portset2);
510 //Wrap the input in movie.
511 temporary_handle<movie> newmovie;
512 newmovie.get()->set_movie_data(_movie.get()->input);
513 newmovie.get()->load(_movie.get()->rerecords, _movie.get()->projectid, *(_movie.get()->input));
514 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
515 newmovie.get()->readonly_mode(false);
517 //Force new lazy rrdata and count a rerecord.
518 temporary_handle<rrdata_set> rrd;
519 rrd.get()->read_base(rrdata::filename(_movie.get()->projectid), true);
520 rrd.get()->add((*core.nrrdata)());
521 //Movie data is lost.
522 core.lua2->callback_movie_lost("reload");
523 try {
524 handle_load_core(*_movie.get(), portset2, false);
525 _movie.get()->gametype = &core.rom->get_sysregion();
526 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
527 core.lua2->callback_do_rewind();
528 } catch(std::bad_alloc& e) {
529 OOM_panic();
530 } catch(std::exception& e) {
531 core.runmode->set_corrupt();
532 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
533 throw;
536 //Set up stuff.
537 core.mlogic->set_movie(*(newmovie()), true);
538 core.mlogic->set_mfile(*(_movie()), true);
539 core.mlogic->set_rrdata(*(rrd()), true);
540 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
541 set_gameinfo(core.mlogic->get_mfile());
543 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
544 core.dispatch->mbranch_change();
545 messages << "ROM reloaded." << std::endl;
548 void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
550 auto& core = CORE();
551 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
552 throw std::runtime_error("Can't rewind movie without existing movie");
554 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
556 //Force unlazying of rrdata and count a rerecord.
557 if(core.mlogic->get_rrdata().is_lazy())
558 core.mlogic->get_rrdata().read_base(rrdata::filename(
559 core.mlogic->get_mfile().projectid), false);
560 core.mlogic->get_rrdata().add((*core.nrrdata)());
562 //Enter readonly mode.
563 core.mlogic->get_movie().readonly_mode(true);
564 core.dispatch->mode_change(true);
565 try {
566 handle_load_core(core.mlogic->get_mfile(), portset, false);
567 core.mlogic->get_mfile().is_savestate = false;
568 core.mlogic->get_mfile().host_memory.clear();
569 core.mlogic->get_movie().reset_state();
570 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
571 core.lua2->callback_do_rewind();
572 } catch(std::bad_alloc& e) {
573 OOM_panic();
574 } catch(std::exception& e) {
575 core.runmode->set_corrupt();
576 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
577 throw;
579 messages << "Movie rewound to beginning." << std::endl;
582 //Load state preserving input. Does not do checks.
583 void do_load_state_preserve(struct moviefile& _movie)
585 auto& core = CORE();
586 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
587 throw std::runtime_error("Can't load movie preserving input without previous movie");
588 if(core.mlogic->get_mfile().projectid != _movie.projectid)
589 throw std::runtime_error("Savestate is from different movie");
591 bool will_load_state = _movie.is_savestate;
592 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
594 //Construct a new movie sharing the input data.
595 temporary_handle<movie> newmovie;
596 newmovie.get()->set_movie_data(core.mlogic->get_mfile().input);
597 newmovie.get()->readonly_mode(true);
598 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
600 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
601 if(will_load_state)
602 newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
603 _movie.input, _movie.projectid);
605 //Count a rerecord.
606 if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
607 core.mlogic->get_rrdata().read_base(rrdata::filename(_movie.projectid), false);
608 core.mlogic->get_rrdata().add((*core.nrrdata)());
610 //Negative return.
611 try {
612 handle_load_core(_movie, portset, will_load_state);
613 } catch(std::bad_alloc& e) {
614 OOM_panic();
615 } catch(std::exception& e) {
616 core.runmode->set_corrupt();
617 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
618 throw;
621 //Set new movie.
622 core.mlogic->set_movie(*(newmovie()), true);
624 //Some fields MUST be taken from movie or one gets desyncs.
625 core.mlogic->get_mfile().is_savestate = _movie.is_savestate;
626 core.mlogic->get_mfile().rtc_second = _movie.rtc_second;
627 core.mlogic->get_mfile().rtc_subsecond = _movie.rtc_subsecond;
628 std::swap(core.mlogic->get_mfile().host_memory, _movie.host_memory);
629 if(!will_load_state)
630 core.mlogic->get_mfile().host_memory.clear();
632 try {
633 //Paint the screen.
634 framebuffer::raw tmp;
635 if(will_load_state) {
636 tmp.load(_movie.screenshot);
637 core.fbuf->redraw_framebuffer(tmp);
638 } else
639 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
640 } catch(...) {
642 delete &_movie;
643 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
644 messages << "Loadstated at earlier point of movie." << std::endl;
647 //Load state from loaded movie file. _movie is consumed.
648 void do_load_state(struct moviefile& _movie, int lmode, bool& used)
650 auto& core = CORE();
651 //Some basic sanity checks.
652 bool current_mode = *core.mlogic ? core.mlogic->get_movie().readonly_mode() : false;
653 bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE;
655 //Load state all branches and load state initial are the same.
656 if(lmode == LOAD_STATE_ALLBRANCH) lmode = LOAD_STATE_INITIAL;
658 //Various checks.
659 if(_movie.force_corrupt)
660 throw std::runtime_error("Movie file invalid");
661 if(!core.rom->is_of_type(_movie.gametype->get_type()))
662 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
663 if(!core.rom->region_compatible_with(_movie.gametype->get_region()))
664 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
665 warn_coretype(_movie.coreversion, *core.rom, will_load_state);
666 warn_roms(_movie, *core.rom, will_load_state);
668 //In certain conditions, trun LOAD_STATE_CURRENT into LOAD_STATE_PRESERVE.
669 if(lmode == LOAD_STATE_CURRENT && current_mode && SET_readonly_load_preserves(*core.settings))
670 lmode = LOAD_STATE_PRESERVE;
671 //If movie file changes, turn LOAD_STATE_CURRENT into LOAD_STATE_RO
672 if(lmode == LOAD_STATE_CURRENT && core.mlogic->get_mfile().projectid != _movie.projectid)
673 lmode = LOAD_STATE_RO;
675 //Handle preserving load specially.
676 if(lmode == LOAD_STATE_PRESERVE) {
677 do_load_state_preserve(_movie);
678 used = true;
679 return;
682 //Create a new movie, and if needed, restore the movie state.
683 temporary_handle<movie> newmovie;
684 newmovie.get()->set_movie_data(_movie.input);
685 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
686 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
687 if(will_load_state)
688 newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
689 NULL, _movie.projectid);
691 //Copy the other branches.
692 if(lmode != LOAD_STATE_INITIAL && core.mlogic->get_mfile().projectid == _movie.projectid) {
693 newmovie.get()->set_movie_data(NULL);
694 auto& oldm = core.mlogic->get_mfile().branches;
695 auto& newm = _movie.branches;
696 auto oldd = core.mlogic->get_mfile().input;
697 auto newd = _movie.input;
698 std::string dflt_name;
699 //What was the old default name?
700 for(auto& i : oldm)
701 if(&i.second == oldd)
702 dflt_name = i.first;
703 //Rename the default to match with old movie if the names differ.
704 if(!newm.count(dflt_name) || &newm[dflt_name] != newd)
705 newm[dflt_name] = *newd;
706 //Copy all other branches.
707 for(auto& i : oldm)
708 if(i.first != dflt_name)
709 newm[i.first] = i.second;
710 //Delete branches that didn't exist.
711 for(auto i = newm.begin(); i != newm.end();) {
712 if(oldm.count(i->first))
713 i++;
714 else
715 i = newm.erase(i);
717 _movie.input = &newm[dflt_name];
718 newmovie.get()->set_movie_data(_movie.input);
721 portctrl::type_set& portset = construct_movie_portset(_movie, *core.rom);
723 temporary_handle<rrdata_set> rrd;
724 bool new_rrdata = false;
725 //Count a rerecord (against new or old movie).
726 if(!*core.mlogic || _movie.projectid != core.mlogic->get_mfile().projectid) {
727 rrd.get()->read_base(rrdata::filename(_movie.projectid), _movie.lazy_project_create);
728 rrd.get()->read(_movie.c_rrdata);
729 rrd.get()->add((*core.nrrdata)());
730 new_rrdata = true;
731 } else {
732 //Unlazy rrdata if needed.
733 if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
734 core.mlogic->get_rrdata().read_base(rrdata::filename(_movie.projectid), false);
735 core.mlogic->get_rrdata().add((*core.nrrdata)());
737 //Negative return.
738 try {
739 core.rom->set_internal_region(_movie.gametype->get_region());
740 handle_load_core(_movie, portset, will_load_state);
741 } catch(std::bad_alloc& e) {
742 OOM_panic();
743 } catch(std::exception& e) {
744 core.runmode->set_corrupt();
745 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
746 throw;
749 //If loaded a movie, clear the is savestate and rrdata.
750 if(!will_load_state) {
751 _movie.is_savestate = false;
752 _movie.host_memory.clear();
755 core.lua2->callback_movie_lost("load");
757 //Copy the data.
758 if(new_rrdata) core.mlogic->set_rrdata(*(rrd()), true);
759 core.mlogic->set_movie(*newmovie(), true);
760 core.mlogic->set_mfile(_movie, true);
761 used = true;
763 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
765 //Activate RW mode if needed.
766 auto& m = core.mlogic->get_movie();
767 if(lmode == LOAD_STATE_RW)
768 m.readonly_mode(false);
769 if(lmode == LOAD_STATE_DEFAULT && !current_mode && m.get_frame_count() <= m.get_current_frame())
770 m.readonly_mode(false);
771 if(lmode == LOAD_STATE_INITIAL && m.get_frame_count() <= m.get_current_frame())
772 m.readonly_mode(false);
773 if(lmode == LOAD_STATE_CURRENT && !current_mode)
774 m.readonly_mode(false);
776 //Paint the screen.
778 framebuffer::raw tmp;
779 if(will_load_state) {
780 tmp.load(_movie.screenshot);
781 core.fbuf->redraw_framebuffer(tmp);
782 } else
783 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
786 core.dispatch->mode_change(m.readonly_mode());
787 print_movie_info(_movie, *core.rom, core.mlogic->get_rrdata());
788 core.dispatch->mbranch_change();
789 set_gameinfo(core.mlogic->get_mfile());
793 void try_request_rom(const std::string& moviefile)
795 auto& core = CORE();
796 moviefile::brief_info info(moviefile);
797 auto sysregs = core_sysregion::find_matching(info.sysregion);
798 rom_request req;
799 req.selected = 0;
800 size_t idx = 0;
801 req.core_guessed = false;
802 for(auto i : sysregs) {
803 //FIXME: Do something with this?
804 //if(i->get_type().get_biosname() != "" && info.hash[1] != "")
805 // has_bios = true;
806 req.cores.push_back(&i->get_type());
807 if(i->get_type().get_core_identifier() == info.corename) {
808 req.selected = idx;
809 req.core_guessed = true;
811 idx++;
813 if(req.cores.empty())
814 throw std::runtime_error("No known core can load movie of type '" + info.sysregion + "'");
815 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
816 req.guessed[i] = false;
817 req.has_slot[i] = (info.hash[i] != "");
818 req.filename[i] = info.hint[i];
819 req.hash[i] = info.hash[i];
820 req.hashxml[i] = info.hashxml[i];
822 try_guess_roms(req);
823 req.canceled = true;
824 graphics_driver_request_rom(req);
825 if(req.canceled)
826 throw std::runtime_error("Canceled loading ROM");
827 //Try to load the ROM using specified core.
828 if(req.selected >= req.cores.size())
829 throw std::runtime_error("Invalid ROM type selected");
830 core_type* selected_core = req.cores[req.selected];
831 loaded_rom newrom(req.filename, selected_core->get_core_identifier(), selected_core->get_iname(), "");
832 *core.rom = newrom;
833 core.dispatch->core_change();
836 //Load state
837 bool do_load_state(const std::string& filename, int lmode)
839 auto& core = CORE();
840 int tmp = -1;
841 std::string filename2 = translate_name_mprefix(filename, tmp, -1);
842 uint64_t origtime = framerate_regulator::get_utime();
843 core.lua2->callback_pre_load(filename2);
844 struct moviefile* mfile = NULL;
845 bool used = false;
846 try {
847 if(core.rom->isnull())
848 try_request_rom(filename2);
849 mfile = new moviefile(filename2, core.rom->get_internal_rom_type());
850 } catch(std::bad_alloc& e) {
851 OOM_panic();
852 } catch(std::exception& e) {
853 platform::error_message(std::string("Can't read movie/savestate: ") + e.what());
854 messages << "Can't read movie/savestate '" << filename2 << "': " << e.what() << std::endl;
855 core.lua2->callback_err_load(filename2);
856 return false;
858 try {
859 do_load_state(*mfile, lmode, used);
860 uint64_t took = framerate_regulator::get_utime() - origtime;
861 messages << "Loaded '" << filename2 << "' in " << took << " microseconds." << std::endl;
862 core.lua2->callback_post_load(filename2, core.mlogic->get_mfile().is_savestate);
863 } catch(std::bad_alloc& e) {
864 OOM_panic();
865 } catch(std::exception& e) {
866 if(!used)
867 delete mfile;
868 platform::error_message(std::string("Can't load movie/savestate: ") + e.what());
869 messages << "Can't load movie/savestate '" << filename2 << "': " << e.what() << std::endl;
870 core.lua2->callback_err_load(filename2);
871 return false;
873 return true;
876 void mainloop_restore_state(const std::vector<char>& state, uint64_t secs, uint64_t ssecs)
878 auto& core = CORE();
879 //Force unlazy rrdata.
880 core.mlogic->get_rrdata().read_base(rrdata::filename(core.mlogic->get_mfile().projectid),
881 false);
882 core.mlogic->get_rrdata().add((*core.nrrdata)());
883 core.mlogic->get_mfile().rtc_second = secs;
884 core.mlogic->get_mfile().rtc_subsecond = ssecs;
885 core.rom->load_core_state(state, true);
888 rrdata::rrdata()
889 : init(false)
893 rrdata_set::instance rrdata::operator()()
895 if(!init)
896 next = rrdata_set::instance(get_random_hexstring(2 * RRDATA_BYTES));
897 init = true;
898 return next++;
901 std::string rrdata::filename(const std::string& projectid)
903 return get_config_path() + "/" + projectid + ".rr";