Tweak format of command help files and do some further command cleanup
[lsnes.git] / src / core / moviedata.cpp
blobb44f9faea648f9dd74b74027047f3f88f1f82169
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 target.romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
236 target.romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
237 target.namehint[i] = core.rom->romimg[i].namehint;
239 target.savestate = core.rom->save_core_state();
240 core.fbuf->get_framebuffer().save(target.screenshot);
241 core.mlogic->get_movie().save_state(target.projectid, target.save_frame,
242 target.lagged_frames, target.pollcounters);
243 target.poll_flag = core.rom->get_pflag();
244 auto prj = core.project->get();
245 if(prj) {
246 target.gamename = prj->gamename;
247 target.authors = prj->authors;
249 target.active_macros = core.controls->get_macro_frames();
250 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
251 core.mlogic->get_rrdata());
252 uint64_t took = framerate_regulator::get_utime() - origtime;
253 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
254 messages << "Saved state " << kind << " '" << filename2 << "' in " << took << " microseconds."
255 << std::endl;
256 core.lua2->callback_post_save(filename2, true);
257 } catch(std::bad_alloc& e) {
258 throw;
259 } catch(std::exception& e) {
260 platform::error_message(std::string("Save failed: ") + e.what());
261 messages << "Save failed: " << e.what() << std::endl;
262 core.lua2->callback_err_save(filename2);
264 last_save = resolve_relative_path(filename2);
265 auto p = core.project->get();
266 if(p) {
267 p->last_save = last_save;
268 p->flush();
272 //Save movie.
273 void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc, std::runtime_error)
275 auto& core = CORE();
276 if(!*core.mlogic || !core.mlogic->get_mfile().gametype) {
277 platform::error_message("Can't save movie without a ROM");
278 messages << "Can't save movie without a ROM" << std::endl;
279 return;
281 auto& target = core.mlogic->get_mfile();
282 std::string filename2 = translate_name_mprefix(filename, binary, 0);
283 core.lua2->callback_pre_save(filename2, false);
284 try {
285 uint64_t origtime = framerate_regulator::get_utime();
286 target.is_savestate = false;
287 auto prj = core.project->get();
288 if(prj) {
289 target.gamename = prj->gamename;
290 target.authors = prj->authors;
292 target.active_macros.clear();
293 target.save(filename2, SET_savecompression(*core.settings), binary > 0,
294 core.mlogic->get_rrdata());
295 uint64_t took = framerate_regulator::get_utime() - origtime;
296 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
297 messages << "Saved movie " << kind << " '" << filename2 << "' in " << took << " microseconds."
298 << std::endl;
299 core.lua2->callback_post_save(filename2, false);
300 } catch(std::bad_alloc& e) {
301 OOM_panic();
302 } catch(std::exception& e) {
303 platform::error_message(std::string("Save failed: ") + e.what());
304 messages << "Save failed: " << e.what() << std::endl;
305 core.lua2->callback_err_save(filename2);
307 last_save = resolve_relative_path(filename2);
308 auto p = core.project->get();
309 if(p) {
310 p->last_save = last_save;
311 p->flush();
315 namespace
317 void populate_volatile_ram(moviefile& mf, std::list<core_vma_info>& vmas)
319 for(auto i : vmas) {
320 //Only regions that are marked as volatile, readwrite not special are initializable.
321 if(!i.volatile_flag || i.readonly || i.special)
322 continue;
323 if(!mf.ramcontent.count(i.name))
324 continue;
325 uint64_t csize = min((uint64_t)mf.ramcontent[i.name].size(), i.size);
326 if(i.backing_ram)
327 memcpy(i.backing_ram, &mf.ramcontent[i.name][0], csize);
328 else
329 for(uint64_t o = 0; o < csize; o++)
330 i.write(o, mf.ramcontent[i.name][o]);
334 std::string format_length(uint64_t mlength)
336 std::ostringstream x;
337 if(mlength >= 3600000ULL) {
338 x << mlength / 3600000ULL << ":";
339 mlength %= 3600000ULL;
341 x << std::setfill('0') << std::setw(2) << mlength / 60000ULL << ":";
342 mlength %= 60000ULL;
343 x << std::setfill('0') << std::setw(2) << mlength / 1000ULL << ".";
344 mlength %= 1000ULL;
345 x << std::setfill('0') << std::setw(3) << mlength;
346 return x.str();
349 void print_movie_info(moviefile& mov, loaded_rom& rom, rrdata_set& rrd)
351 if(!rom.isnull())
352 messages << "ROM Type " << rom.get_hname() << " region " << rom.region_get_hname()
353 << std::endl;
354 std::string len, rerecs;
355 len = format_length(mov.get_movie_length());
356 std::string rerecords = mov.is_savestate ? (stringfmt() << rrd.count()).str() : mov.rerecords;
357 messages << "Rerecords " << rerecords << " length " << format_length(mov.get_movie_length())
358 << " (" << mov.get_frame_count() << " frames)" << std::endl;
359 if(mov.gamename != "")
360 messages << "Game name: " << mov.gamename << std::endl;
361 for(size_t i = 0; i < mov.authors.size(); i++)
362 if(mov.authors[i].second == "")
363 messages << "Author: " << mov.authors[i].first << std::endl;
364 else if(mov.authors[i].first == "")
365 messages << "Author: (" << mov.authors[i].second << ")" << std::endl;
366 else
367 messages << "Author: " << mov.authors[i].first << " ("
368 << mov.authors[i].second << ")" << std::endl;
371 void warn_coretype(const std::string& mov_core, loaded_rom& against, bool loadstate)
373 if(against.isnull())
374 return;
375 std::string rom_core = against.get_core_identifier();
376 if(mov_core == rom_core)
377 return;
378 std::ostringstream x;
379 x << (loadstate ? "Error: " : "Warning: ");
380 x << "Emulator core version mismatch!" << std::endl
381 << "\tCrurrent: " << rom_core << std::endl
382 << "\tMovie: " << mov_core << std::endl;
383 if(loadstate)
384 throw std::runtime_error(x.str());
385 else
386 messages << x.str();
389 void warn_roms(moviefile& mov, loaded_rom& against, bool loadstate)
391 bool rom_ok = true;
392 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
393 if(mov.namehint[i] == "")
394 mov.namehint[i] = against.romimg[i].namehint;
395 if(mov.romimg_sha256[i] == "")
396 mov.romimg_sha256[i] = against.romimg[i].sha_256.read();
397 if(mov.romxml_sha256[i] == "")
398 mov.romxml_sha256[i] = against.romxml[i].sha_256.read();
399 rom_ok = rom_ok & warn_hash_mismatch(mov.romimg_sha256[i], against.romimg[i],
400 (stringfmt() << "ROM #" << (i + 1)).str(), loadstate);
401 rom_ok = rom_ok & warn_hash_mismatch(mov.romxml_sha256[i], against.romxml[i],
402 (stringfmt() << "XML #" << (i + 1)).str(), loadstate);
404 if(!rom_ok)
405 throw std::runtime_error("Incorrect ROM");
408 portctrl::type_set& construct_movie_portset(moviefile& mov, loaded_rom& against)
410 auto ctrldata = against.controllerconfig(mov.settings);
411 return portctrl::type_set::make(ctrldata.ports, ctrldata.portindex());
414 void handle_load_core(moviefile& _movie, portctrl::type_set& portset, bool will_load_state)
416 auto& core = CORE();
417 core.random_seed_value = _movie.movie_rtc_second;
418 if(will_load_state) {
419 //If settings possibly change, reload the ROM.
420 if(!*core.mlogic || core.mlogic->get_mfile().projectid != _movie.projectid)
421 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
422 //Load the savestate and movie state.
423 //Set the core ports in order to avoid port state being reinitialized when loading.
424 core.controls->set_ports(portset);
425 core.rom->load_core_state(_movie.savestate);
426 core.rom->set_pflag(_movie.poll_flag);
427 core.controls->set_macro_frames(_movie.active_macros);
428 } else {
429 //Reload the ROM in order to rewind to the beginning.
430 core.rom->load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
431 //Load the SRAM and volatile RAM. Or anchor savestate if any.
432 core.controls->set_ports(portset);
433 _movie.rtc_second = _movie.movie_rtc_second;
434 _movie.rtc_subsecond = _movie.movie_rtc_subsecond;
435 if(!_movie.anchor_savestate.empty()) {
436 core.rom->load_core_state(_movie.anchor_savestate);
437 } else {
438 core.rom->load_sram(_movie.movie_sram);
439 std::list<core_vma_info> vmas = core.rom->vma_list();
440 populate_volatile_ram(_movie, vmas);
442 core.rom->set_pflag(0);
443 core.controls->set_macro_frames(std::map<std::string, uint64_t>());
448 void do_load_rom() throw(std::bad_alloc, std::runtime_error)
450 auto& core = CORE();
451 bool load_readwrite = !*core.mlogic || !core.mlogic->get_movie().readonly_mode();
452 if(*core.mlogic) {
453 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
454 //If portset or gametype changes, force readwrite with new movie.
455 if(core.mlogic->get_mfile().input->get_types() != portset) load_readwrite = true;
456 if(!core.rom->is_of_type(core.mlogic->get_mfile().gametype->get_type())) load_readwrite = true;
459 if(!load_readwrite) {
460 //Read-only load. This is pretty simple.
461 //Force unlazying of rrdata and count a rerecord.
462 if(core.mlogic->get_rrdata().is_lazy())
463 core.mlogic->get_rrdata().read_base(rrdata::filename(
464 core.mlogic->get_mfile().projectid), false);
465 core.mlogic->get_rrdata().add((*core.nrrdata)());
467 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
469 try {
470 handle_load_core(core.mlogic->get_mfile(), portset, false);
471 core.mlogic->get_mfile().gametype = &core.rom->get_sysregion();
472 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
473 core.mlogic->get_mfile().namehint[i] = core.rom->romimg[i].namehint;
474 core.mlogic->get_mfile().romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
475 core.mlogic->get_mfile().romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
477 core.mlogic->get_mfile().is_savestate = false;
478 core.mlogic->get_mfile().host_memory.clear();
479 core.mlogic->get_movie().reset_state();
480 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
481 core.lua2->callback_do_rewind();
482 } catch(std::bad_alloc& e) {
483 OOM_panic();
484 } catch(std::exception& e) {
485 core.runmode->set_corrupt();
486 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
487 throw;
489 } else {
490 //The more complicated Read-Write case.
491 //We need to create a new movie and movie file.
492 temporary_handle<moviefile> _movie;
493 _movie.get()->force_corrupt = false;
494 _movie.get()->gametype = NULL; //Not yet known.
495 _movie.get()->coreversion = core.rom->get_core_identifier();
496 _movie.get()->projectid = get_random_hexstring(40);
497 _movie.get()->rerecords = "0";
498 _movie.get()->rerecords_mem = 0;
499 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
500 _movie.get()->namehint[i] = core.rom->romimg[i].namehint;
501 _movie.get()->romimg_sha256[i] = core.rom->romimg[i].sha_256.read();
502 _movie.get()->romxml_sha256[i] = core.rom->romxml[i].sha_256.read();
504 _movie.get()->is_savestate = false;
505 _movie.get()->save_frame = 0;
506 _movie.get()->lagged_frames = 0;
507 _movie.get()->poll_flag = false;
508 _movie.get()->movie_rtc_second = _movie.get()->rtc_second = 1000000000ULL;
509 _movie.get()->movie_rtc_subsecond = _movie.get()->rtc_subsecond = 0;
510 _movie.get()->start_paused = false;
511 _movie.get()->lazy_project_create = true;
512 portctrl::type_set& portset2 = construct_movie_portset(*_movie.get(), *core.rom);
513 _movie.get()->input = NULL;
514 _movie.get()->create_default_branch(portset2);
516 //Wrap the input in movie.
517 temporary_handle<movie> newmovie;
518 newmovie.get()->set_movie_data(_movie.get()->input);
519 newmovie.get()->load(_movie.get()->rerecords, _movie.get()->projectid, *(_movie.get()->input));
520 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
521 newmovie.get()->readonly_mode(false);
523 //Force new lazy rrdata and count a rerecord.
524 temporary_handle<rrdata_set> rrd;
525 rrd.get()->read_base(rrdata::filename(_movie.get()->projectid), true);
526 rrd.get()->add((*core.nrrdata)());
527 //Movie data is lost.
528 core.lua2->callback_movie_lost("reload");
529 try {
530 handle_load_core(*_movie.get(), portset2, false);
531 _movie.get()->gametype = &core.rom->get_sysregion();
532 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
533 core.lua2->callback_do_rewind();
534 } catch(std::bad_alloc& e) {
535 OOM_panic();
536 } catch(std::exception& e) {
537 core.runmode->set_corrupt();
538 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
539 throw;
542 //Set up stuff.
543 core.mlogic->set_movie(*(newmovie()), true);
544 core.mlogic->set_mfile(*(_movie()), true);
545 core.mlogic->set_rrdata(*(rrd()), true);
546 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
547 set_gameinfo(core.mlogic->get_mfile());
549 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
550 core.dispatch->mbranch_change();
551 messages << "ROM reloaded." << std::endl;
554 void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
556 auto& core = CORE();
557 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
558 throw std::runtime_error("Can't rewind movie without existing movie");
560 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
562 //Force unlazying of rrdata and count a rerecord.
563 if(core.mlogic->get_rrdata().is_lazy())
564 core.mlogic->get_rrdata().read_base(rrdata::filename(
565 core.mlogic->get_mfile().projectid), false);
566 core.mlogic->get_rrdata().add((*core.nrrdata)());
568 //Enter readonly mode.
569 core.mlogic->get_movie().readonly_mode(true);
570 core.dispatch->mode_change(true);
571 try {
572 handle_load_core(core.mlogic->get_mfile(), portset, false);
573 core.mlogic->get_mfile().is_savestate = false;
574 core.mlogic->get_mfile().host_memory.clear();
575 core.mlogic->get_movie().reset_state();
576 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
577 core.lua2->callback_do_rewind();
578 } catch(std::bad_alloc& e) {
579 OOM_panic();
580 } catch(std::exception& e) {
581 core.runmode->set_corrupt();
582 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
583 throw;
585 messages << "Movie rewound to beginning." << std::endl;
588 //Load state preserving input. Does not do checks.
589 void do_load_state_preserve(struct moviefile& _movie)
591 auto& core = CORE();
592 if(!*core.mlogic || !core.mlogic->get_mfile().gametype)
593 throw std::runtime_error("Can't load movie preserving input without previous movie");
594 if(core.mlogic->get_mfile().projectid != _movie.projectid)
595 throw std::runtime_error("Savestate is from different movie");
597 bool will_load_state = _movie.is_savestate;
598 portctrl::type_set& portset = construct_movie_portset(core.mlogic->get_mfile(), *core.rom);
600 //Construct a new movie sharing the input data.
601 temporary_handle<movie> newmovie;
602 newmovie.get()->set_movie_data(core.mlogic->get_mfile().input);
603 newmovie.get()->readonly_mode(true);
604 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
606 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
607 if(will_load_state)
608 newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
609 _movie.input, _movie.projectid);
611 //Count a rerecord.
612 if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
613 core.mlogic->get_rrdata().read_base(rrdata::filename(_movie.projectid), false);
614 core.mlogic->get_rrdata().add((*core.nrrdata)());
616 //Negative return.
617 try {
618 handle_load_core(_movie, portset, will_load_state);
619 } catch(std::bad_alloc& e) {
620 OOM_panic();
621 } catch(std::exception& e) {
622 core.runmode->set_corrupt();
623 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
624 throw;
627 //Set new movie.
628 core.mlogic->set_movie(*(newmovie()), true);
630 //Some fields MUST be taken from movie or one gets desyncs.
631 core.mlogic->get_mfile().is_savestate = _movie.is_savestate;
632 core.mlogic->get_mfile().rtc_second = _movie.rtc_second;
633 core.mlogic->get_mfile().rtc_subsecond = _movie.rtc_subsecond;
634 std::swap(core.mlogic->get_mfile().host_memory, _movie.host_memory);
635 if(!will_load_state)
636 core.mlogic->get_mfile().host_memory.clear();
638 try {
639 //Paint the screen.
640 framebuffer::raw tmp;
641 if(will_load_state) {
642 tmp.load(_movie.screenshot);
643 core.fbuf->redraw_framebuffer(tmp);
644 } else
645 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
646 } catch(...) {
648 delete &_movie;
649 core.dispatch->mode_change(core.mlogic->get_movie().readonly_mode());
650 messages << "Loadstated at earlier point of movie." << std::endl;
653 //Load state from loaded movie file. _movie is consumed.
654 void do_load_state(struct moviefile& _movie, int lmode, bool& used)
656 auto& core = CORE();
657 //Some basic sanity checks.
658 bool current_mode = *core.mlogic ? core.mlogic->get_movie().readonly_mode() : false;
659 bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE;
661 //Load state all branches and load state initial are the same.
662 if(lmode == LOAD_STATE_ALLBRANCH) lmode = LOAD_STATE_INITIAL;
664 //Various checks.
665 if(_movie.force_corrupt)
666 throw std::runtime_error("Movie file invalid");
667 if(!core.rom->is_of_type(_movie.gametype->get_type()))
668 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
669 if(!core.rom->region_compatible_with(_movie.gametype->get_region()))
670 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
671 warn_coretype(_movie.coreversion, *core.rom, will_load_state);
672 warn_roms(_movie, *core.rom, will_load_state);
674 //In certain conditions, trun LOAD_STATE_CURRENT into LOAD_STATE_PRESERVE.
675 if(lmode == LOAD_STATE_CURRENT && current_mode && SET_readonly_load_preserves(*core.settings))
676 lmode = LOAD_STATE_PRESERVE;
677 //If movie file changes, turn LOAD_STATE_CURRENT into LOAD_STATE_RO
678 if(lmode == LOAD_STATE_CURRENT && core.mlogic->get_mfile().projectid != _movie.projectid)
679 lmode = LOAD_STATE_RO;
681 //Handle preserving load specially.
682 if(lmode == LOAD_STATE_PRESERVE) {
683 do_load_state_preserve(_movie);
684 used = true;
685 return;
688 //Create a new movie, and if needed, restore the movie state.
689 temporary_handle<movie> newmovie;
690 newmovie.get()->set_movie_data(_movie.input);
691 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
692 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
693 if(will_load_state)
694 newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
695 NULL, _movie.projectid);
697 //Copy the other branches.
698 if(lmode != LOAD_STATE_INITIAL && core.mlogic->get_mfile().projectid == _movie.projectid) {
699 newmovie.get()->set_movie_data(NULL);
700 auto& oldm = core.mlogic->get_mfile().branches;
701 auto& newm = _movie.branches;
702 auto oldd = core.mlogic->get_mfile().input;
703 auto newd = _movie.input;
704 std::string dflt_name;
705 //What was the old default name?
706 for(auto& i : oldm)
707 if(&i.second == oldd)
708 dflt_name = i.first;
709 //Rename the default to match with old movie if the names differ.
710 if(!newm.count(dflt_name) || &newm[dflt_name] != newd)
711 newm[dflt_name] = *newd;
712 //Copy all other branches.
713 for(auto& i : oldm)
714 if(i.first != dflt_name)
715 newm[i.first] = i.second;
716 //Delete branches that didn't exist.
717 for(auto i = newm.begin(); i != newm.end();) {
718 if(oldm.count(i->first))
719 i++;
720 else
721 i = newm.erase(i);
723 _movie.input = &newm[dflt_name];
724 newmovie.get()->set_movie_data(_movie.input);
727 portctrl::type_set& portset = construct_movie_portset(_movie, *core.rom);
729 temporary_handle<rrdata_set> rrd;
730 bool new_rrdata = false;
731 //Count a rerecord (against new or old movie).
732 if(!*core.mlogic || _movie.projectid != core.mlogic->get_mfile().projectid) {
733 rrd.get()->read_base(rrdata::filename(_movie.projectid), _movie.lazy_project_create);
734 rrd.get()->read(_movie.c_rrdata);
735 rrd.get()->add((*core.nrrdata)());
736 new_rrdata = true;
737 } else {
738 //Unlazy rrdata if needed.
739 if(core.mlogic->get_rrdata().is_lazy() && !_movie.lazy_project_create)
740 core.mlogic->get_rrdata().read_base(rrdata::filename(_movie.projectid), false);
741 core.mlogic->get_rrdata().add((*core.nrrdata)());
743 //Negative return.
744 try {
745 core.rom->set_internal_region(_movie.gametype->get_region());
746 handle_load_core(_movie, portset, will_load_state);
747 } catch(std::bad_alloc& e) {
748 OOM_panic();
749 } catch(std::exception& e) {
750 core.runmode->set_corrupt();
751 core.fbuf->redraw_framebuffer(emu_framebuffer::screen_corrupt, true);
752 throw;
755 //If loaded a movie, clear the is savestate and rrdata.
756 if(!will_load_state) {
757 _movie.is_savestate = false;
758 _movie.host_memory.clear();
761 core.lua2->callback_movie_lost("load");
763 //Copy the data.
764 if(new_rrdata) core.mlogic->set_rrdata(*(rrd()), true);
765 core.mlogic->set_movie(*newmovie(), true);
766 core.mlogic->set_mfile(_movie, true);
767 used = true;
769 set_mprefix(get_mprefix_for_project(core.mlogic->get_mfile().projectid));
771 //Activate RW mode if needed.
772 auto& m = core.mlogic->get_movie();
773 if(lmode == LOAD_STATE_RW)
774 m.readonly_mode(false);
775 if(lmode == LOAD_STATE_DEFAULT && !current_mode && m.get_frame_count() <= m.get_current_frame())
776 m.readonly_mode(false);
777 if(lmode == LOAD_STATE_INITIAL && m.get_frame_count() <= m.get_current_frame())
778 m.readonly_mode(false);
779 if(lmode == LOAD_STATE_CURRENT && !current_mode)
780 m.readonly_mode(false);
782 //Paint the screen.
784 framebuffer::raw tmp;
785 if(will_load_state) {
786 tmp.load(_movie.screenshot);
787 core.fbuf->redraw_framebuffer(tmp);
788 } else
789 core.fbuf->redraw_framebuffer(core.rom->draw_cover());
792 core.dispatch->mode_change(m.readonly_mode());
793 print_movie_info(_movie, *core.rom, core.mlogic->get_rrdata());
794 core.dispatch->mbranch_change();
795 set_gameinfo(core.mlogic->get_mfile());
799 void try_request_rom(const std::string& moviefile)
801 auto& core = CORE();
802 moviefile::brief_info info(moviefile);
803 auto sysregs = core_sysregion::find_matching(info.sysregion);
804 rom_request req;
805 req.selected = 0;
806 size_t idx = 0;
807 req.core_guessed = false;
808 for(auto i : sysregs) {
809 //FIXME: Do something with this?
810 //if(i->get_type().get_biosname() != "" && info.hash[1] != "")
811 // has_bios = true;
812 req.cores.push_back(&i->get_type());
813 if(i->get_type().get_core_identifier() == info.corename) {
814 req.selected = idx;
815 req.core_guessed = true;
817 idx++;
819 if(req.cores.empty())
820 throw std::runtime_error("No known core can load movie of type '" + info.sysregion + "'");
821 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
822 req.guessed[i] = false;
823 req.has_slot[i] = (info.hash[i] != "");
824 req.filename[i] = info.hint[i];
825 req.hash[i] = info.hash[i];
826 req.hashxml[i] = info.hashxml[i];
828 try_guess_roms(req);
829 req.canceled = true;
830 graphics_driver_request_rom(req);
831 if(req.canceled)
832 throw std::runtime_error("Canceled loading ROM");
833 //Try to load the ROM using specified core.
834 if(req.selected >= req.cores.size())
835 throw std::runtime_error("Invalid ROM type selected");
836 core_type* selected_core = req.cores[req.selected];
837 loaded_rom newrom(req.filename, selected_core->get_core_identifier(), selected_core->get_iname(), "");
838 *core.rom = newrom;
839 core.dispatch->core_change();
842 //Load state
843 bool do_load_state(const std::string& filename, int lmode)
845 auto& core = CORE();
846 int tmp = -1;
847 std::string filename2 = translate_name_mprefix(filename, tmp, -1);
848 uint64_t origtime = framerate_regulator::get_utime();
849 core.lua2->callback_pre_load(filename2);
850 struct moviefile* mfile = NULL;
851 bool used = false;
852 try {
853 if(core.rom->isnull())
854 try_request_rom(filename2);
855 mfile = new moviefile(filename2, core.rom->get_internal_rom_type());
856 } catch(std::bad_alloc& e) {
857 OOM_panic();
858 } catch(std::exception& e) {
859 platform::error_message(std::string("Can't read movie/savestate: ") + e.what());
860 messages << "Can't read movie/savestate '" << filename2 << "': " << e.what() << std::endl;
861 core.lua2->callback_err_load(filename2);
862 return false;
864 try {
865 do_load_state(*mfile, lmode, used);
866 uint64_t took = framerate_regulator::get_utime() - origtime;
867 messages << "Loaded '" << filename2 << "' in " << took << " microseconds." << std::endl;
868 core.lua2->callback_post_load(filename2, core.mlogic->get_mfile().is_savestate);
869 } catch(std::bad_alloc& e) {
870 OOM_panic();
871 } catch(std::exception& e) {
872 if(!used)
873 delete mfile;
874 platform::error_message(std::string("Can't load movie/savestate: ") + e.what());
875 messages << "Can't load movie/savestate '" << filename2 << "': " << e.what() << std::endl;
876 core.lua2->callback_err_load(filename2);
877 return false;
879 return true;
882 void mainloop_restore_state(const std::vector<char>& state, uint64_t secs, uint64_t ssecs)
884 auto& core = CORE();
885 //Force unlazy rrdata.
886 core.mlogic->get_rrdata().read_base(rrdata::filename(core.mlogic->get_mfile().projectid),
887 false);
888 core.mlogic->get_rrdata().add((*core.nrrdata)());
889 core.mlogic->get_mfile().rtc_second = secs;
890 core.mlogic->get_mfile().rtc_subsecond = ssecs;
891 core.rom->load_core_state(state, true);
894 rrdata::rrdata()
895 : init(false)
899 rrdata_set::instance rrdata::operator()()
901 if(!init)
902 next = rrdata_set::instance(get_random_hexstring(2 * RRDATA_BYTES));
903 init = true;
904 return next++;
907 std::string rrdata::filename(const std::string& projectid)
909 return get_config_path() + "/" + projectid + ".rr";