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"
9 #include "core/misc.hpp"
10 #include "core/moviedata.hpp"
11 #include "core/rrdata.hpp"
12 #include "core/settings.hpp"
13 #include "library/string.hpp"
14 #include "interface/romtype.hpp"
19 #include <boost/filesystem.hpp>
21 #ifdef BOOST_FILESYSTEM3
22 namespace boost_fs
= boost::filesystem3
;
24 namespace boost_fs
= boost::filesystem
;
27 struct moviefile our_movie
;
28 struct loaded_rom
* our_rom
;
31 std::string last_save
;
35 time_t __wrap_time(time_t* t
)
37 time_t v
= static_cast<time_t>(our_movie
.rtc_second
);
44 std::vector
<char>& get_host_memory()
46 return our_movie
.host_memory
;
51 return movb
.get_movie();
56 numeric_setting
savecompression(lsnes_set
, "savecompression", 0, 9, 7);
57 boolean_setting
readonly_load_preserves(lsnes_set
, "preserve_on_readonly_load", true);
59 path_setting
slotpath_setting(lsnes_set
, "slotpath");
61 class projectprefix_setting
: public setting
66 projectprefix_setting() throw(std::bad_alloc
)
67 : setting(lsnes_set
, "$project")
71 bool blank(bool really
) throw(std::bad_alloc
, std::runtime_error
)
82 void set(const std::string
& value
) throw(std::bad_alloc
, std::runtime_error
)
87 std::string
get() throw(std::bad_alloc
)
91 operator std::string() throw()
93 lock_holder
lck(this);
101 function_ptr_command
<const std::string
&> dump_coresave(lsnes_cmd
, "dump-coresave", "Dump bsnes core state",
102 "Syntax: dump-coresave <name>\nDumps core save to <name>\n",
103 [](const std::string
& name
) throw(std::bad_alloc
, std::runtime_error
) {
104 auto x
= our_rom
->save_core_state();
105 x
.resize(x
.size() - 32);
106 std::ofstream
y(name
.c_str(), std::ios::out
| std::ios::binary
);
107 y
.write(&x
[0], x
.size());
109 messages
<< "Saved core state to " << name
<< std::endl
;
112 bool warn_hash_mismatch(const std::string
& mhash
, const loaded_slot
& slot
,
113 const std::string
& name
, bool fatal
)
115 if(mhash
== slot
.sha_256
)
118 messages
<< "WARNING: " << name
<< " hash mismatch!" << std::endl
119 << "\tMovie: " << mhash
<< std::endl
120 << "\tOur ROM: " << slot
.sha_256
<< std::endl
;
123 messages
<< "ERROR: " << name
<< " hash mismatch!" << std::endl
124 << "\tMovie: " << mhash
<< std::endl
125 << "\tOur ROM: " << slot
.sha_256
<< std::endl
;
131 std::string
translate_name_mprefix(std::string original
, bool forio
)
133 size_t prefixloc
= original
.find("${project}");
134 if(prefixloc
< original
.length()) {
135 std::string pprf
= forio
? (slotpath_setting
.get() + "/") : std::string("");
137 return pprf
+ static_cast<std::string
>(mprefix
) + original
.substr(prefixloc
+ 10);
139 return original
.substr(0, prefixloc
) + static_cast<std::string
>(mprefix
) +
140 original
.substr(prefixloc
+ 10);
145 std::pair
<std::string
, std::string
> split_author(const std::string
& author
) throw(std::bad_alloc
,
148 std::string _author
= author
;
149 std::string fullname
;
150 std::string nickname
;
151 size_t split
= _author
.find_first_of("|");
152 if(split
>= _author
.length()) {
155 fullname
= _author
.substr(0, split
);
156 nickname
= _author
.substr(split
+ 1);
158 if(fullname
== "" && nickname
== "")
159 throw std::runtime_error("Bad author name");
160 return std::make_pair(fullname
, nickname
);
165 void do_save_state(const std::string
& filename
) throw(std::bad_alloc
,
168 if(!our_movie
.gametype
) {
169 messages
<< "Can't save movie without a ROM" << std::endl
;
172 std::string filename2
= translate_name_mprefix(filename
, true);
173 lua_callback_pre_save(filename2
, true);
175 uint64_t origtime
= get_utime();
177 our_movie
.prefix
= sanitize_prefix(mprefix
.prefix
);
178 our_movie
.is_savestate
= true;
179 our_movie
.sram
= our_rom
->rtype
->save_sram();
180 for(size_t i
= 0; i
< sizeof(our_rom
->romimg
)/sizeof(our_rom
->romimg
[0]); i
++) {
181 our_movie
.romimg_sha256
[i
] = our_rom
->romimg
[i
].sha_256
;
182 our_movie
.romxml_sha256
[i
] = our_rom
->romxml
[i
].sha_256
;
184 our_movie
.savestate
= our_rom
->save_core_state();
185 get_framebuffer().save(our_movie
.screenshot
);
186 movb
.get_movie().save_state(our_movie
.projectid
, our_movie
.save_frame
, our_movie
.lagged_frames
,
187 our_movie
.pollcounters
);
188 our_movie
.input
= movb
.get_movie().save();
189 our_movie
.save(filename2
, savecompression
);
190 our_movie
.poll_flag
= our_rom
->rtype
->get_pflag();
191 uint64_t took
= get_utime() - origtime
;
192 messages
<< "Saved state '" << filename2
<< "' in " << took
<< " microseconds." << std::endl
;
193 lua_callback_post_save(filename2
, true);
194 } catch(std::bad_alloc
& e
) {
196 } catch(std::exception
& e
) {
197 messages
<< "Save failed: " << e
.what() << std::endl
;
198 lua_callback_err_save(filename2
);
200 last_save
= boost_fs::absolute(boost_fs::path(filename2
)).string();
204 void do_save_movie(const std::string
& filename
) throw(std::bad_alloc
, std::runtime_error
)
206 if(!our_movie
.gametype
) {
207 messages
<< "Can't save movie without a ROM" << std::endl
;
210 std::string filename2
= translate_name_mprefix(filename
, true);
211 lua_callback_pre_save(filename2
, false);
213 uint64_t origtime
= get_utime();
215 our_movie
.prefix
= sanitize_prefix(static_cast<std::string
>(mprefix
.prefix
));
216 our_movie
.is_savestate
= false;
217 our_movie
.input
= movb
.get_movie().save();
218 our_movie
.save(filename2
, savecompression
);
219 uint64_t took
= get_utime() - origtime
;
220 messages
<< "Saved movie '" << filename2
<< "' in " << took
<< " microseconds." << std::endl
;
221 lua_callback_post_save(filename2
, false);
222 } catch(std::bad_alloc
& e
) {
224 } catch(std::exception
& e
) {
225 messages
<< "Save failed: " << e
.what() << std::endl
;
226 lua_callback_err_save(filename2
);
228 last_save
= boost_fs::absolute(boost_fs::path(filename2
)).string();
231 extern time_t random_seed_value
;
233 void do_load_beginning(bool reload
) throw(std::bad_alloc
, std::runtime_error
)
235 bool force_rw
= false;
236 if(!our_movie
.gametype
&& !reload
) {
237 messages
<< "Can't load movie without a ROM" << std::endl
;
243 //Force unlazy rrdata.
244 rrdata::read_base(our_movie
.projectid
, false);
245 rrdata::add_internal();
247 auto ctrldata
= our_rom
->rtype
->controllerconfig(our_movie
.settings
);
248 port_type_set
& portset
= port_type_set::make(ctrldata
.ports
, ctrldata
.portindex
);
249 controls
.set_ports(portset
);
250 if(our_movie
.input
.get_types() != portset
) {
251 //The input type changes, so set the types.
253 our_movie
.input
.clear(portset
);
254 movb
.get_movie().load(our_movie
.rerecords
, our_movie
.projectid
, our_movie
.input
);
258 bool ro
= movb
.get_movie().readonly_mode() && !force_rw
;
259 movb
.get_movie().reset_state();
260 random_seed_value
= our_movie
.movie_rtc_second
;
261 our_rom
->load(our_movie
.settings
, our_movie
.movie_rtc_second
, our_movie
.movie_rtc_subsecond
);
262 our_movie
.gametype
= &our_rom
->rtype
->combine_region(*our_rom
->region
);
264 movb
.get_movie().readonly_mode(ro
);
266 our_rom
->rtype
->load_sram(our_movie
.movie_sram
);
267 our_movie
.rtc_second
= our_movie
.movie_rtc_second
;
268 our_movie
.rtc_subsecond
= our_movie
.movie_rtc_subsecond
;
269 if(!our_movie
.anchor_savestate
.empty())
270 our_rom
->load_core_state(our_movie
.anchor_savestate
);
271 our_rom
->rtype
->set_pflag(0);
272 redraw_framebuffer(our_rom
->rtype
->draw_cover());
273 lua_callback_do_rewind();
274 } catch(std::bad_alloc
& e
) {
276 } catch(std::exception
& e
) {
277 system_corrupt
= true;
278 redraw_framebuffer(screen_corrupt
, true);
281 information_dispatch::do_mode_change(movb
.get_movie().readonly_mode());
282 our_movie
.is_savestate
= false;
283 our_movie
.host_memory
.clear();
285 messages
<< "ROM reloaded." << std::endl
;
287 messages
<< "Movie rewound to beginning." << std::endl
;
290 //Load state from loaded movie file (does not catch errors).
291 void do_load_state(struct moviefile
& _movie
, int lmode
)
293 bool current_mode
= movb
.get_movie().readonly_mode();
294 if(_movie
.force_corrupt
)
295 throw std::runtime_error("Movie file invalid");
296 bool will_load_state
= _movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
;
297 if(our_rom
->rtype
&& &(_movie
.gametype
->get_type()) != our_rom
->rtype
)
298 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
299 if(our_rom
->orig_region
&& !our_rom
->orig_region
->compatible_with(_movie
.gametype
->get_region()))
300 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
302 if(our_rom
->rtype
&& _movie
.coreversion
!= our_rom
->rtype
->get_core_identifier()) {
303 if(will_load_state
) {
304 std::ostringstream x
;
305 x
<< "ERROR: Emulator core version mismatch!" << std::endl
306 << "\tThis version: " << our_rom
->rtype
->get_core_identifier() << std::endl
307 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
308 throw std::runtime_error(x
.str());
310 messages
<< "WARNING: Emulator core version mismatch!" << std::endl
311 << "\tThis version: " << our_rom
->rtype
->get_core_identifier() << std::endl
312 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
315 for(size_t i
= 0; i
< sizeof(our_rom
->romimg
)/sizeof(our_rom
->romimg
[0]); i
++) {
316 rom_ok
= rom_ok
& warn_hash_mismatch(_movie
.romimg_sha256
[i
], our_rom
->romimg
[i
],
317 (stringfmt() << "ROM #" << (i
+ 1)).str(), will_load_state
);
318 rom_ok
= rom_ok
& warn_hash_mismatch(_movie
.romxml_sha256
[i
], our_rom
->romxml
[i
],
319 (stringfmt() << "XML #" << (i
+ 1)).str(), will_load_state
);
322 throw std::runtime_error("Incorrect ROM");
324 if(lmode
== LOAD_STATE_CURRENT
&& movb
.get_movie().readonly_mode() && readonly_load_preserves
)
325 lmode
= LOAD_STATE_PRESERVE
;
328 if(lmode
== LOAD_STATE_PRESERVE
)
329 newmovie
= movb
.get_movie();
331 newmovie
.load(_movie
.rerecords
, _movie
.projectid
, _movie
.input
);
334 newmovie
.restore_state(_movie
.save_frame
, _movie
.lagged_frames
, _movie
.pollcounters
, true,
335 (lmode
== LOAD_STATE_PRESERVE
) ? &_movie
.input
: NULL
, _movie
.projectid
);
337 auto ctrldata
= our_rom
->rtype
->controllerconfig(_movie
.settings
);
338 port_type_set
& portset
= port_type_set::make(ctrldata
.ports
, ctrldata
.portindex
);
341 rrdata::read_base(_movie
.projectid
, _movie
.lazy_project_create
);
342 rrdata::read(_movie
.c_rrdata
);
343 rrdata::add_internal();
345 our_rom
->region
= _movie
.gametype
? &(_movie
.gametype
->get_region()) : NULL
;
346 random_seed_value
= _movie
.movie_rtc_second
;
347 our_rom
->load(_movie
.settings
, _movie
.movie_rtc_second
, _movie
.movie_rtc_subsecond
);
349 if(will_load_state
) {
350 //Load the savestate and movie state.
351 //Set the core ports in order to avoid port state being reinitialized when loading.
352 controls
.set_ports(portset
);
353 our_rom
->load_core_state(_movie
.savestate
);
354 our_rom
->rtype
->set_pflag(_movie
.poll_flag
);
356 our_rom
->rtype
->load_sram(_movie
.movie_sram
);
357 controls
.set_ports(portset
);
358 _movie
.rtc_second
= _movie
.movie_rtc_second
;
359 _movie
.rtc_subsecond
= _movie
.movie_rtc_subsecond
;
360 if(!_movie
.anchor_savestate
.empty())
361 our_rom
->load_core_state(_movie
.anchor_savestate
);
362 our_rom
->rtype
->set_pflag(0);
364 } catch(std::bad_alloc
& e
) {
366 } catch(std::exception
& e
) {
367 system_corrupt
= true;
368 redraw_framebuffer(screen_corrupt
, true);
372 //Okay, copy the movie data.
373 if(lmode
!= LOAD_STATE_PRESERVE
)
376 //The is_savestate MUST be taken from movie (except LOAD_STATE_MOVIE), or one gets desyncs.
377 our_movie
.is_savestate
= _movie
.is_savestate
;
379 if(!our_movie
.is_savestate
|| lmode
== LOAD_STATE_MOVIE
) {
380 our_movie
.is_savestate
= false;
381 our_movie
.host_memory
.clear();
383 if(our_movie
.prefix
!= "" && lmode
!= LOAD_STATE_PRESERVE
) {
384 mprefix
.prefix
= our_movie
.prefix
;
387 movb
.get_movie() = newmovie
;
388 //Activate RW mode if needed.
389 if(lmode
== LOAD_STATE_RW
)
390 movb
.get_movie().readonly_mode(false);
391 if(lmode
== LOAD_STATE_DEFAULT
&& !current_mode
&&
392 movb
.get_movie().get_frame_count() <= movb
.get_movie().get_current_frame())
393 movb
.get_movie().readonly_mode(false);
394 if(lmode
== LOAD_STATE_INITIAL
&& movb
.get_movie().get_frame_count() <= movb
.get_movie().get_current_frame())
395 movb
.get_movie().readonly_mode(false);
396 if(lmode
== LOAD_STATE_CURRENT
&& !current_mode
)
397 movb
.get_movie().readonly_mode(false);
401 if(will_load_state
) {
402 tmp
.load(_movie
.screenshot
);
403 redraw_framebuffer(tmp
);
405 redraw_framebuffer(our_rom
->rtype
->draw_cover());
407 information_dispatch::do_mode_change(movb
.get_movie().readonly_mode());
409 messages
<< "ROM Type " << our_rom
->rtype
->get_hname() << " region " << our_rom
->region
->get_hname()
411 uint64_t mlength
= _movie
.get_movie_length();
414 std::ostringstream x
;
415 if(mlength
> 3600000000000) {
416 x
<< mlength
/ 3600000000000 << ":";
417 mlength
%= 3600000000000;
419 x
<< std::setfill('0') << std::setw(2) << mlength
/ 60000000000 << ":";
420 mlength
%= 60000000000;
421 x
<< std::setfill('0') << std::setw(2) << mlength
/ 1000000000 << ".";
422 mlength
%= 1000000000;
423 x
<< std::setfill('0') << std::setw(3) << mlength
/ 1000000;
424 std::string rerecords
= _movie
.rerecords
;
425 if(lmode
!= LOAD_STATE_MOVIE
)
426 rerecords
= (stringfmt() << rrdata::count()).str();
427 messages
<< "Rerecords " << rerecords
<< " length " << x
.str() << " ("
428 << _movie
.get_frame_count() << " frames)" << std::endl
;
430 if(_movie
.gamename
!= "")
431 messages
<< "Game Name: " << _movie
.gamename
<< std::endl
;
432 for(size_t i
= 0; i
< _movie
.authors
.size(); i
++)
433 messages
<< "Author: " << _movie
.authors
[i
].first
<< "(" << _movie
.authors
[i
].second
<< ")"
438 bool do_load_state(const std::string
& filename
, int lmode
)
440 std::string filename2
= translate_name_mprefix(filename
, true);
441 uint64_t origtime
= get_utime();
442 lua_callback_pre_load(filename2
);
443 struct moviefile mfile
;
445 mfile
= moviefile(filename2
, *our_rom
->rtype
);
446 } catch(std::bad_alloc
& e
) {
448 } catch(std::exception
& e
) {
449 messages
<< "Can't read movie/savestate '" << filename2
<< "': " << e
.what() << std::endl
;
450 lua_callback_err_load(filename2
);
454 do_load_state(mfile
, lmode
);
455 uint64_t took
= get_utime() - origtime
;
456 messages
<< "Loaded '" << filename2
<< "' in " << took
<< " microseconds." << std::endl
;
457 lua_callback_post_load(filename2
, our_movie
.is_savestate
);
458 } catch(std::bad_alloc
& e
) {
460 } catch(std::exception
& e
) {
461 messages
<< "Can't load movie/savestate '" << filename2
<< "': " << e
.what() << std::endl
;
462 lua_callback_err_load(filename2
);
468 void mainloop_restore_state(const std::vector
<char>& state
, uint64_t secs
, uint64_t ssecs
)
470 //Force unlazy rrdata.
471 rrdata::read_base(our_movie
.projectid
, false);
472 rrdata::add_internal();
473 our_movie
.rtc_second
= secs
;
474 our_movie
.rtc_subsecond
= ssecs
;
475 our_rom
->load_core_state(state
, true);