Fix SA1 open bus
[lsnes.git] / src / core / moviedata.cpp
blob651b7994a92a1f8c41b3f6b074499c5708d7155b
1 #include "lsnes.hpp"
3 #include "core/command.hpp"
4 #include "core/controller.hpp"
5 #include "core/dispatch.hpp"
6 #include "core/framebuffer.hpp"
7 #include "core/framerate.hpp"
8 #include "core/misc.hpp"
9 #include "core/mainloop.hpp"
10 #include "core/movie.hpp"
11 #include "core/moviedata.hpp"
12 #include "core/project.hpp"
13 #include "core/romguess.hpp"
14 #include "core/rrdata.hpp"
15 #include "core/settings.hpp"
16 #include "lua/lua.hpp"
17 #include "library/directory.hpp"
18 #include "library/string.hpp"
19 #include "library/minmax.hpp"
20 #include "library/temporary_handle.hpp"
21 #include "interface/romtype.hpp"
23 #include <iomanip>
24 #include <fstream>
26 struct loaded_rom our_rom;
27 bool system_corrupt;
28 std::string last_save;
29 void update_movie_state();
31 namespace
33 settingvar::variable<settingvar::model_int<0, 9>> savecompression(lsnes_vset, "savecompression",
34 "Movie‣Saving‣Compression", 7);
35 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> readonly_load_preserves(lsnes_vset,
36 "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&> dump_coresave(lsnes_cmd, "dump-coresave", "Dump bsnes core state",
51 "Syntax: dump-coresave <name>\nDumps core save to <name>\n",
52 [](const std::string& name) throw(std::bad_alloc, std::runtime_error) {
53 auto x = our_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)
83 threads::alock h(mprefix_lock);
84 mprefix_valid = (pfx != "");
85 mprefix = pfx;
87 update_movie_state();
90 std::string get_mprefix_for_project(const std::string& prjid)
92 std::string filename = get_config_path() + "/" + safe_filename(prjid) + ".pfx";
93 std::ifstream strm(filename);
94 if(!strm)
95 return "";
96 std::string pfx;
97 std::getline(strm, pfx);
98 return strip_CR(pfx);
101 class _lsnes_pflag_handler : public movie::poll_flag
103 public:
104 ~_lsnes_pflag_handler()
107 int get_pflag()
109 return our_rom.rtype->get_pflag();
111 void set_pflag(int flag)
113 our_rom.rtype->set_pflag(flag);
115 } lsnes_pflag_handler;
118 std::string get_mprefix_for_project()
120 return get_mprefix_for_project(movb ? movb.get_mfile().projectid : "");
123 void set_mprefix_for_project(const std::string& prjid, const std::string& pfx)
125 std::string filename = get_config_path() + "/" + safe_filename(prjid) + ".pfx";
126 std::ofstream strm(filename);
127 strm << pfx << std::endl;
130 void set_mprefix_for_project(const std::string& pfx)
132 set_mprefix_for_project(movb ? movb.get_mfile().projectid : "", pfx);
133 set_mprefix(pfx);
136 std::string translate_name_mprefix(std::string original, int& binary, int save)
138 auto p = project_get();
139 regex_results r = regex("\\$SLOT:(.*)", original);
140 if(r) {
141 if(binary < 0)
142 binary = jukebox_dflt_binary ? 1 : 0;
143 if(p) {
144 uint64_t branch = p->get_current_branch();
145 std::string branch_str;
146 std::string filename;
147 if(branch) branch_str = (stringfmt() << "--" << branch).str();
148 filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
149 while(save < 0 && branch) {
150 if(zip::file_exists(filename))
151 break;
152 branch = p->get_parent_branch(branch);
153 branch_str = branch ? ((stringfmt() << "--" << branch).str()) : "";
154 filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
156 return filename;
157 } else {
158 std::string pprf = lsnes_vset["slotpath"].str() + "/";
159 return pprf + get_mprefix() + r[1] + ".lsmv";
161 } else {
162 if(binary < 0)
163 binary = (save ? save_dflt_binary : movie_dflt_binary) ? 1 : 0;
164 return original;
168 std::pair<std::string, std::string> split_author(const std::string& author) throw(std::bad_alloc,
169 std::runtime_error)
171 std::string _author = author;
172 std::string fullname;
173 std::string nickname;
174 size_t split = _author.find_first_of("|");
175 if(split >= _author.length()) {
176 fullname = _author;
177 } else {
178 fullname = _author.substr(0, split);
179 nickname = _author.substr(split + 1);
181 if(fullname == "" && nickname == "")
182 throw std::runtime_error("Bad author name");
183 return std::make_pair(fullname, nickname);
186 //Resolve relative path.
187 std::string resolve_relative_path(const std::string& path)
189 try {
190 return get_absolute_path(path);
191 } catch(...) {
192 return path;
196 //Save state.
197 void do_save_state(const std::string& filename, int binary) throw(std::bad_alloc,
198 std::runtime_error)
200 if(!movb || !movb.get_mfile().gametype) {
201 platform::error_message("Can't save movie without a ROM");
202 messages << "Can't save movie without a ROM" << std::endl;
203 return;
205 auto& target = movb.get_mfile();
206 std::string filename2 = translate_name_mprefix(filename, binary, 1);
207 lua_callback_pre_save(filename2, true);
208 try {
209 uint64_t origtime = get_utime();
210 target.is_savestate = true;
211 target.sram = our_rom.rtype->save_sram();
212 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
213 target.romimg_sha256[i] = our_rom.romimg[i].sha_256.read();
214 target.romxml_sha256[i] = our_rom.romxml[i].sha_256.read();
215 target.namehint[i] = our_rom.romimg[i].namehint;
217 target.savestate = our_rom.save_core_state();
218 get_framebuffer().save(target.screenshot);
219 movb.get_movie().save_state(target.projectid, target.save_frame, target.lagged_frames,
220 target.pollcounters);
221 target.poll_flag = our_rom.rtype->get_pflag();
222 auto prj = project_get();
223 if(prj) {
224 target.gamename = prj->gamename;
225 target.authors = prj->authors;
227 target.active_macros = controls.get_macro_frames();
228 target.save(filename2, savecompression, binary > 0, movb.get_rrdata());
229 uint64_t took = get_utime() - origtime;
230 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
231 messages << "Saved state " << kind << " '" << filename2 << "' in " << took << " microseconds."
232 << std::endl;
233 lua_callback_post_save(filename2, true);
234 } catch(std::bad_alloc& e) {
235 throw;
236 } catch(std::exception& e) {
237 platform::error_message(std::string("Save failed: ") + e.what());
238 messages << "Save failed: " << e.what() << std::endl;
239 lua_callback_err_save(filename2);
241 last_save = resolve_relative_path(filename2);
242 auto p = project_get();
243 if(p) {
244 p->last_save = last_save;
245 p->flush();
249 //Save movie.
250 void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc, std::runtime_error)
252 if(!movb || !movb.get_mfile().gametype) {
253 platform::error_message("Can't save movie without a ROM");
254 messages << "Can't save movie without a ROM" << std::endl;
255 return;
257 auto& target = movb.get_mfile();
258 std::string filename2 = translate_name_mprefix(filename, binary, 0);
259 lua_callback_pre_save(filename2, false);
260 try {
261 uint64_t origtime = get_utime();
262 target.is_savestate = false;
263 auto prj = project_get();
264 if(prj) {
265 target.gamename = prj->gamename;
266 target.authors = prj->authors;
268 target.active_macros.clear();
269 target.save(filename2, savecompression, binary > 0, movb.get_rrdata());
270 uint64_t took = get_utime() - origtime;
271 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
272 messages << "Saved movie " << kind << " '" << filename2 << "' in " << took << " microseconds."
273 << std::endl;
274 lua_callback_post_save(filename2, false);
275 } catch(std::bad_alloc& e) {
276 OOM_panic();
277 } catch(std::exception& e) {
278 platform::error_message(std::string("Save failed: ") + e.what());
279 messages << "Save failed: " << e.what() << std::endl;
280 lua_callback_err_save(filename2);
282 last_save = resolve_relative_path(filename2);
283 auto p = project_get();
284 if(p) {
285 p->last_save = last_save;
286 p->flush();
290 extern time_t random_seed_value;
292 namespace
294 void populate_volatile_ram(moviefile& mf, std::list<core_vma_info>& vmas)
296 for(auto i : vmas) {
297 //Only regions that are marked as volatile, readwrite not special are initializable.
298 if(!i.volatile_flag || i.readonly || i.special)
299 continue;
300 if(!mf.ramcontent.count(i.name))
301 continue;
302 uint64_t csize = min((uint64_t)mf.ramcontent[i.name].size(), i.size);
303 if(i.backing_ram)
304 memcpy(i.backing_ram, &mf.ramcontent[i.name][0], csize);
305 else
306 for(uint64_t o = 0; o < csize; o++)
307 i.write(o, mf.ramcontent[i.name][o]);
311 std::string format_length(uint64_t mlength)
313 std::ostringstream x;
314 if(mlength >= 3600000ULL) {
315 x << mlength / 3600000ULL << ":";
316 mlength %= 3600000ULL;
318 x << std::setfill('0') << std::setw(2) << mlength / 60000ULL << ":";
319 mlength %= 60000ULL;
320 x << std::setfill('0') << std::setw(2) << mlength / 1000ULL << ".";
321 mlength %= 1000ULL;
322 x << std::setfill('0') << std::setw(3) << mlength;
323 return x.str();
326 void print_movie_info(moviefile& mov, loaded_rom& rom, rrdata_set& rrd)
328 if(rom.rtype)
329 messages << "ROM Type " << rom.rtype->get_hname() << " region " << rom.region->get_hname()
330 << std::endl;
331 std::string len, rerecs;
332 len = format_length(mov.get_movie_length());
333 std::string rerecords = mov.is_savestate ? (stringfmt() << rrd.count()).str() : mov.rerecords;
334 messages << "Rerecords " << rerecords << " length " << format_length(mov.get_movie_length())
335 << " (" << mov.get_frame_count() << " frames)" << std::endl;
336 if(mov.gamename != "")
337 messages << "Game name: " << mov.gamename << std::endl;
338 for(size_t i = 0; i < mov.authors.size(); i++)
339 if(mov.authors[i].second == "")
340 messages << "Author: " << mov.authors[i].first << std::endl;
341 else if(mov.authors[i].first == "")
342 messages << "Author: (" << mov.authors[i].second << ")" << std::endl;
343 else
344 messages << "Author: " << mov.authors[i].first << " ("
345 << mov.authors[i].second << ")" << std::endl;
348 void warn_coretype(const std::string& mov_core, loaded_rom& against, bool loadstate)
350 if(!against.rtype)
351 return;
352 std::string rom_core = against.rtype->get_core_identifier();
353 if(mov_core == rom_core)
354 return;
355 std::ostringstream x;
356 x << (loadstate ? "Error: " : "Warning: ");
357 x << "Emulator core version mismatch!" << std::endl
358 << "\tCrurrent: " << rom_core << std::endl
359 << "\tMovie: " << mov_core << std::endl;
360 if(loadstate)
361 throw std::runtime_error(x.str());
362 else
363 messages << x.str();
366 void warn_roms(moviefile& mov, loaded_rom& against, bool loadstate)
368 bool rom_ok = true;
369 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
370 if(mov.namehint[i] == "")
371 mov.namehint[i] = against.romimg[i].namehint;
372 if(mov.romimg_sha256[i] == "")
373 mov.romimg_sha256[i] = against.romimg[i].sha_256.read();
374 if(mov.romxml_sha256[i] == "")
375 mov.romxml_sha256[i] = against.romxml[i].sha_256.read();
376 rom_ok = rom_ok & warn_hash_mismatch(mov.romimg_sha256[i], against.romimg[i],
377 (stringfmt() << "ROM #" << (i + 1)).str(), loadstate);
378 rom_ok = rom_ok & warn_hash_mismatch(mov.romxml_sha256[i], against.romxml[i],
379 (stringfmt() << "XML #" << (i + 1)).str(), loadstate);
381 if(!rom_ok)
382 throw std::runtime_error("Incorrect ROM");
385 port_type_set& construct_movie_portset(moviefile& mov, loaded_rom& against)
387 auto ctrldata = against.rtype->controllerconfig(mov.settings);
388 return port_type_set::make(ctrldata.ports, ctrldata.portindex());
391 void handle_load_core(moviefile& _movie, port_type_set& portset, bool will_load_state)
393 random_seed_value = _movie.movie_rtc_second;
394 if(will_load_state) {
395 //If settings possibly change, reload the ROM.
396 if(!movb || movb.get_mfile().projectid != _movie.projectid)
397 our_rom.load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
398 //Load the savestate and movie state.
399 //Set the core ports in order to avoid port state being reinitialized when loading.
400 controls.set_ports(portset);
401 our_rom.load_core_state(_movie.savestate);
402 our_rom.rtype->set_pflag(_movie.poll_flag);
403 controls.set_macro_frames(_movie.active_macros);
404 } else {
405 //Reload the ROM in order to rewind to the beginning.
406 our_rom.load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
407 //Load the SRAM and volatile RAM. Or anchor savestate if any.
408 controls.set_ports(portset);
409 _movie.rtc_second = _movie.movie_rtc_second;
410 _movie.rtc_subsecond = _movie.movie_rtc_subsecond;
411 if(!_movie.anchor_savestate.empty()) {
412 our_rom.load_core_state(_movie.anchor_savestate);
413 } else {
414 our_rom.rtype->load_sram(_movie.movie_sram);
415 std::list<core_vma_info> vmas = our_rom.rtype->vma_list();
416 populate_volatile_ram(_movie, vmas);
418 our_rom.rtype->set_pflag(0);
419 controls.set_macro_frames(std::map<std::string, uint64_t>());
424 void do_load_rom() throw(std::bad_alloc, std::runtime_error)
426 bool load_readwrite = !movb || !movb.get_movie().readonly_mode();
427 if(movb) {
428 port_type_set& portset = construct_movie_portset(movb.get_mfile(), our_rom);
429 //If portset or gametype changes, force readwrite with new movie.
430 if(movb.get_mfile().input->get_types() != portset) load_readwrite = true;
431 if(our_rom.rtype != &movb.get_mfile().gametype->get_type()) load_readwrite = true;
434 if(!load_readwrite) {
435 //Read-only load. This is pretty simple.
436 //Force unlazying of rrdata and count a rerecord.
437 if(movb.get_rrdata().is_lazy())
438 movb.get_rrdata().read_base(rrdata_filename(movb.get_mfile().projectid), false);
439 movb.get_rrdata().add(next_rrdata());
441 port_type_set& portset = construct_movie_portset(movb.get_mfile(), our_rom);
443 try {
444 handle_load_core(movb.get_mfile(), portset, false);
445 movb.get_mfile().gametype = &our_rom.rtype->combine_region(*our_rom.region);
446 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
447 movb.get_mfile().namehint[i] = our_rom.romimg[i].namehint;
448 movb.get_mfile().romimg_sha256[i] = our_rom.romimg[i].sha_256.read();
449 movb.get_mfile().romxml_sha256[i] = our_rom.romxml[i].sha_256.read();
451 movb.get_mfile().is_savestate = false;
452 movb.get_mfile().host_memory.clear();
453 movb.get_movie().reset_state();
454 redraw_framebuffer(our_rom.rtype->draw_cover());
455 lua_callback_do_rewind();
456 } catch(std::bad_alloc& e) {
457 OOM_panic();
458 } catch(std::exception& e) {
459 system_corrupt = true;
460 redraw_framebuffer(screen_corrupt, true);
461 throw;
463 } else {
464 //The more complicated Read-Write case.
465 //We need to create a new movie and movie file.
466 temporary_handle<moviefile> _movie;
467 _movie.get()->force_corrupt = false;
468 _movie.get()->gametype = NULL; //Not yet known.
469 _movie.get()->coreversion = our_rom.rtype->get_core_identifier();
470 _movie.get()->projectid = get_random_hexstring(40);
471 _movie.get()->rerecords = "0";
472 _movie.get()->rerecords_mem = 0;
473 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
474 _movie.get()->namehint[i] = our_rom.romimg[i].namehint;
475 _movie.get()->romimg_sha256[i] = our_rom.romimg[i].sha_256.read();
476 _movie.get()->romxml_sha256[i] = our_rom.romxml[i].sha_256.read();
478 _movie.get()->is_savestate = false;
479 _movie.get()->save_frame = 0;
480 _movie.get()->lagged_frames = 0;
481 _movie.get()->poll_flag = false;
482 _movie.get()->movie_rtc_second = _movie.get()->rtc_second = 1000000000ULL;
483 _movie.get()->movie_rtc_subsecond = _movie.get()->rtc_subsecond = 0;
484 _movie.get()->start_paused = false;
485 _movie.get()->lazy_project_create = true;
486 port_type_set& portset2 = construct_movie_portset(*_movie.get(), our_rom);
487 _movie.get()->input = NULL;
488 _movie.get()->create_default_branch(portset2);
490 //Wrap the input in movie.
491 temporary_handle<movie> newmovie;
492 newmovie.get()->set_movie_data(_movie.get()->input);
493 newmovie.get()->load(_movie.get()->rerecords, _movie.get()->projectid, *(_movie.get()->input));
494 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
495 newmovie.get()->readonly_mode(false);
497 //Force new lazy rrdata and count a rerecord.
498 temporary_handle<rrdata_set> rrd;
499 rrd.get()->read_base(rrdata_filename(_movie.get()->projectid), true);
500 rrd.get()->add(next_rrdata());
501 //Movie data is lost.
502 lua_callback_movie_lost("reload");
503 try {
504 handle_load_core(*_movie.get(), portset2, false);
505 _movie.get()->gametype = &our_rom.rtype->combine_region(*our_rom.region);
506 redraw_framebuffer(our_rom.rtype->draw_cover());
507 lua_callback_do_rewind();
508 } catch(std::bad_alloc& e) {
509 OOM_panic();
510 } catch(std::exception& e) {
511 system_corrupt = true;
512 redraw_framebuffer(screen_corrupt, true);
513 throw;
516 //Set up stuff.
517 movb.set_movie(*(newmovie()), true);
518 movb.set_mfile(*(_movie()), true);
519 movb.set_rrdata(*(rrd()), true);
520 set_mprefix(get_mprefix_for_project(movb.get_mfile().projectid));
522 notify_mode_change(movb.get_movie().readonly_mode());
523 notify_mbranch_change();
524 messages << "ROM reloaded." << std::endl;
527 void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
529 if(!movb || !movb.get_mfile().gametype)
530 throw std::runtime_error("Can't rewind movie without existing movie");
532 port_type_set& portset = construct_movie_portset(movb.get_mfile(), our_rom);
534 //Force unlazying of rrdata and count a rerecord.
535 if(movb.get_rrdata().is_lazy())
536 movb.get_rrdata().read_base(rrdata_filename(movb.get_mfile().projectid), false);
537 movb.get_rrdata().add(next_rrdata());
539 //Enter readonly mode.
540 movb.get_movie().readonly_mode(true);
541 notify_mode_change(true);
542 try {
543 handle_load_core(movb.get_mfile(), portset, false);
544 movb.get_mfile().is_savestate = false;
545 movb.get_mfile().host_memory.clear();
546 movb.get_movie().reset_state();
547 redraw_framebuffer(our_rom.rtype->draw_cover());
548 lua_callback_do_rewind();
549 } catch(std::bad_alloc& e) {
550 OOM_panic();
551 } catch(std::exception& e) {
552 system_corrupt = true;
553 redraw_framebuffer(screen_corrupt, true);
554 throw;
556 messages << "Movie rewound to beginning." << std::endl;
559 //Load state preserving input. Does not do checks.
560 void do_load_state_preserve(struct moviefile& _movie)
562 if(!movb || !movb.get_mfile().gametype)
563 throw std::runtime_error("Can't load movie preserving input without previous movie");
564 if(movb.get_mfile().projectid != _movie.projectid)
565 throw std::runtime_error("Savestate is from different movie");
567 bool will_load_state = _movie.is_savestate;
568 port_type_set& portset = construct_movie_portset(movb.get_mfile(), our_rom);
570 //Construct a new movie sharing the input data.
571 temporary_handle<movie> newmovie;
572 newmovie.get()->set_movie_data(movb.get_mfile().input);
573 newmovie.get()->readonly_mode(true);
574 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
576 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
577 if(will_load_state)
578 newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
579 _movie.input, _movie.projectid);
581 //Count a rerecord.
582 if(movb.get_rrdata().is_lazy() && !_movie.lazy_project_create)
583 movb.get_rrdata().read_base(rrdata_filename(_movie.projectid), false);
584 movb.get_rrdata().add(next_rrdata());
586 //Negative return.
587 try {
588 handle_load_core(_movie, portset, will_load_state);
589 } catch(std::bad_alloc& e) {
590 OOM_panic();
591 } catch(std::exception& e) {
592 system_corrupt = true;
593 redraw_framebuffer(screen_corrupt, true);
594 throw;
597 //Set new movie.
598 movb.set_movie(*(newmovie()), true);
600 //Some fields MUST be taken from movie or one gets desyncs.
601 movb.get_mfile().is_savestate = _movie.is_savestate;
602 movb.get_mfile().rtc_second = _movie.rtc_second;
603 movb.get_mfile().rtc_subsecond = _movie.rtc_subsecond;
604 std::swap(movb.get_mfile().host_memory, _movie.host_memory);
605 if(!will_load_state)
606 movb.get_mfile().host_memory.clear();
608 try {
609 //Paint the screen.
610 framebuffer::raw tmp;
611 if(will_load_state) {
612 tmp.load(_movie.screenshot);
613 redraw_framebuffer(tmp);
614 } else
615 redraw_framebuffer(our_rom.rtype->draw_cover());
616 } catch(...) {
618 delete &_movie;
619 notify_mode_change(movb.get_movie().readonly_mode());
620 messages << "Loadstated at earlier point of movie." << std::endl;
623 //Load state from loaded movie file. _movie is consumed.
624 void do_load_state(struct moviefile& _movie, int lmode, bool& used)
626 //Some basic sanity checks.
627 bool current_mode = movb ? movb.get_movie().readonly_mode() : false;
628 bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE;
630 //Load state all branches and load state initial are the same.
631 if(lmode == LOAD_STATE_ALLBRANCH) lmode = LOAD_STATE_INITIAL;
633 //Various checks.
634 if(_movie.force_corrupt)
635 throw std::runtime_error("Movie file invalid");
636 if(&(_movie.gametype->get_type()) != our_rom.rtype)
637 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
638 if(our_rom.orig_region && !our_rom.orig_region->compatible_with(_movie.gametype->get_region()))
639 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
640 warn_coretype(_movie.coreversion, our_rom, will_load_state);
641 warn_roms(_movie, our_rom, will_load_state);
643 //In certain conditions, trun LOAD_STATE_CURRENT into LOAD_STATE_PRESERVE.
644 if(lmode == LOAD_STATE_CURRENT && current_mode && readonly_load_preserves)
645 lmode = LOAD_STATE_PRESERVE;
647 //Handle preserving load specially.
648 if(lmode == LOAD_STATE_PRESERVE) {
649 do_load_state_preserve(_movie);
650 used = true;
651 return;
654 //Create a new movie, and if needed, restore the movie state.
655 temporary_handle<movie> newmovie;
656 newmovie.get()->set_movie_data(_movie.input);
657 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
658 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
659 if(will_load_state)
660 newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
661 NULL, _movie.projectid);
663 //Copy the other branches.
664 if(lmode != LOAD_STATE_INITIAL && movb.get_mfile().projectid == _movie.projectid) {
665 newmovie.get()->set_movie_data(NULL);
666 auto& oldm = movb.get_mfile().branches;
667 auto& newm = _movie.branches;
668 auto oldd = movb.get_mfile().input;
669 auto newd = _movie.input;
670 std::string dflt_name;
671 //What was the old default name?
672 for(auto& i : oldm)
673 if(&i.second == oldd)
674 dflt_name = i.first;
675 //Rename the default to match with old movie if the names differ.
676 if(!newm.count(dflt_name) || &newm[dflt_name] != newd)
677 newm[dflt_name] = *newd;
678 //Copy all other branches.
679 for(auto& i : oldm)
680 if(i.first != dflt_name)
681 newm[i.first] = i.second;
682 //Delete branches that didn't exist.
683 for(auto i = newm.begin(); i != newm.end();) {
684 if(oldm.count(i->first))
685 i++;
686 else
687 i = newm.erase(i);
689 _movie.input = &newm[dflt_name];
690 newmovie.get()->set_movie_data(_movie.input);
693 port_type_set& portset = construct_movie_portset(_movie, our_rom);
695 temporary_handle<rrdata_set> rrd;
696 bool new_rrdata = false;
697 //Count a rerecord (against new or old movie).
698 if(!movb || _movie.projectid != movb.get_mfile().projectid) {
699 rrd.get()->read_base(rrdata_filename(_movie.projectid), _movie.lazy_project_create);
700 rrd.get()->read(_movie.c_rrdata);
701 rrd.get()->add(next_rrdata());
702 new_rrdata = true;
703 } else {
704 //Unlazy rrdata if needed.
705 if(movb.get_rrdata().is_lazy() && !_movie.lazy_project_create)
706 movb.get_rrdata().read_base(rrdata_filename(_movie.projectid), false);
707 movb.get_rrdata().add(next_rrdata());
709 //Negative return.
710 try {
711 our_rom.region = _movie.gametype ? &(_movie.gametype->get_region()) : NULL;
712 handle_load_core(_movie, portset, will_load_state);
713 } catch(std::bad_alloc& e) {
714 OOM_panic();
715 } catch(std::exception& e) {
716 system_corrupt = true;
717 redraw_framebuffer(screen_corrupt, true);
718 throw;
721 //If loaded a movie, clear the is savestate and rrdata.
722 if(!will_load_state) {
723 _movie.is_savestate = false;
724 _movie.host_memory.clear();
727 lua_callback_movie_lost("load");
729 //Copy the data.
730 if(new_rrdata) movb.set_rrdata(*(rrd()), true);
731 movb.set_movie(*newmovie(), true);
732 movb.set_mfile(_movie, true);
733 used = true;
735 set_mprefix(get_mprefix_for_project(movb.get_mfile().projectid));
737 //Activate RW mode if needed.
738 auto& m = movb.get_movie();
739 if(lmode == LOAD_STATE_RW)
740 m.readonly_mode(false);
741 if(lmode == LOAD_STATE_DEFAULT && !current_mode && m.get_frame_count() <= m.get_current_frame())
742 m.readonly_mode(false);
743 if(lmode == LOAD_STATE_INITIAL && m.get_frame_count() <= m.get_current_frame())
744 m.readonly_mode(false);
745 if(lmode == LOAD_STATE_CURRENT && !current_mode)
746 m.readonly_mode(false);
748 //Paint the screen.
750 framebuffer::raw tmp;
751 if(will_load_state) {
752 tmp.load(_movie.screenshot);
753 redraw_framebuffer(tmp);
754 } else
755 redraw_framebuffer(our_rom.rtype->draw_cover());
758 notify_mode_change(m.readonly_mode());
759 print_movie_info(_movie, our_rom, movb.get_rrdata());
760 notify_mbranch_change();
764 void try_request_rom(const std::string& moviefile)
766 moviefile::brief_info info(moviefile);
767 auto sysregs = core_sysregion::find_matching(info.sysregion);
768 rom_request req;
769 req.selected = 0;
770 size_t idx = 0;
771 req.core_guessed = false;
772 for(auto i : sysregs) {
773 //FIXME: Do something with this?
774 //if(i->get_type().get_biosname() != "" && info.hash[1] != "")
775 // has_bios = true;
776 req.cores.push_back(&i->get_type());
777 if(i->get_type().get_core_identifier() == info.corename) {
778 req.selected = idx;
779 req.core_guessed = true;
781 idx++;
783 if(req.cores.empty())
784 throw std::runtime_error("No known core can load movie of type '" + info.sysregion + "'");
785 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
786 req.guessed[i] = false;
787 req.has_slot[i] = (info.hash[i] != "");
788 req.filename[i] = info.hint[i];
789 req.hash[i] = info.hash[i];
790 req.hashxml[i] = info.hashxml[i];
792 try_guess_roms(req);
793 req.canceled = true;
794 graphics_driver_request_rom(req);
795 if(req.canceled)
796 throw std::runtime_error("Canceled loading ROM");
797 //Try to load the ROM using specified core.
798 if(req.selected >= req.cores.size())
799 throw std::runtime_error("Invalid ROM type selected");
800 core_type* selected_core = req.cores[req.selected];
801 loaded_rom newrom(req.filename, selected_core->get_core_identifier(), selected_core->get_iname(), "");
802 our_rom = newrom;
803 notify_core_change();
806 //Load state
807 bool do_load_state(const std::string& filename, int lmode)
809 int tmp = -1;
810 std::string filename2 = translate_name_mprefix(filename, tmp, -1);
811 uint64_t origtime = get_utime();
812 lua_callback_pre_load(filename2);
813 struct moviefile* mfile = NULL;
814 bool used = false;
815 try {
816 if(our_rom.rtype->isnull())
817 try_request_rom(filename2);
818 mfile = new moviefile(filename2, *our_rom.rtype);
819 } catch(std::bad_alloc& e) {
820 OOM_panic();
821 } catch(std::exception& e) {
822 platform::error_message(std::string("Can't read movie/savestate: ") + e.what());
823 messages << "Can't read movie/savestate '" << filename2 << "': " << e.what() << std::endl;
824 lua_callback_err_load(filename2);
825 return false;
827 try {
828 do_load_state(*mfile, lmode, used);
829 uint64_t took = get_utime() - origtime;
830 messages << "Loaded '" << filename2 << "' in " << took << " microseconds." << std::endl;
831 lua_callback_post_load(filename2, movb.get_mfile().is_savestate);
832 } catch(std::bad_alloc& e) {
833 OOM_panic();
834 } catch(std::exception& e) {
835 if(!used)
836 delete mfile;
837 platform::error_message(std::string("Can't load movie/savestate: ") + e.what());
838 messages << "Can't load movie/savestate '" << filename2 << "': " << e.what() << std::endl;
839 lua_callback_err_load(filename2);
840 return false;
842 return true;
845 void mainloop_restore_state(const std::vector<char>& state, uint64_t secs, uint64_t ssecs)
847 //Force unlazy rrdata.
848 movb.get_rrdata().read_base(rrdata_filename(movb.get_mfile().projectid), false);
849 movb.get_rrdata().add(next_rrdata());
850 movb.get_mfile().rtc_second = secs;
851 movb.get_mfile().rtc_subsecond = ssecs;
852 our_rom.load_core_state(state, true);