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"
29 std::string last_save
;
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
;
41 std::string
get_mprefix()
43 threads::alock
h(mprefix_lock
);
50 command::fnptr
<const std::string
&> test4(lsnes_cmds
, CMOVIEDATA::panic
,
51 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
53 if(*core
.mlogic
) emerg_save_movie(core
.mlogic
->get_mfile(), core
.mlogic
->get_rrdata());
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
) {
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());
64 messages
<< "Saved core state to " << name
<< std::endl
;
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())
73 messages
<< "WARNING: " << name
<< " hash mismatch!" << std::endl
74 << "\tMovie: " << mhash
<< std::endl
75 << "\tOur ROM: " << slot
.sha_256
.read() << std::endl
;
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
;
86 void set_mprefix(const std::string
& pfx
)
90 threads::alock
h(mprefix_lock
);
91 mprefix_valid
= (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
);
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
121 ~_lsnes_pflag_handler()
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()
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
)
151 set_mprefix_for_project(*core
.mlogic
? core
.mlogic
->get_mfile().projectid
: "", pfx
);
155 std::string
translate_name_mprefix(std::string original
, int& binary
, int save
)
158 auto p
= core
.project
->get();
159 regex_results r
= regex("\\$SLOT:(.*)", original
);
162 binary
= core
.jukebox
->save_binary() ? 1 : 0;
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
))
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";
178 std::string pprf
= core
.setcache
->get("slotpath") + "/";
179 return pprf
+ get_mprefix() + r
[1] + ".lsmv";
183 binary
= (save
? save_dflt_binary(*core
.settings
) :
184 movie_dflt_binary(*core
.settings
)) ? 1 : 0;
189 std::pair
<std::string
, std::string
> split_author(const std::string
& author
) throw(std::bad_alloc
,
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()) {
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
)
211 return directory::absolute_path(path
);
218 void do_save_state(const std::string
& filename
, int binary
) throw(std::bad_alloc
,
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
;
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);
231 uint64_t origtime
= framerate_regulator::get_utime();
232 target
.is_savestate
= true;
233 target
.sram
= core
.rom
->save_sram();
234 for(size_t i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
235 auto& img
= core
.rom
->get_rom(i
);
236 auto& xml
= core
.rom
->get_markup(i
);
237 target
.romimg_sha256
[i
] = img
.sha_256
.read();
238 target
.romxml_sha256
[i
] = xml
.sha_256
.read();
239 target
.namehint
[i
] = img
.namehint
;
241 target
.savestate
= core
.rom
->save_core_state();
242 core
.fbuf
->get_framebuffer().save(target
.screenshot
);
243 core
.mlogic
->get_movie().save_state(target
.projectid
, target
.save_frame
,
244 target
.lagged_frames
, target
.pollcounters
);
245 target
.poll_flag
= core
.rom
->get_pflag();
246 auto prj
= core
.project
->get();
248 target
.gamename
= prj
->gamename
;
249 target
.authors
= prj
->authors
;
251 target
.active_macros
= core
.controls
->get_macro_frames();
252 target
.save(filename2
, SET_savecompression(*core
.settings
), binary
> 0,
253 core
.mlogic
->get_rrdata());
254 uint64_t took
= framerate_regulator::get_utime() - origtime
;
255 std::string kind
= (binary
> 0) ? "(binary format)" : "(zip format)";
256 messages
<< "Saved state " << kind
<< " '" << filename2
<< "' in " << took
<< " microseconds."
258 core
.lua2
->callback_post_save(filename2
, true);
259 } catch(std::bad_alloc
& e
) {
261 } catch(std::exception
& e
) {
262 platform::error_message(std::string("Save failed: ") + e
.what());
263 messages
<< "Save failed: " << e
.what() << std::endl
;
264 core
.lua2
->callback_err_save(filename2
);
266 last_save
= resolve_relative_path(filename2
);
267 auto p
= core
.project
->get();
269 p
->last_save
= last_save
;
275 void do_save_movie(const std::string
& filename
, int binary
) throw(std::bad_alloc
, std::runtime_error
)
278 if(!*core
.mlogic
|| !core
.mlogic
->get_mfile().gametype
) {
279 platform::error_message("Can't save movie without a ROM");
280 messages
<< "Can't save movie without a ROM" << std::endl
;
283 auto& target
= core
.mlogic
->get_mfile();
284 std::string filename2
= translate_name_mprefix(filename
, binary
, 0);
285 core
.lua2
->callback_pre_save(filename2
, false);
287 uint64_t origtime
= framerate_regulator::get_utime();
288 target
.is_savestate
= false;
289 auto prj
= core
.project
->get();
291 target
.gamename
= prj
->gamename
;
292 target
.authors
= prj
->authors
;
294 target
.active_macros
.clear();
295 target
.save(filename2
, SET_savecompression(*core
.settings
), binary
> 0,
296 core
.mlogic
->get_rrdata());
297 uint64_t took
= framerate_regulator::get_utime() - origtime
;
298 std::string kind
= (binary
> 0) ? "(binary format)" : "(zip format)";
299 messages
<< "Saved movie " << kind
<< " '" << filename2
<< "' in " << took
<< " microseconds."
301 core
.lua2
->callback_post_save(filename2
, false);
302 } catch(std::bad_alloc
& e
) {
304 } catch(std::exception
& e
) {
305 platform::error_message(std::string("Save failed: ") + e
.what());
306 messages
<< "Save failed: " << e
.what() << std::endl
;
307 core
.lua2
->callback_err_save(filename2
);
309 last_save
= resolve_relative_path(filename2
);
310 auto p
= core
.project
->get();
312 p
->last_save
= last_save
;
319 void populate_volatile_ram(moviefile
& mf
, std::list
<core_vma_info
>& vmas
)
322 //Only regions that are marked as volatile, readwrite not special are initializable.
323 if(!i
.volatile_flag
|| i
.readonly
|| i
.special
)
325 if(!mf
.ramcontent
.count(i
.name
))
327 uint64_t csize
= min((uint64_t)mf
.ramcontent
[i
.name
].size(), i
.size
);
329 memcpy(i
.backing_ram
, &mf
.ramcontent
[i
.name
][0], csize
);
331 for(uint64_t o
= 0; o
< csize
; o
++)
332 i
.write(o
, mf
.ramcontent
[i
.name
][o
]);
336 std::string
format_length(uint64_t mlength
)
338 std::ostringstream x
;
339 if(mlength
>= 3600000ULL) {
340 x
<< mlength
/ 3600000ULL << ":";
341 mlength
%= 3600000ULL;
343 x
<< std::setfill('0') << std::setw(2) << mlength
/ 60000ULL << ":";
345 x
<< std::setfill('0') << std::setw(2) << mlength
/ 1000ULL << ".";
347 x
<< std::setfill('0') << std::setw(3) << mlength
;
351 void print_movie_info(moviefile
& mov
, loaded_rom
& rom
, rrdata_set
& rrd
)
354 messages
<< "ROM Type " << rom
.get_hname() << " region " << rom
.region_get_hname()
356 std::string len
, rerecs
;
357 len
= format_length(mov
.get_movie_length());
358 std::string rerecords
= mov
.is_savestate
? (stringfmt() << rrd
.count()).str() : mov
.rerecords
;
359 messages
<< "Rerecords " << rerecords
<< " length " << format_length(mov
.get_movie_length())
360 << " (" << mov
.get_frame_count() << " frames)" << std::endl
;
361 if(mov
.gamename
!= "")
362 messages
<< "Game name: " << mov
.gamename
<< std::endl
;
363 for(size_t i
= 0; i
< mov
.authors
.size(); i
++)
364 if(mov
.authors
[i
].second
== "")
365 messages
<< "Author: " << mov
.authors
[i
].first
<< std::endl
;
366 else if(mov
.authors
[i
].first
== "")
367 messages
<< "Author: (" << mov
.authors
[i
].second
<< ")" << std::endl
;
369 messages
<< "Author: " << mov
.authors
[i
].first
<< " ("
370 << mov
.authors
[i
].second
<< ")" << std::endl
;
373 void warn_coretype(const std::string
& mov_core
, loaded_rom
& against
, bool loadstate
)
377 std::string rom_core
= against
.get_core_identifier();
378 if(mov_core
== rom_core
)
380 std::ostringstream x
;
381 x
<< (loadstate
? "Error: " : "Warning: ");
382 x
<< "Emulator core version mismatch!" << std::endl
383 << "\tCrurrent: " << rom_core
<< std::endl
384 << "\tMovie: " << mov_core
<< std::endl
;
386 throw std::runtime_error(x
.str());
391 void warn_roms(moviefile
& mov
, loaded_rom
& against
, bool loadstate
)
394 for(size_t i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
395 auto& img
= against
.get_rom(i
);
396 auto& xml
= against
.get_markup(i
);
397 if(mov
.namehint
[i
] == "")
398 mov
.namehint
[i
] = img
.namehint
;
399 if(mov
.romimg_sha256
[i
] == "")
400 mov
.romimg_sha256
[i
] = img
.sha_256
.read();
401 if(mov
.romxml_sha256
[i
] == "")
402 mov
.romxml_sha256
[i
] = xml
.sha_256
.read();
403 rom_ok
= rom_ok
& warn_hash_mismatch(mov
.romimg_sha256
[i
], img
,
404 (stringfmt() << "ROM #" << (i
+ 1)).str(), loadstate
);
405 rom_ok
= rom_ok
& warn_hash_mismatch(mov
.romxml_sha256
[i
], xml
,
406 (stringfmt() << "XML #" << (i
+ 1)).str(), loadstate
);
409 throw std::runtime_error("Incorrect ROM");
412 portctrl::type_set
& construct_movie_portset(moviefile
& mov
, loaded_rom
& against
)
414 auto ctrldata
= against
.controllerconfig(mov
.settings
);
415 return portctrl::type_set::make(ctrldata
.ports
, ctrldata
.portindex());
418 void handle_load_core(moviefile
& _movie
, portctrl::type_set
& portset
, bool will_load_state
)
421 core
.random_seed_value
= _movie
.movie_rtc_second
;
422 if(will_load_state
) {
423 //If settings possibly change, reload the ROM.
424 if(!*core
.mlogic
|| core
.mlogic
->get_mfile().projectid
!= _movie
.projectid
)
425 core
.rom
->load(_movie
.settings
, _movie
.movie_rtc_second
, _movie
.movie_rtc_subsecond
);
426 //Load the savestate and movie state.
427 //Set the core ports in order to avoid port state being reinitialized when loading.
428 core
.controls
->set_ports(portset
);
429 core
.rom
->load_core_state(_movie
.savestate
);
430 core
.rom
->set_pflag(_movie
.poll_flag
);
431 core
.controls
->set_macro_frames(_movie
.active_macros
);
433 //Reload the ROM in order to rewind to the beginning.
434 core
.rom
->load(_movie
.settings
, _movie
.movie_rtc_second
, _movie
.movie_rtc_subsecond
);
435 //Load the SRAM and volatile RAM. Or anchor savestate if any.
436 core
.controls
->set_ports(portset
);
437 _movie
.rtc_second
= _movie
.movie_rtc_second
;
438 _movie
.rtc_subsecond
= _movie
.movie_rtc_subsecond
;
439 if(!_movie
.anchor_savestate
.empty()) {
440 core
.rom
->load_core_state(_movie
.anchor_savestate
);
442 core
.rom
->load_sram(_movie
.movie_sram
);
443 std::list
<core_vma_info
> vmas
= core
.rom
->vma_list();
444 populate_volatile_ram(_movie
, vmas
);
446 core
.rom
->set_pflag(0);
447 core
.controls
->set_macro_frames(std::map
<std::string
, uint64_t>());
452 void do_load_rom() throw(std::bad_alloc
, std::runtime_error
)
455 bool load_readwrite
= !*core
.mlogic
|| !core
.mlogic
->get_movie().readonly_mode();
457 portctrl::type_set
& portset
= construct_movie_portset(core
.mlogic
->get_mfile(), *core
.rom
);
458 //If portset or gametype changes, force readwrite with new movie.
459 if(core
.mlogic
->get_mfile().input
->get_types() != portset
) load_readwrite
= true;
460 if(!core
.rom
->is_of_type(core
.mlogic
->get_mfile().gametype
->get_type())) load_readwrite
= true;
463 if(!load_readwrite
) {
464 //Read-only load. This is pretty simple.
465 //Force unlazying of rrdata and count a rerecord.
466 if(core
.mlogic
->get_rrdata().is_lazy())
467 core
.mlogic
->get_rrdata().read_base(rrdata::filename(
468 core
.mlogic
->get_mfile().projectid
), false);
469 core
.mlogic
->get_rrdata().add((*core
.nrrdata
)());
471 portctrl::type_set
& portset
= construct_movie_portset(core
.mlogic
->get_mfile(), *core
.rom
);
474 handle_load_core(core
.mlogic
->get_mfile(), portset
, false);
475 core
.mlogic
->get_mfile().gametype
= &core
.rom
->get_sysregion();
476 for(size_t i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
477 auto& img
= core
.rom
->get_rom(i
);
478 auto& xml
= core
.rom
->get_markup(i
);
479 core
.mlogic
->get_mfile().namehint
[i
] = img
.namehint
;
480 core
.mlogic
->get_mfile().romimg_sha256
[i
] = img
.sha_256
.read();
481 core
.mlogic
->get_mfile().romxml_sha256
[i
] = xml
.sha_256
.read();
483 core
.mlogic
->get_mfile().is_savestate
= false;
484 core
.mlogic
->get_mfile().host_memory
.clear();
485 core
.mlogic
->get_movie().reset_state();
486 core
.fbuf
->redraw_framebuffer(core
.rom
->draw_cover());
487 core
.lua2
->callback_do_rewind();
488 } catch(std::bad_alloc
& e
) {
490 } catch(std::exception
& e
) {
491 core
.runmode
->set_corrupt();
492 core
.fbuf
->redraw_framebuffer(emu_framebuffer::screen_corrupt
, true);
496 //The more complicated Read-Write case.
497 //We need to create a new movie and movie file.
498 temporary_handle
<moviefile
> _movie
;
499 _movie
.get()->force_corrupt
= false;
500 _movie
.get()->gametype
= NULL
; //Not yet known.
501 _movie
.get()->coreversion
= core
.rom
->get_core_identifier();
502 _movie
.get()->projectid
= get_random_hexstring(40);
503 _movie
.get()->rerecords
= "0";
504 _movie
.get()->rerecords_mem
= 0;
505 for(size_t i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
506 auto& img
= core
.rom
->get_rom(i
);
507 auto& xml
= core
.rom
->get_markup(i
);
508 _movie
.get()->namehint
[i
] = img
.namehint
;
509 _movie
.get()->romimg_sha256
[i
] = img
.sha_256
.read();
510 _movie
.get()->romxml_sha256
[i
] = xml
.sha_256
.read();
512 _movie
.get()->is_savestate
= false;
513 _movie
.get()->save_frame
= 0;
514 _movie
.get()->lagged_frames
= 0;
515 _movie
.get()->poll_flag
= false;
516 _movie
.get()->movie_rtc_second
= _movie
.get()->rtc_second
= 1000000000ULL;
517 _movie
.get()->movie_rtc_subsecond
= _movie
.get()->rtc_subsecond
= 0;
518 _movie
.get()->start_paused
= false;
519 _movie
.get()->lazy_project_create
= true;
520 portctrl::type_set
& portset2
= construct_movie_portset(*_movie
.get(), *core
.rom
);
521 _movie
.get()->input
= NULL
;
522 _movie
.get()->create_default_branch(portset2
);
524 //Wrap the input in movie.
525 temporary_handle
<movie
> newmovie
;
526 newmovie
.get()->set_movie_data(_movie
.get()->input
);
527 newmovie
.get()->load(_movie
.get()->rerecords
, _movie
.get()->projectid
, *(_movie
.get()->input
));
528 newmovie
.get()->set_pflag_handler(&lsnes_pflag_handler
);
529 newmovie
.get()->readonly_mode(false);
531 //Force new lazy rrdata and count a rerecord.
532 temporary_handle
<rrdata_set
> rrd
;
533 rrd
.get()->read_base(rrdata::filename(_movie
.get()->projectid
), true);
534 rrd
.get()->add((*core
.nrrdata
)());
535 //Movie data is lost.
536 core
.lua2
->callback_movie_lost("reload");
538 handle_load_core(*_movie
.get(), portset2
, false);
539 _movie
.get()->gametype
= &core
.rom
->get_sysregion();
540 } catch(std::bad_alloc
& e
) {
542 } catch(std::exception
& e
) {
543 core
.runmode
->set_corrupt();
544 core
.fbuf
->redraw_framebuffer(emu_framebuffer::screen_corrupt
, true);
549 core
.mlogic
->set_movie(*(newmovie()), true);
550 core
.mlogic
->set_mfile(*(_movie()), true);
551 core
.mlogic
->set_rrdata(*(rrd()), true);
552 set_mprefix(get_mprefix_for_project(core
.mlogic
->get_mfile().projectid
));
553 set_gameinfo(core
.mlogic
->get_mfile());
555 core
.fbuf
->redraw_framebuffer(core
.rom
->draw_cover());
556 core
.lua2
->callback_do_rewind();
558 core
.dispatch
->mode_change(core
.mlogic
->get_movie().readonly_mode());
559 core
.dispatch
->mbranch_change();
560 messages
<< "ROM reloaded." << std::endl
;
563 void do_load_rewind() throw(std::bad_alloc
, std::runtime_error
)
566 if(!*core
.mlogic
|| !core
.mlogic
->get_mfile().gametype
)
567 throw std::runtime_error("Can't rewind movie without existing movie");
569 portctrl::type_set
& portset
= construct_movie_portset(core
.mlogic
->get_mfile(), *core
.rom
);
571 //Force unlazying of rrdata and count a rerecord.
572 if(core
.mlogic
->get_rrdata().is_lazy())
573 core
.mlogic
->get_rrdata().read_base(rrdata::filename(
574 core
.mlogic
->get_mfile().projectid
), false);
575 core
.mlogic
->get_rrdata().add((*core
.nrrdata
)());
577 //Enter readonly mode.
578 core
.mlogic
->get_movie().readonly_mode(true);
579 core
.dispatch
->mode_change(true);
581 handle_load_core(core
.mlogic
->get_mfile(), portset
, false);
582 core
.mlogic
->get_mfile().is_savestate
= false;
583 core
.mlogic
->get_mfile().host_memory
.clear();
584 core
.mlogic
->get_movie().reset_state();
585 core
.fbuf
->redraw_framebuffer(core
.rom
->draw_cover());
586 core
.lua2
->callback_do_rewind();
587 } catch(std::bad_alloc
& e
) {
589 } catch(std::exception
& e
) {
590 core
.runmode
->set_corrupt();
591 core
.fbuf
->redraw_framebuffer(emu_framebuffer::screen_corrupt
, true);
594 messages
<< "Movie rewound to beginning." << std::endl
;
597 //Load state preserving input. Does not do checks.
598 void do_load_state_preserve(struct moviefile
& _movie
)
601 if(!*core
.mlogic
|| !core
.mlogic
->get_mfile().gametype
)
602 throw std::runtime_error("Can't load movie preserving input without previous movie");
603 if(core
.mlogic
->get_mfile().projectid
!= _movie
.projectid
)
604 throw std::runtime_error("Savestate is from different movie");
606 bool will_load_state
= _movie
.is_savestate
;
607 portctrl::type_set
& portset
= construct_movie_portset(core
.mlogic
->get_mfile(), *core
.rom
);
609 //Construct a new movie sharing the input data.
610 temporary_handle
<movie
> newmovie
;
611 newmovie
.get()->set_movie_data(core
.mlogic
->get_mfile().input
);
612 newmovie
.get()->readonly_mode(true);
613 newmovie
.get()->set_pflag_handler(&lsnes_pflag_handler
);
615 newmovie
.get()->load(_movie
.rerecords
, _movie
.projectid
, *_movie
.input
);
617 newmovie
.get()->restore_state(_movie
.save_frame
, _movie
.lagged_frames
, _movie
.pollcounters
, true,
618 _movie
.input
, _movie
.projectid
);
621 if(core
.mlogic
->get_rrdata().is_lazy() && !_movie
.lazy_project_create
)
622 core
.mlogic
->get_rrdata().read_base(rrdata::filename(_movie
.projectid
), false);
623 core
.mlogic
->get_rrdata().add((*core
.nrrdata
)());
627 handle_load_core(_movie
, portset
, will_load_state
);
628 } catch(std::bad_alloc
& e
) {
630 } catch(std::exception
& e
) {
631 core
.runmode
->set_corrupt();
632 core
.fbuf
->redraw_framebuffer(emu_framebuffer::screen_corrupt
, true);
637 core
.mlogic
->set_movie(*(newmovie()), true);
639 //Some fields MUST be taken from movie or one gets desyncs.
640 core
.mlogic
->get_mfile().is_savestate
= _movie
.is_savestate
;
641 core
.mlogic
->get_mfile().rtc_second
= _movie
.rtc_second
;
642 core
.mlogic
->get_mfile().rtc_subsecond
= _movie
.rtc_subsecond
;
643 std::swap(core
.mlogic
->get_mfile().host_memory
, _movie
.host_memory
);
645 core
.mlogic
->get_mfile().host_memory
.clear();
649 framebuffer::raw tmp
;
650 if(will_load_state
) {
651 tmp
.load(_movie
.screenshot
);
652 core
.fbuf
->redraw_framebuffer(tmp
);
654 core
.fbuf
->redraw_framebuffer(core
.rom
->draw_cover());
658 core
.dispatch
->mode_change(core
.mlogic
->get_movie().readonly_mode());
659 messages
<< "Loadstated at earlier point of movie." << std::endl
;
662 //Load state from loaded movie file. _movie is consumed.
663 void do_load_state(struct moviefile
& _movie
, int lmode
, bool& used
)
666 //Some basic sanity checks.
667 bool current_mode
= *core
.mlogic
? core
.mlogic
->get_movie().readonly_mode() : false;
668 bool will_load_state
= _movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
;
670 //Load state all branches and load state initial are the same.
671 if(lmode
== LOAD_STATE_ALLBRANCH
) lmode
= LOAD_STATE_INITIAL
;
674 if(_movie
.force_corrupt
)
675 throw std::runtime_error("Movie file invalid");
676 if(!core
.rom
->is_of_type(_movie
.gametype
->get_type()))
677 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
678 if(!core
.rom
->region_compatible_with(_movie
.gametype
->get_region()))
679 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
680 warn_coretype(_movie
.coreversion
, *core
.rom
, will_load_state
);
681 warn_roms(_movie
, *core
.rom
, will_load_state
);
683 //In certain conditions, trun LOAD_STATE_CURRENT into LOAD_STATE_PRESERVE.
684 if(lmode
== LOAD_STATE_CURRENT
&& current_mode
&& SET_readonly_load_preserves(*core
.settings
))
685 lmode
= LOAD_STATE_PRESERVE
;
686 //If movie file changes, turn LOAD_STATE_CURRENT into LOAD_STATE_RO
687 if(lmode
== LOAD_STATE_CURRENT
&& core
.mlogic
->get_mfile().projectid
!= _movie
.projectid
)
688 lmode
= LOAD_STATE_RO
;
690 //Handle preserving load specially.
691 if(lmode
== LOAD_STATE_PRESERVE
) {
692 do_load_state_preserve(_movie
);
697 //Create a new movie, and if needed, restore the movie state.
698 temporary_handle
<movie
> newmovie
;
699 newmovie
.get()->set_movie_data(_movie
.input
);
700 newmovie
.get()->load(_movie
.rerecords
, _movie
.projectid
, *_movie
.input
);
701 newmovie
.get()->set_pflag_handler(&lsnes_pflag_handler
);
703 newmovie
.get()->restore_state(_movie
.save_frame
, _movie
.lagged_frames
, _movie
.pollcounters
, true,
704 NULL
, _movie
.projectid
);
706 //Copy the other branches.
707 if(lmode
!= LOAD_STATE_INITIAL
&& core
.mlogic
->get_mfile().projectid
== _movie
.projectid
) {
708 newmovie
.get()->set_movie_data(NULL
);
709 auto& oldm
= core
.mlogic
->get_mfile().branches
;
710 auto& newm
= _movie
.branches
;
711 auto oldd
= core
.mlogic
->get_mfile().input
;
712 auto newd
= _movie
.input
;
713 std::string dflt_name
;
714 //What was the old default name?
716 if(&i
.second
== oldd
)
718 //Rename the default to match with old movie if the names differ.
719 if(!newm
.count(dflt_name
) || &newm
[dflt_name
] != newd
)
720 newm
[dflt_name
] = *newd
;
721 //Copy all other branches.
723 if(i
.first
!= dflt_name
)
724 newm
[i
.first
] = i
.second
;
725 //Delete branches that didn't exist.
726 for(auto i
= newm
.begin(); i
!= newm
.end();) {
727 if(oldm
.count(i
->first
))
732 _movie
.input
= &newm
[dflt_name
];
733 newmovie
.get()->set_movie_data(_movie
.input
);
736 portctrl::type_set
& portset
= construct_movie_portset(_movie
, *core
.rom
);
738 temporary_handle
<rrdata_set
> rrd
;
739 bool new_rrdata
= false;
740 //Count a rerecord (against new or old movie).
741 if(!*core
.mlogic
|| _movie
.projectid
!= core
.mlogic
->get_mfile().projectid
) {
742 rrd
.get()->read_base(rrdata::filename(_movie
.projectid
), _movie
.lazy_project_create
);
743 rrd
.get()->read(_movie
.c_rrdata
);
744 rrd
.get()->add((*core
.nrrdata
)());
747 //Unlazy rrdata if needed.
748 if(core
.mlogic
->get_rrdata().is_lazy() && !_movie
.lazy_project_create
)
749 core
.mlogic
->get_rrdata().read_base(rrdata::filename(_movie
.projectid
), false);
750 core
.mlogic
->get_rrdata().add((*core
.nrrdata
)());
754 core
.rom
->set_internal_region(_movie
.gametype
->get_region());
755 handle_load_core(_movie
, portset
, will_load_state
);
756 } catch(std::bad_alloc
& e
) {
758 } catch(std::exception
& e
) {
759 core
.runmode
->set_corrupt();
760 core
.fbuf
->redraw_framebuffer(emu_framebuffer::screen_corrupt
, true);
764 //If loaded a movie, clear the is savestate and rrdata.
765 if(!will_load_state
) {
766 _movie
.is_savestate
= false;
767 _movie
.host_memory
.clear();
770 core
.lua2
->callback_movie_lost("load");
773 if(new_rrdata
) core
.mlogic
->set_rrdata(*(rrd()), true);
774 core
.mlogic
->set_movie(*newmovie(), true);
775 core
.mlogic
->set_mfile(_movie
, true);
778 set_mprefix(get_mprefix_for_project(core
.mlogic
->get_mfile().projectid
));
780 //Activate RW mode if needed.
781 auto& m
= core
.mlogic
->get_movie();
782 if(lmode
== LOAD_STATE_RW
)
783 m
.readonly_mode(false);
784 if(lmode
== LOAD_STATE_DEFAULT
&& !current_mode
&& m
.get_frame_count() <= m
.get_current_frame())
785 m
.readonly_mode(false);
786 if(lmode
== LOAD_STATE_INITIAL
&& m
.get_frame_count() <= m
.get_current_frame())
787 m
.readonly_mode(false);
788 if(lmode
== LOAD_STATE_CURRENT
&& !current_mode
)
789 m
.readonly_mode(false);
793 framebuffer::raw tmp
;
794 if(will_load_state
) {
795 tmp
.load(_movie
.screenshot
);
796 core
.fbuf
->redraw_framebuffer(tmp
);
798 core
.fbuf
->redraw_framebuffer(core
.rom
->draw_cover());
801 core
.dispatch
->mode_change(m
.readonly_mode());
802 print_movie_info(_movie
, *core
.rom
, core
.mlogic
->get_rrdata());
803 core
.dispatch
->mbranch_change();
804 set_gameinfo(core
.mlogic
->get_mfile());
808 void try_request_rom(const std::string
& moviefile
)
811 moviefile::brief_info
info(moviefile
);
812 auto sysregs
= core_sysregion::find_matching(info
.sysregion
);
816 req
.core_guessed
= false;
817 for(auto i
: sysregs
) {
818 //FIXME: Do something with this?
819 //if(i->get_type().get_biosname() != "" && info.hash[1] != "")
821 req
.cores
.push_back(&i
->get_type());
822 if(i
->get_type().get_core_identifier() == info
.corename
) {
824 req
.core_guessed
= true;
828 if(req
.cores
.empty())
829 throw std::runtime_error("No known core can load movie of type '" + info
.sysregion
+ "'");
830 for(unsigned i
= 0; i
< ROM_SLOT_COUNT
; i
++) {
831 req
.guessed
[i
] = false;
832 req
.has_slot
[i
] = (info
.hash
[i
] != "");
833 req
.filename
[i
] = info
.hint
[i
];
834 req
.hash
[i
] = info
.hash
[i
];
835 req
.hashxml
[i
] = info
.hashxml
[i
];
839 graphics_driver_request_rom(req
);
841 throw std::runtime_error("Canceled loading ROM");
842 //Try to load the ROM using specified core.
843 if(req
.selected
>= req
.cores
.size())
844 throw std::runtime_error("Invalid ROM type selected");
845 core_type
* selected_core
= req
.cores
[req
.selected
];
846 loaded_rom
newrom(req
.filename
, selected_core
->get_core_identifier(), selected_core
->get_iname(), "");
848 core
.dispatch
->core_change();
852 bool do_load_state(const std::string
& filename
, int lmode
)
856 std::string filename2
= translate_name_mprefix(filename
, tmp
, -1);
857 uint64_t origtime
= framerate_regulator::get_utime();
858 core
.lua2
->callback_pre_load(filename2
);
859 struct moviefile
* mfile
= NULL
;
862 if(core
.rom
->isnull())
863 try_request_rom(filename2
);
864 mfile
= new moviefile(filename2
, core
.rom
->get_internal_rom_type());
865 } catch(std::bad_alloc
& e
) {
867 } catch(std::exception
& e
) {
868 platform::error_message(std::string("Can't read movie/savestate: ") + e
.what());
869 messages
<< "Can't read movie/savestate '" << filename2
<< "': " << e
.what() << std::endl
;
870 core
.lua2
->callback_err_load(filename2
);
874 do_load_state(*mfile
, lmode
, used
);
875 uint64_t took
= framerate_regulator::get_utime() - origtime
;
876 messages
<< "Loaded '" << filename2
<< "' in " << took
<< " microseconds." << std::endl
;
877 core
.lua2
->callback_post_load(filename2
, core
.mlogic
->get_mfile().is_savestate
);
878 } catch(std::bad_alloc
& e
) {
880 } catch(std::exception
& e
) {
883 platform::error_message(std::string("Can't load movie/savestate: ") + e
.what());
884 messages
<< "Can't load movie/savestate '" << filename2
<< "': " << e
.what() << std::endl
;
885 core
.lua2
->callback_err_load(filename2
);
891 void mainloop_restore_state(const std::vector
<char>& state
, uint64_t secs
, uint64_t ssecs
)
894 //Force unlazy rrdata.
895 core
.mlogic
->get_rrdata().read_base(rrdata::filename(core
.mlogic
->get_mfile().projectid
),
897 core
.mlogic
->get_rrdata().add((*core
.nrrdata
)());
898 core
.mlogic
->get_mfile().rtc_second
= secs
;
899 core
.mlogic
->get_mfile().rtc_subsecond
= ssecs
;
900 core
.rom
->load_core_state(state
, true);
908 rrdata_set::instance
rrdata::operator()()
911 next
= rrdata_set::instance(get_random_hexstring(2 * RRDATA_BYTES
));
916 std::string
rrdata::filename(const std::string
& projectid
)
918 return get_config_path() + "/" + projectid
+ ".rr";