Split random number functions from misc.cpp to dedicated file
[lsnes.git] / src / core / moviedata.cpp
blob941173b1925f06f26dbdd4dabfb4556b3547852c
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/instance.hpp"
11 #include "core/moviedata.hpp"
12 #include "core/project.hpp"
13 #include "core/random.hpp"
14 #include "core/romguess.hpp"
15 #include "core/rrdata.hpp"
16 #include "core/settings.hpp"
17 #include "lua/lua.hpp"
18 #include "library/directory.hpp"
19 #include "library/string.hpp"
20 #include "library/minmax.hpp"
21 #include "library/temporary_handle.hpp"
22 #include "interface/romtype.hpp"
24 #include <iomanip>
25 #include <fstream>
27 struct loaded_rom our_rom;
28 bool system_corrupt;
29 std::string last_save;
30 void update_movie_state();
32 namespace
34 settingvar::variable<settingvar::model_int<0, 9>> savecompression(lsnes_vset, "savecompression",
35 "Movie‣Saving‣Compression", 7);
36 settingvar::variable<settingvar::model_bool<settingvar::yes_no>> readonly_load_preserves(lsnes_vset,
37 "preserve_on_readonly_load", "Movie‣Loading‣Preserve on readonly load", true);
38 threads::lock mprefix_lock;
39 std::string mprefix;
40 bool mprefix_valid;
42 std::string get_mprefix()
44 threads::alock h(mprefix_lock);
45 if(!mprefix_valid)
46 return "movieslot";
47 else
48 return mprefix + "-";
51 command::fnptr<const std::string&> dump_coresave(lsnes_cmd, "dump-coresave", "Dump bsnes core state",
52 "Syntax: dump-coresave <name>\nDumps core save to <name>\n",
53 [](const std::string& name) throw(std::bad_alloc, std::runtime_error) {
54 auto x = our_rom.save_core_state();
55 x.resize(x.size() - 32);
56 std::ofstream y(name.c_str(), std::ios::out | std::ios::binary);
57 y.write(&x[0], x.size());
58 y.close();
59 messages << "Saved core state to " << name << std::endl;
60 });
62 bool warn_hash_mismatch(const std::string& mhash, const fileimage::image& slot,
63 const std::string& name, bool fatal)
65 if(mhash == slot.sha_256.read())
66 return true;
67 if(!fatal) {
68 messages << "WARNING: " << name << " hash mismatch!" << std::endl
69 << "\tMovie: " << mhash << std::endl
70 << "\tOur ROM: " << slot.sha_256.read() << std::endl;
71 return true;
72 } else {
73 platform::error_message("Can't load state because hashes mismatch");
74 messages << "ERROR: " << name << " hash mismatch!" << std::endl
75 << "\tMovie: " << mhash << std::endl
76 << "\tOur ROM: " << slot.sha_256.read() << std::endl;
77 return false;
81 void set_mprefix(const std::string& pfx)
84 threads::alock h(mprefix_lock);
85 mprefix_valid = (pfx != "");
86 mprefix = pfx;
88 update_movie_state();
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 class _lsnes_pflag_handler : public movie::poll_flag
104 public:
105 ~_lsnes_pflag_handler()
108 int get_pflag()
110 return our_rom.rtype->get_pflag();
112 void set_pflag(int flag)
114 our_rom.rtype->set_pflag(flag);
116 } lsnes_pflag_handler;
119 std::string get_mprefix_for_project()
121 return get_mprefix_for_project(lsnes_instance.mlogic ? lsnes_instance.mlogic.get_mfile().projectid : "");
124 void set_mprefix_for_project(const std::string& prjid, const std::string& pfx)
126 std::string filename = get_config_path() + "/" + safe_filename(prjid) + ".pfx";
127 std::ofstream strm(filename);
128 strm << pfx << std::endl;
131 void set_mprefix_for_project(const std::string& pfx)
133 set_mprefix_for_project(lsnes_instance.mlogic ? lsnes_instance.mlogic.get_mfile().projectid : "", pfx);
134 set_mprefix(pfx);
137 std::string translate_name_mprefix(std::string original, int& binary, int save)
139 auto p = project_get();
140 regex_results r = regex("\\$SLOT:(.*)", original);
141 if(r) {
142 if(binary < 0)
143 binary = jukebox_dflt_binary ? 1 : 0;
144 if(p) {
145 uint64_t branch = p->get_current_branch();
146 std::string branch_str;
147 std::string filename;
148 if(branch) branch_str = (stringfmt() << "--" << branch).str();
149 filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
150 while(save < 0 && branch) {
151 if(zip::file_exists(filename))
152 break;
153 branch = p->get_parent_branch(branch);
154 branch_str = branch ? ((stringfmt() << "--" << branch).str()) : "";
155 filename = p->directory + "/" + p->prefix + "-" + r[1] + branch_str + ".lss";
157 return filename;
158 } else {
159 std::string pprf = lsnes_instance.setcache.get("slotpath") + "/";
160 return pprf + get_mprefix() + r[1] + ".lsmv";
162 } else {
163 if(binary < 0)
164 binary = (save ? save_dflt_binary : movie_dflt_binary) ? 1 : 0;
165 return original;
169 std::pair<std::string, std::string> split_author(const std::string& author) throw(std::bad_alloc,
170 std::runtime_error)
172 std::string _author = author;
173 std::string fullname;
174 std::string nickname;
175 size_t split = _author.find_first_of("|");
176 if(split >= _author.length()) {
177 fullname = _author;
178 } else {
179 fullname = _author.substr(0, split);
180 nickname = _author.substr(split + 1);
182 if(fullname == "" && nickname == "")
183 throw std::runtime_error("Bad author name");
184 return std::make_pair(fullname, nickname);
187 //Resolve relative path.
188 std::string resolve_relative_path(const std::string& path)
190 try {
191 return get_absolute_path(path);
192 } catch(...) {
193 return path;
197 //Save state.
198 void do_save_state(const std::string& filename, int binary) throw(std::bad_alloc,
199 std::runtime_error)
201 if(!lsnes_instance.mlogic || !lsnes_instance.mlogic.get_mfile().gametype) {
202 platform::error_message("Can't save movie without a ROM");
203 messages << "Can't save movie without a ROM" << std::endl;
204 return;
206 auto& target = lsnes_instance.mlogic.get_mfile();
207 std::string filename2 = translate_name_mprefix(filename, binary, 1);
208 lua_callback_pre_save(filename2, true);
209 try {
210 uint64_t origtime = get_utime();
211 target.is_savestate = true;
212 target.sram = our_rom.rtype->save_sram();
213 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
214 target.romimg_sha256[i] = our_rom.romimg[i].sha_256.read();
215 target.romxml_sha256[i] = our_rom.romxml[i].sha_256.read();
216 target.namehint[i] = our_rom.romimg[i].namehint;
218 target.savestate = our_rom.save_core_state();
219 get_framebuffer().save(target.screenshot);
220 lsnes_instance.mlogic.get_movie().save_state(target.projectid, target.save_frame,
221 target.lagged_frames, target.pollcounters);
222 target.poll_flag = our_rom.rtype->get_pflag();
223 auto prj = project_get();
224 if(prj) {
225 target.gamename = prj->gamename;
226 target.authors = prj->authors;
228 target.active_macros = controls.get_macro_frames();
229 target.save(filename2, savecompression, binary > 0, lsnes_instance.mlogic.get_rrdata());
230 uint64_t took = get_utime() - origtime;
231 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
232 messages << "Saved state " << kind << " '" << filename2 << "' in " << took << " microseconds."
233 << std::endl;
234 lua_callback_post_save(filename2, true);
235 } catch(std::bad_alloc& e) {
236 throw;
237 } catch(std::exception& e) {
238 platform::error_message(std::string("Save failed: ") + e.what());
239 messages << "Save failed: " << e.what() << std::endl;
240 lua_callback_err_save(filename2);
242 last_save = resolve_relative_path(filename2);
243 auto p = project_get();
244 if(p) {
245 p->last_save = last_save;
246 p->flush();
250 //Save movie.
251 void do_save_movie(const std::string& filename, int binary) throw(std::bad_alloc, std::runtime_error)
253 if(!lsnes_instance.mlogic || !lsnes_instance.mlogic.get_mfile().gametype) {
254 platform::error_message("Can't save movie without a ROM");
255 messages << "Can't save movie without a ROM" << std::endl;
256 return;
258 auto& target = lsnes_instance.mlogic.get_mfile();
259 std::string filename2 = translate_name_mprefix(filename, binary, 0);
260 lua_callback_pre_save(filename2, false);
261 try {
262 uint64_t origtime = get_utime();
263 target.is_savestate = false;
264 auto prj = project_get();
265 if(prj) {
266 target.gamename = prj->gamename;
267 target.authors = prj->authors;
269 target.active_macros.clear();
270 target.save(filename2, savecompression, binary > 0, lsnes_instance.mlogic.get_rrdata());
271 uint64_t took = get_utime() - origtime;
272 std::string kind = (binary > 0) ? "(binary format)" : "(zip format)";
273 messages << "Saved movie " << kind << " '" << filename2 << "' in " << took << " microseconds."
274 << std::endl;
275 lua_callback_post_save(filename2, false);
276 } catch(std::bad_alloc& e) {
277 OOM_panic();
278 } catch(std::exception& e) {
279 platform::error_message(std::string("Save failed: ") + e.what());
280 messages << "Save failed: " << e.what() << std::endl;
281 lua_callback_err_save(filename2);
283 last_save = resolve_relative_path(filename2);
284 auto p = project_get();
285 if(p) {
286 p->last_save = last_save;
287 p->flush();
291 extern time_t random_seed_value;
293 namespace
295 void populate_volatile_ram(moviefile& mf, std::list<core_vma_info>& vmas)
297 for(auto i : vmas) {
298 //Only regions that are marked as volatile, readwrite not special are initializable.
299 if(!i.volatile_flag || i.readonly || i.special)
300 continue;
301 if(!mf.ramcontent.count(i.name))
302 continue;
303 uint64_t csize = min((uint64_t)mf.ramcontent[i.name].size(), i.size);
304 if(i.backing_ram)
305 memcpy(i.backing_ram, &mf.ramcontent[i.name][0], csize);
306 else
307 for(uint64_t o = 0; o < csize; o++)
308 i.write(o, mf.ramcontent[i.name][o]);
312 std::string format_length(uint64_t mlength)
314 std::ostringstream x;
315 if(mlength >= 3600000ULL) {
316 x << mlength / 3600000ULL << ":";
317 mlength %= 3600000ULL;
319 x << std::setfill('0') << std::setw(2) << mlength / 60000ULL << ":";
320 mlength %= 60000ULL;
321 x << std::setfill('0') << std::setw(2) << mlength / 1000ULL << ".";
322 mlength %= 1000ULL;
323 x << std::setfill('0') << std::setw(3) << mlength;
324 return x.str();
327 void print_movie_info(moviefile& mov, loaded_rom& rom, rrdata_set& rrd)
329 if(rom.rtype)
330 messages << "ROM Type " << rom.rtype->get_hname() << " region " << rom.region->get_hname()
331 << std::endl;
332 std::string len, rerecs;
333 len = format_length(mov.get_movie_length());
334 std::string rerecords = mov.is_savestate ? (stringfmt() << rrd.count()).str() : mov.rerecords;
335 messages << "Rerecords " << rerecords << " length " << format_length(mov.get_movie_length())
336 << " (" << mov.get_frame_count() << " frames)" << std::endl;
337 if(mov.gamename != "")
338 messages << "Game name: " << mov.gamename << std::endl;
339 for(size_t i = 0; i < mov.authors.size(); i++)
340 if(mov.authors[i].second == "")
341 messages << "Author: " << mov.authors[i].first << std::endl;
342 else if(mov.authors[i].first == "")
343 messages << "Author: (" << mov.authors[i].second << ")" << std::endl;
344 else
345 messages << "Author: " << mov.authors[i].first << " ("
346 << mov.authors[i].second << ")" << std::endl;
349 void warn_coretype(const std::string& mov_core, loaded_rom& against, bool loadstate)
351 if(!against.rtype)
352 return;
353 std::string rom_core = against.rtype->get_core_identifier();
354 if(mov_core == rom_core)
355 return;
356 std::ostringstream x;
357 x << (loadstate ? "Error: " : "Warning: ");
358 x << "Emulator core version mismatch!" << std::endl
359 << "\tCrurrent: " << rom_core << std::endl
360 << "\tMovie: " << mov_core << std::endl;
361 if(loadstate)
362 throw std::runtime_error(x.str());
363 else
364 messages << x.str();
367 void warn_roms(moviefile& mov, loaded_rom& against, bool loadstate)
369 bool rom_ok = true;
370 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
371 if(mov.namehint[i] == "")
372 mov.namehint[i] = against.romimg[i].namehint;
373 if(mov.romimg_sha256[i] == "")
374 mov.romimg_sha256[i] = against.romimg[i].sha_256.read();
375 if(mov.romxml_sha256[i] == "")
376 mov.romxml_sha256[i] = against.romxml[i].sha_256.read();
377 rom_ok = rom_ok & warn_hash_mismatch(mov.romimg_sha256[i], against.romimg[i],
378 (stringfmt() << "ROM #" << (i + 1)).str(), loadstate);
379 rom_ok = rom_ok & warn_hash_mismatch(mov.romxml_sha256[i], against.romxml[i],
380 (stringfmt() << "XML #" << (i + 1)).str(), loadstate);
382 if(!rom_ok)
383 throw std::runtime_error("Incorrect ROM");
386 port_type_set& construct_movie_portset(moviefile& mov, loaded_rom& against)
388 auto ctrldata = against.rtype->controllerconfig(mov.settings);
389 return port_type_set::make(ctrldata.ports, ctrldata.portindex());
392 void handle_load_core(moviefile& _movie, port_type_set& portset, bool will_load_state)
394 random_seed_value = _movie.movie_rtc_second;
395 if(will_load_state) {
396 //If settings possibly change, reload the ROM.
397 if(!lsnes_instance.mlogic || lsnes_instance.mlogic.get_mfile().projectid != _movie.projectid)
398 our_rom.load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
399 //Load the savestate and movie state.
400 //Set the core ports in order to avoid port state being reinitialized when loading.
401 controls.set_ports(portset);
402 our_rom.load_core_state(_movie.savestate);
403 our_rom.rtype->set_pflag(_movie.poll_flag);
404 controls.set_macro_frames(_movie.active_macros);
405 } else {
406 //Reload the ROM in order to rewind to the beginning.
407 our_rom.load(_movie.settings, _movie.movie_rtc_second, _movie.movie_rtc_subsecond);
408 //Load the SRAM and volatile RAM. Or anchor savestate if any.
409 controls.set_ports(portset);
410 _movie.rtc_second = _movie.movie_rtc_second;
411 _movie.rtc_subsecond = _movie.movie_rtc_subsecond;
412 if(!_movie.anchor_savestate.empty()) {
413 our_rom.load_core_state(_movie.anchor_savestate);
414 } else {
415 our_rom.rtype->load_sram(_movie.movie_sram);
416 std::list<core_vma_info> vmas = our_rom.rtype->vma_list();
417 populate_volatile_ram(_movie, vmas);
419 our_rom.rtype->set_pflag(0);
420 controls.set_macro_frames(std::map<std::string, uint64_t>());
425 void do_load_rom() throw(std::bad_alloc, std::runtime_error)
427 bool load_readwrite = !lsnes_instance.mlogic || !lsnes_instance.mlogic.get_movie().readonly_mode();
428 if(lsnes_instance.mlogic) {
429 port_type_set& portset = construct_movie_portset(lsnes_instance.mlogic.get_mfile(), our_rom);
430 //If portset or gametype changes, force readwrite with new movie.
431 if(lsnes_instance.mlogic.get_mfile().input->get_types() != portset) load_readwrite = true;
432 if(our_rom.rtype != &lsnes_instance.mlogic.get_mfile().gametype->get_type()) load_readwrite = true;
435 if(!load_readwrite) {
436 //Read-only load. This is pretty simple.
437 //Force unlazying of rrdata and count a rerecord.
438 if(lsnes_instance.mlogic.get_rrdata().is_lazy())
439 lsnes_instance.mlogic.get_rrdata().read_base(rrdata_filename(
440 lsnes_instance.mlogic.get_mfile().projectid), false);
441 lsnes_instance.mlogic.get_rrdata().add(next_rrdata());
443 port_type_set& portset = construct_movie_portset(lsnes_instance.mlogic.get_mfile(), our_rom);
445 try {
446 handle_load_core(lsnes_instance.mlogic.get_mfile(), portset, false);
447 lsnes_instance.mlogic.get_mfile().gametype = &our_rom.rtype->combine_region(*our_rom.region);
448 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
449 lsnes_instance.mlogic.get_mfile().namehint[i] = our_rom.romimg[i].namehint;
450 lsnes_instance.mlogic.get_mfile().romimg_sha256[i] = our_rom.romimg[i].sha_256.read();
451 lsnes_instance.mlogic.get_mfile().romxml_sha256[i] = our_rom.romxml[i].sha_256.read();
453 lsnes_instance.mlogic.get_mfile().is_savestate = false;
454 lsnes_instance.mlogic.get_mfile().host_memory.clear();
455 lsnes_instance.mlogic.get_movie().reset_state();
456 redraw_framebuffer(our_rom.rtype->draw_cover());
457 lua_callback_do_rewind();
458 } catch(std::bad_alloc& e) {
459 OOM_panic();
460 } catch(std::exception& e) {
461 system_corrupt = true;
462 redraw_framebuffer(screen_corrupt, true);
463 throw;
465 } else {
466 //The more complicated Read-Write case.
467 //We need to create a new movie and movie file.
468 temporary_handle<moviefile> _movie;
469 _movie.get()->force_corrupt = false;
470 _movie.get()->gametype = NULL; //Not yet known.
471 _movie.get()->coreversion = our_rom.rtype->get_core_identifier();
472 _movie.get()->projectid = get_random_hexstring(40);
473 _movie.get()->rerecords = "0";
474 _movie.get()->rerecords_mem = 0;
475 for(size_t i = 0; i < ROM_SLOT_COUNT; i++) {
476 _movie.get()->namehint[i] = our_rom.romimg[i].namehint;
477 _movie.get()->romimg_sha256[i] = our_rom.romimg[i].sha_256.read();
478 _movie.get()->romxml_sha256[i] = our_rom.romxml[i].sha_256.read();
480 _movie.get()->is_savestate = false;
481 _movie.get()->save_frame = 0;
482 _movie.get()->lagged_frames = 0;
483 _movie.get()->poll_flag = false;
484 _movie.get()->movie_rtc_second = _movie.get()->rtc_second = 1000000000ULL;
485 _movie.get()->movie_rtc_subsecond = _movie.get()->rtc_subsecond = 0;
486 _movie.get()->start_paused = false;
487 _movie.get()->lazy_project_create = true;
488 port_type_set& portset2 = construct_movie_portset(*_movie.get(), our_rom);
489 _movie.get()->input = NULL;
490 _movie.get()->create_default_branch(portset2);
492 //Wrap the input in movie.
493 temporary_handle<movie> newmovie;
494 newmovie.get()->set_movie_data(_movie.get()->input);
495 newmovie.get()->load(_movie.get()->rerecords, _movie.get()->projectid, *(_movie.get()->input));
496 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
497 newmovie.get()->readonly_mode(false);
499 //Force new lazy rrdata and count a rerecord.
500 temporary_handle<rrdata_set> rrd;
501 rrd.get()->read_base(rrdata_filename(_movie.get()->projectid), true);
502 rrd.get()->add(next_rrdata());
503 //Movie data is lost.
504 lua_callback_movie_lost("reload");
505 try {
506 handle_load_core(*_movie.get(), portset2, false);
507 _movie.get()->gametype = &our_rom.rtype->combine_region(*our_rom.region);
508 redraw_framebuffer(our_rom.rtype->draw_cover());
509 lua_callback_do_rewind();
510 } catch(std::bad_alloc& e) {
511 OOM_panic();
512 } catch(std::exception& e) {
513 system_corrupt = true;
514 redraw_framebuffer(screen_corrupt, true);
515 throw;
518 //Set up stuff.
519 lsnes_instance.mlogic.set_movie(*(newmovie()), true);
520 lsnes_instance.mlogic.set_mfile(*(_movie()), true);
521 lsnes_instance.mlogic.set_rrdata(*(rrd()), true);
522 set_mprefix(get_mprefix_for_project(lsnes_instance.mlogic.get_mfile().projectid));
524 notify_mode_change(lsnes_instance.mlogic.get_movie().readonly_mode());
525 notify_mbranch_change();
526 messages << "ROM reloaded." << std::endl;
529 void do_load_rewind() throw(std::bad_alloc, std::runtime_error)
531 if(!lsnes_instance.mlogic || !lsnes_instance.mlogic.get_mfile().gametype)
532 throw std::runtime_error("Can't rewind movie without existing movie");
534 port_type_set& portset = construct_movie_portset(lsnes_instance.mlogic.get_mfile(), our_rom);
536 //Force unlazying of rrdata and count a rerecord.
537 if(lsnes_instance.mlogic.get_rrdata().is_lazy())
538 lsnes_instance.mlogic.get_rrdata().read_base(rrdata_filename(
539 lsnes_instance.mlogic.get_mfile().projectid), false);
540 lsnes_instance.mlogic.get_rrdata().add(next_rrdata());
542 //Enter readonly mode.
543 lsnes_instance.mlogic.get_movie().readonly_mode(true);
544 notify_mode_change(true);
545 try {
546 handle_load_core(lsnes_instance.mlogic.get_mfile(), portset, false);
547 lsnes_instance.mlogic.get_mfile().is_savestate = false;
548 lsnes_instance.mlogic.get_mfile().host_memory.clear();
549 lsnes_instance.mlogic.get_movie().reset_state();
550 redraw_framebuffer(our_rom.rtype->draw_cover());
551 lua_callback_do_rewind();
552 } catch(std::bad_alloc& e) {
553 OOM_panic();
554 } catch(std::exception& e) {
555 system_corrupt = true;
556 redraw_framebuffer(screen_corrupt, true);
557 throw;
559 messages << "Movie rewound to beginning." << std::endl;
562 //Load state preserving input. Does not do checks.
563 void do_load_state_preserve(struct moviefile& _movie)
565 if(!lsnes_instance.mlogic || !lsnes_instance.mlogic.get_mfile().gametype)
566 throw std::runtime_error("Can't load movie preserving input without previous movie");
567 if(lsnes_instance.mlogic.get_mfile().projectid != _movie.projectid)
568 throw std::runtime_error("Savestate is from different movie");
570 bool will_load_state = _movie.is_savestate;
571 port_type_set& portset = construct_movie_portset(lsnes_instance.mlogic.get_mfile(), our_rom);
573 //Construct a new movie sharing the input data.
574 temporary_handle<movie> newmovie;
575 newmovie.get()->set_movie_data(lsnes_instance.mlogic.get_mfile().input);
576 newmovie.get()->readonly_mode(true);
577 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
579 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
580 if(will_load_state)
581 newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
582 _movie.input, _movie.projectid);
584 //Count a rerecord.
585 if(lsnes_instance.mlogic.get_rrdata().is_lazy() && !_movie.lazy_project_create)
586 lsnes_instance.mlogic.get_rrdata().read_base(rrdata_filename(_movie.projectid), false);
587 lsnes_instance.mlogic.get_rrdata().add(next_rrdata());
589 //Negative return.
590 try {
591 handle_load_core(_movie, portset, will_load_state);
592 } catch(std::bad_alloc& e) {
593 OOM_panic();
594 } catch(std::exception& e) {
595 system_corrupt = true;
596 redraw_framebuffer(screen_corrupt, true);
597 throw;
600 //Set new movie.
601 lsnes_instance.mlogic.set_movie(*(newmovie()), true);
603 //Some fields MUST be taken from movie or one gets desyncs.
604 lsnes_instance.mlogic.get_mfile().is_savestate = _movie.is_savestate;
605 lsnes_instance.mlogic.get_mfile().rtc_second = _movie.rtc_second;
606 lsnes_instance.mlogic.get_mfile().rtc_subsecond = _movie.rtc_subsecond;
607 std::swap(lsnes_instance.mlogic.get_mfile().host_memory, _movie.host_memory);
608 if(!will_load_state)
609 lsnes_instance.mlogic.get_mfile().host_memory.clear();
611 try {
612 //Paint the screen.
613 framebuffer::raw tmp;
614 if(will_load_state) {
615 tmp.load(_movie.screenshot);
616 redraw_framebuffer(tmp);
617 } else
618 redraw_framebuffer(our_rom.rtype->draw_cover());
619 } catch(...) {
621 delete &_movie;
622 notify_mode_change(lsnes_instance.mlogic.get_movie().readonly_mode());
623 messages << "Loadstated at earlier point of movie." << std::endl;
626 //Load state from loaded movie file. _movie is consumed.
627 void do_load_state(struct moviefile& _movie, int lmode, bool& used)
629 //Some basic sanity checks.
630 bool current_mode = lsnes_instance.mlogic ? lsnes_instance.mlogic.get_movie().readonly_mode() : false;
631 bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE;
633 //Load state all branches and load state initial are the same.
634 if(lmode == LOAD_STATE_ALLBRANCH) lmode = LOAD_STATE_INITIAL;
636 //Various checks.
637 if(_movie.force_corrupt)
638 throw std::runtime_error("Movie file invalid");
639 if(&(_movie.gametype->get_type()) != our_rom.rtype)
640 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
641 if(our_rom.orig_region && !our_rom.orig_region->compatible_with(_movie.gametype->get_region()))
642 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
643 warn_coretype(_movie.coreversion, our_rom, will_load_state);
644 warn_roms(_movie, our_rom, will_load_state);
646 //In certain conditions, trun LOAD_STATE_CURRENT into LOAD_STATE_PRESERVE.
647 if(lmode == LOAD_STATE_CURRENT && current_mode && readonly_load_preserves)
648 lmode = LOAD_STATE_PRESERVE;
649 //If movie file changes, turn LOAD_STATE_CURRENT into LOAD_STATE_RO
650 if(lmode == LOAD_STATE_CURRENT && lsnes_instance.mlogic.get_mfile().projectid != _movie.projectid)
651 lmode = LOAD_STATE_RO;
653 //Handle preserving load specially.
654 if(lmode == LOAD_STATE_PRESERVE) {
655 do_load_state_preserve(_movie);
656 used = true;
657 return;
660 //Create a new movie, and if needed, restore the movie state.
661 temporary_handle<movie> newmovie;
662 newmovie.get()->set_movie_data(_movie.input);
663 newmovie.get()->load(_movie.rerecords, _movie.projectid, *_movie.input);
664 newmovie.get()->set_pflag_handler(&lsnes_pflag_handler);
665 if(will_load_state)
666 newmovie.get()->restore_state(_movie.save_frame, _movie.lagged_frames, _movie.pollcounters, true,
667 NULL, _movie.projectid);
669 //Copy the other branches.
670 if(lmode != LOAD_STATE_INITIAL && lsnes_instance.mlogic.get_mfile().projectid == _movie.projectid) {
671 newmovie.get()->set_movie_data(NULL);
672 auto& oldm = lsnes_instance.mlogic.get_mfile().branches;
673 auto& newm = _movie.branches;
674 auto oldd = lsnes_instance.mlogic.get_mfile().input;
675 auto newd = _movie.input;
676 std::string dflt_name;
677 //What was the old default name?
678 for(auto& i : oldm)
679 if(&i.second == oldd)
680 dflt_name = i.first;
681 //Rename the default to match with old movie if the names differ.
682 if(!newm.count(dflt_name) || &newm[dflt_name] != newd)
683 newm[dflt_name] = *newd;
684 //Copy all other branches.
685 for(auto& i : oldm)
686 if(i.first != dflt_name)
687 newm[i.first] = i.second;
688 //Delete branches that didn't exist.
689 for(auto i = newm.begin(); i != newm.end();) {
690 if(oldm.count(i->first))
691 i++;
692 else
693 i = newm.erase(i);
695 _movie.input = &newm[dflt_name];
696 newmovie.get()->set_movie_data(_movie.input);
699 port_type_set& portset = construct_movie_portset(_movie, our_rom);
701 temporary_handle<rrdata_set> rrd;
702 bool new_rrdata = false;
703 //Count a rerecord (against new or old movie).
704 if(!lsnes_instance.mlogic || _movie.projectid != lsnes_instance.mlogic.get_mfile().projectid) {
705 rrd.get()->read_base(rrdata_filename(_movie.projectid), _movie.lazy_project_create);
706 rrd.get()->read(_movie.c_rrdata);
707 rrd.get()->add(next_rrdata());
708 new_rrdata = true;
709 } else {
710 //Unlazy rrdata if needed.
711 if(lsnes_instance.mlogic.get_rrdata().is_lazy() && !_movie.lazy_project_create)
712 lsnes_instance.mlogic.get_rrdata().read_base(rrdata_filename(_movie.projectid), false);
713 lsnes_instance.mlogic.get_rrdata().add(next_rrdata());
715 //Negative return.
716 try {
717 our_rom.region = _movie.gametype ? &(_movie.gametype->get_region()) : NULL;
718 handle_load_core(_movie, portset, will_load_state);
719 } catch(std::bad_alloc& e) {
720 OOM_panic();
721 } catch(std::exception& e) {
722 system_corrupt = true;
723 redraw_framebuffer(screen_corrupt, true);
724 throw;
727 //If loaded a movie, clear the is savestate and rrdata.
728 if(!will_load_state) {
729 _movie.is_savestate = false;
730 _movie.host_memory.clear();
733 lua_callback_movie_lost("load");
735 //Copy the data.
736 if(new_rrdata) lsnes_instance.mlogic.set_rrdata(*(rrd()), true);
737 lsnes_instance.mlogic.set_movie(*newmovie(), true);
738 lsnes_instance.mlogic.set_mfile(_movie, true);
739 used = true;
741 set_mprefix(get_mprefix_for_project(lsnes_instance.mlogic.get_mfile().projectid));
743 //Activate RW mode if needed.
744 auto& m = lsnes_instance.mlogic.get_movie();
745 if(lmode == LOAD_STATE_RW)
746 m.readonly_mode(false);
747 if(lmode == LOAD_STATE_DEFAULT && !current_mode && m.get_frame_count() <= m.get_current_frame())
748 m.readonly_mode(false);
749 if(lmode == LOAD_STATE_INITIAL && m.get_frame_count() <= m.get_current_frame())
750 m.readonly_mode(false);
751 if(lmode == LOAD_STATE_CURRENT && !current_mode)
752 m.readonly_mode(false);
754 //Paint the screen.
756 framebuffer::raw tmp;
757 if(will_load_state) {
758 tmp.load(_movie.screenshot);
759 redraw_framebuffer(tmp);
760 } else
761 redraw_framebuffer(our_rom.rtype->draw_cover());
764 notify_mode_change(m.readonly_mode());
765 print_movie_info(_movie, our_rom, lsnes_instance.mlogic.get_rrdata());
766 notify_mbranch_change();
770 void try_request_rom(const std::string& moviefile)
772 moviefile::brief_info info(moviefile);
773 auto sysregs = core_sysregion::find_matching(info.sysregion);
774 rom_request req;
775 req.selected = 0;
776 size_t idx = 0;
777 req.core_guessed = false;
778 for(auto i : sysregs) {
779 //FIXME: Do something with this?
780 //if(i->get_type().get_biosname() != "" && info.hash[1] != "")
781 // has_bios = true;
782 req.cores.push_back(&i->get_type());
783 if(i->get_type().get_core_identifier() == info.corename) {
784 req.selected = idx;
785 req.core_guessed = true;
787 idx++;
789 if(req.cores.empty())
790 throw std::runtime_error("No known core can load movie of type '" + info.sysregion + "'");
791 for(unsigned i = 0; i < ROM_SLOT_COUNT; i++) {
792 req.guessed[i] = false;
793 req.has_slot[i] = (info.hash[i] != "");
794 req.filename[i] = info.hint[i];
795 req.hash[i] = info.hash[i];
796 req.hashxml[i] = info.hashxml[i];
798 try_guess_roms(req);
799 req.canceled = true;
800 graphics_driver_request_rom(req);
801 if(req.canceled)
802 throw std::runtime_error("Canceled loading ROM");
803 //Try to load the ROM using specified core.
804 if(req.selected >= req.cores.size())
805 throw std::runtime_error("Invalid ROM type selected");
806 core_type* selected_core = req.cores[req.selected];
807 loaded_rom newrom(req.filename, selected_core->get_core_identifier(), selected_core->get_iname(), "");
808 our_rom = newrom;
809 notify_core_change();
812 //Load state
813 bool do_load_state(const std::string& filename, int lmode)
815 int tmp = -1;
816 std::string filename2 = translate_name_mprefix(filename, tmp, -1);
817 uint64_t origtime = get_utime();
818 lua_callback_pre_load(filename2);
819 struct moviefile* mfile = NULL;
820 bool used = false;
821 try {
822 if(our_rom.rtype->isnull())
823 try_request_rom(filename2);
824 mfile = new moviefile(filename2, *our_rom.rtype);
825 } catch(std::bad_alloc& e) {
826 OOM_panic();
827 } catch(std::exception& e) {
828 platform::error_message(std::string("Can't read movie/savestate: ") + e.what());
829 messages << "Can't read movie/savestate '" << filename2 << "': " << e.what() << std::endl;
830 lua_callback_err_load(filename2);
831 return false;
833 try {
834 do_load_state(*mfile, lmode, used);
835 uint64_t took = get_utime() - origtime;
836 messages << "Loaded '" << filename2 << "' in " << took << " microseconds." << std::endl;
837 lua_callback_post_load(filename2, lsnes_instance.mlogic.get_mfile().is_savestate);
838 } catch(std::bad_alloc& e) {
839 OOM_panic();
840 } catch(std::exception& e) {
841 if(!used)
842 delete mfile;
843 platform::error_message(std::string("Can't load movie/savestate: ") + e.what());
844 messages << "Can't load movie/savestate '" << filename2 << "': " << e.what() << std::endl;
845 lua_callback_err_load(filename2);
846 return false;
848 return true;
851 void mainloop_restore_state(const std::vector<char>& state, uint64_t secs, uint64_t ssecs)
853 //Force unlazy rrdata.
854 lsnes_instance.mlogic.get_rrdata().read_base(rrdata_filename(lsnes_instance.mlogic.get_mfile().projectid),
855 false);
856 lsnes_instance.mlogic.get_rrdata().add(next_rrdata());
857 lsnes_instance.mlogic.get_mfile().rtc_second = secs;
858 lsnes_instance.mlogic.get_mfile().rtc_subsecond = ssecs;
859 our_rom.load_core_state(state, true);