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"
27 struct loaded_rom our_rom
;
29 std::string last_save
;
30 void update_movie_state();
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
;
42 std::string
get_mprefix()
44 threads::alock
h(mprefix_lock
);
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());
59 messages
<< "Saved core state to " << name
<< std::endl
;
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())
68 messages
<< "WARNING: " << name
<< " hash mismatch!" << std::endl
69 << "\tMovie: " << mhash
<< std::endl
70 << "\tOur ROM: " << slot
.sha_256
.read() << std::endl
;
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
;
81 void set_mprefix(const std::string
& pfx
)
84 threads::alock
h(mprefix_lock
);
85 mprefix_valid
= (pfx
!= "");
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
);
98 std::getline(strm
, pfx
);
102 class _lsnes_pflag_handler
: public movie::poll_flag
105 ~_lsnes_pflag_handler()
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
);
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
);
143 binary
= jukebox_dflt_binary
? 1 : 0;
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
))
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";
159 std::string pprf
= lsnes_instance
.setcache
.get("slotpath") + "/";
160 return pprf
+ get_mprefix() + r
[1] + ".lsmv";
164 binary
= (save
? save_dflt_binary
: movie_dflt_binary
) ? 1 : 0;
169 std::pair
<std::string
, std::string
> split_author(const std::string
& author
) throw(std::bad_alloc
,
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()) {
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
)
191 return get_absolute_path(path
);
198 void do_save_state(const std::string
& filename
, int binary
) throw(std::bad_alloc
,
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
;
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);
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();
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."
234 lua_callback_post_save(filename2
, true);
235 } catch(std::bad_alloc
& e
) {
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();
245 p
->last_save
= last_save
;
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
;
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);
262 uint64_t origtime
= get_utime();
263 target
.is_savestate
= false;
264 auto prj
= project_get();
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."
275 lua_callback_post_save(filename2
, false);
276 } catch(std::bad_alloc
& e
) {
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();
286 p
->last_save
= last_save
;
291 extern time_t random_seed_value
;
295 void populate_volatile_ram(moviefile
& mf
, std::list
<core_vma_info
>& vmas
)
298 //Only regions that are marked as volatile, readwrite not special are initializable.
299 if(!i
.volatile_flag
|| i
.readonly
|| i
.special
)
301 if(!mf
.ramcontent
.count(i
.name
))
303 uint64_t csize
= min((uint64_t)mf
.ramcontent
[i
.name
].size(), i
.size
);
305 memcpy(i
.backing_ram
, &mf
.ramcontent
[i
.name
][0], csize
);
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 << ":";
321 x
<< std::setfill('0') << std::setw(2) << mlength
/ 1000ULL << ".";
323 x
<< std::setfill('0') << std::setw(3) << mlength
;
327 void print_movie_info(moviefile
& mov
, loaded_rom
& rom
, rrdata_set
& rrd
)
330 messages
<< "ROM Type " << rom
.rtype
->get_hname() << " region " << rom
.region
->get_hname()
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
;
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
)
353 std::string rom_core
= against
.rtype
->get_core_identifier();
354 if(mov_core
== rom_core
)
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
;
362 throw std::runtime_error(x
.str());
367 void warn_roms(moviefile
& mov
, loaded_rom
& against
, bool loadstate
)
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
);
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
);
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
);
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
);
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
) {
460 } catch(std::exception
& e
) {
461 system_corrupt
= true;
462 redraw_framebuffer(screen_corrupt
, true);
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");
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
) {
512 } catch(std::exception
& e
) {
513 system_corrupt
= true;
514 redraw_framebuffer(screen_corrupt
, true);
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);
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
) {
554 } catch(std::exception
& e
) {
555 system_corrupt
= true;
556 redraw_framebuffer(screen_corrupt
, true);
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
);
581 newmovie
.get()->restore_state(_movie
.save_frame
, _movie
.lagged_frames
, _movie
.pollcounters
, true,
582 _movie
.input
, _movie
.projectid
);
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());
591 handle_load_core(_movie
, portset
, will_load_state
);
592 } catch(std::bad_alloc
& e
) {
594 } catch(std::exception
& e
) {
595 system_corrupt
= true;
596 redraw_framebuffer(screen_corrupt
, true);
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
);
609 lsnes_instance
.mlogic
.get_mfile().host_memory
.clear();
613 framebuffer::raw tmp
;
614 if(will_load_state
) {
615 tmp
.load(_movie
.screenshot
);
616 redraw_framebuffer(tmp
);
618 redraw_framebuffer(our_rom
.rtype
->draw_cover());
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
;
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
);
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
);
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?
679 if(&i
.second
== oldd
)
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.
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
))
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());
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());
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
) {
721 } catch(std::exception
& e
) {
722 system_corrupt
= true;
723 redraw_framebuffer(screen_corrupt
, true);
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");
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);
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);
756 framebuffer::raw tmp
;
757 if(will_load_state
) {
758 tmp
.load(_movie
.screenshot
);
759 redraw_framebuffer(tmp
);
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
);
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] != "")
782 req
.cores
.push_back(&i
->get_type());
783 if(i
->get_type().get_core_identifier() == info
.corename
) {
785 req
.core_guessed
= true;
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
];
800 graphics_driver_request_rom(req
);
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(), "");
809 notify_core_change();
813 bool do_load_state(const std::string
& filename
, int lmode
)
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
;
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
) {
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
);
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
) {
840 } catch(std::exception
& e
) {
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
);
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
),
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);