1 #include "moviedata.hpp"
3 #include "framerate.hpp"
6 #include "framebuffer.hpp"
10 #include <snes/snes.hpp>
11 #include <ui-libsnes/libsnes.hpp>
13 #include "settings.hpp"
14 #include "controller.hpp"
16 struct moviefile our_movie
;
17 struct loaded_rom
* our_rom
;
23 time_t __wrap_time(time_t* t
)
25 time_t v
= static_cast<time_t>(our_movie
.rtc_second
);
32 std::vector
<char>& get_host_memory()
34 return our_movie
.host_memory
;
39 return movb
.get_movie();
44 numeric_setting
savecompression("savecompression", 0, 9, 7);
47 function_ptr_command
<> get_gamename("get-gamename", "Get the game name",
48 "Syntax: get-gamename\nPrints the game name\n",
49 []() throw(std::bad_alloc
, std::runtime_error
) {
50 messages
<< "Game name is '" << our_movie
.gamename
<< "'" << std::endl
;
53 function_ptr_command
<const std::string
&> set_gamename("set-gamename", "Set the game name",
54 "Syntax: set-gamename <name>\nSets the game name to <name>\n",
55 [](const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
) {
56 our_movie
.gamename
= args
;
57 messages
<< "Game name changed to '" << our_movie
.gamename
<< "'" << std::endl
;
60 function_ptr_command
<> show_authors("show-authors", "Show the run authors",
61 "Syntax: show-authors\nShows the run authors\n",
62 []() throw(std::bad_alloc
, std::runtime_error
)
65 for(auto i
: our_movie
.authors
) {
66 messages
<< (idx
++) << ": " << i
.first
<< "|" << i
.second
<< std::endl
;
68 messages
<< "End of authors list" << std::endl
;
71 function_ptr_command
<tokensplitter
&> add_author("add-author", "Add an author",
72 "Syntax: add-author <fullname>\nSyntax: add-author |<nickname>\n"
73 "Syntax: add-author <fullname>|<nickname>\nAdds a new author\n",
74 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
75 auto g
= split_author(t
.tail());
76 our_movie
.authors
.push_back(g
);
77 messages
<< (our_movie
.authors
.size() - 1) << ": " << g
.first
<< "|" << g
.second
<< std::endl
;
80 function_ptr_command
<tokensplitter
&> remove_author("remove-author", "Remove an author",
81 "Syntax: remove-author <id>\nRemoves author with ID <id>\n",
82 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
83 uint64_t index
= parse_value
<uint64_t>(t
.tail());
84 if(index
>= our_movie
.authors
.size())
85 throw std::runtime_error("No such author");
86 our_movie
.authors
.erase(our_movie
.authors
.begin() + index
);
89 function_ptr_command
<tokensplitter
&> edit_author("edit-author", "Edit an author",
90 "Syntax: edit-author <authorid> <fullname>\nSyntax: edit-author <authorid> |<nickname>\n"
91 "Syntax: edit-author <authorid> <fullname>|<nickname>\nEdits author name\n",
92 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
93 uint64_t index
= parse_value
<uint64_t>(t
);
94 if(index
>= our_movie
.authors
.size())
95 throw std::runtime_error("No such author");
96 auto g
= split_author(t
.tail());
97 our_movie
.authors
[index
] = g
;
100 void warn_hash_mismatch(const std::string
& mhash
, const loaded_slot
& slot
,
101 const std::string
& name
)
103 if(mhash
!= slot
.sha256
) {
104 messages
<< "WARNING: " << name
<< " hash mismatch!" << std::endl
105 << "\tMovie: " << mhash
<< std::endl
106 << "\tOur ROM: " << slot
.sha256
<< std::endl
;
111 std::pair
<std::string
, std::string
> split_author(const std::string
& author
) throw(std::bad_alloc
,
114 std::string _author
= author
;
115 std::string fullname
;
116 std::string nickname
;
117 size_t split
= _author
.find_first_of("|");
118 if(split
>= _author
.length()) {
121 fullname
= _author
.substr(0, split
);
122 nickname
= _author
.substr(split
+ 1);
124 if(fullname
== "" && nickname
== "")
125 throw std::runtime_error("Bad author name");
126 return std::make_pair(fullname
, nickname
);
131 void do_save_state(const std::string
& filename
) throw(std::bad_alloc
,
134 lua_callback_pre_save(filename
, true);
136 uint64_t origtime
= get_utime();
137 our_movie
.is_savestate
= true;
138 our_movie
.sram
= save_sram();
139 our_movie
.savestate
= save_core_state();
140 framebuffer
.save(our_movie
.screenshot
);
141 auto s
= movb
.get_movie().save_state();
142 our_movie
.movie_state
.resize(s
.size());
143 memcpy(&our_movie
.movie_state
[0], &s
[0], s
.size());
144 our_movie
.input
= movb
.get_movie().save();
145 our_movie
.save(filename
, savecompression
);
146 uint64_t took
= get_utime() - origtime
;
147 messages
<< "Saved state '" << filename
<< "' in " << took
<< " microseconds." << std::endl
;
148 lua_callback_post_save(filename
, true);
149 } catch(std::bad_alloc
& e
) {
151 } catch(std::exception
& e
) {
152 messages
<< "Save failed: " << e
.what() << std::endl
;
153 lua_callback_err_save(filename
);
158 void do_save_movie(const std::string
& filename
) throw(std::bad_alloc
, std::runtime_error
)
160 lua_callback_pre_save(filename
, false);
162 uint64_t origtime
= get_utime();
163 our_movie
.is_savestate
= false;
164 our_movie
.input
= movb
.get_movie().save();
165 our_movie
.save(filename
, savecompression
);
166 uint64_t took
= get_utime() - origtime
;
167 messages
<< "Saved movie '" << filename
<< "' in " << took
<< " microseconds." << std::endl
;
168 lua_callback_post_save(filename
, false);
169 } catch(std::bad_alloc
& e
) {
171 } catch(std::exception
& e
) {
172 messages
<< "Save failed: " << e
.what() << std::endl
;
173 lua_callback_err_save(filename
);
177 //Load state from loaded movie file (does not catch errors).
178 void do_load_state(struct moviefile
& _movie
, int lmode
)
180 bool current_mode
= movb
.get_movie().readonly_mode();
181 if(_movie
.force_corrupt
)
182 throw std::runtime_error("Movie file invalid");
183 bool will_load_state
= _movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
;
184 if(gtype::toromtype(_movie
.gametype
) != our_rom
->rtype
)
185 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
186 if(gtype::toromregion(_movie
.gametype
) != our_rom
->orig_region
&& our_rom
->orig_region
!= REGION_AUTO
)
187 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
189 if(_movie
.coreversion
!= bsnes_core_version
) {
190 if(will_load_state
) {
191 std::ostringstream x
;
192 x
<< "ERROR: Emulator core version mismatch!" << std::endl
193 << "\tThis version: " << bsnes_core_version
<< std::endl
194 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
195 throw std::runtime_error(x
.str());
197 messages
<< "WARNING: Emulator core version mismatch!" << std::endl
198 << "\tThis version: " << bsnes_core_version
<< std::endl
199 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
201 warn_hash_mismatch(_movie
.rom_sha256
, our_rom
->rom
, "ROM #1");
202 warn_hash_mismatch(_movie
.romxml_sha256
, our_rom
->rom_xml
, "XML #1");
203 warn_hash_mismatch(_movie
.slota_sha256
, our_rom
->slota
, "ROM #2");
204 warn_hash_mismatch(_movie
.slotaxml_sha256
, our_rom
->slota_xml
, "XML #2");
205 warn_hash_mismatch(_movie
.slotb_sha256
, our_rom
->slotb
, "ROM #3");
206 warn_hash_mismatch(_movie
.slotbxml_sha256
, our_rom
->slotb_xml
, "XML #3");
208 SNES::config
.random
= false;
209 SNES::config
.expansion_port
= SNES::System::ExpansionPortDevice::None
;
212 if(lmode
== LOAD_STATE_PRESERVE
)
213 newmovie
= movb
.get_movie();
215 newmovie
.load(_movie
.rerecords
, _movie
.projectid
, _movie
.input
);
217 if(will_load_state
) {
218 std::vector
<unsigned char> tmp
;
219 tmp
.resize(_movie
.movie_state
.size());
220 memcpy(&tmp
[0], &_movie
.movie_state
[0], tmp
.size());
221 newmovie
.restore_state(tmp
, true);
225 rrdata::read_base(_movie
.projectid
);
226 rrdata::read(_movie
.c_rrdata
);
227 rrdata::add_internal();
229 our_rom
->region
= gtype::toromregion(_movie
.gametype
);
232 if(_movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
) {
233 //Load the savestate and movie state.
234 controller_set_port_type(0, _movie
.port1
);
235 controller_set_port_type(1, _movie
.port2
);
236 load_core_state(_movie
.savestate
);
237 framebuffer
.load(_movie
.screenshot
);
239 load_sram(_movie
.movie_sram
);
240 controller_set_port_type(0, _movie
.port1
);
241 controller_set_port_type(1, _movie
.port2
);
242 framebuffer
= screen_nosignal
;
244 } catch(std::bad_alloc
& e
) {
246 } catch(std::exception
& e
) {
247 system_corrupt
= true;
248 framebuffer
= screen_corrupt
;
252 //Okay, copy the movie data.
254 if(!our_movie
.is_savestate
|| lmode
== LOAD_STATE_MOVIE
) {
255 our_movie
.is_savestate
= false;
256 our_movie
.host_memory
.clear();
258 movb
.get_movie() = newmovie
;
259 //Activate RW mode if needed.
260 if(lmode
== LOAD_STATE_RW
)
261 movb
.get_movie().readonly_mode(false);
262 if(lmode
== LOAD_STATE_DEFAULT
&& movb
.get_movie().get_frame_count() <= movb
.get_movie().get_current_frame())
263 movb
.get_movie().readonly_mode(false);
264 if(lmode
== LOAD_STATE_CURRENT
&& !current_mode
)
265 movb
.get_movie().readonly_mode(false);
266 messages
<< "ROM Type ";
267 switch(our_rom
->rtype
) {
274 case ROMTYPE_BSXSLOTTED
:
275 messages
<< "BS-X slotted";
277 case ROMTYPE_SUFAMITURBO
:
278 messages
<< "Sufami Turbo";
281 messages
<< "Super Game Boy";
284 messages
<< "Unknown";
287 messages
<< " region ";
288 switch(our_rom
->region
) {
296 messages
<< "Unknown";
299 messages
<< std::endl
;
300 uint64_t mlength
= _movie
.get_movie_length();
303 std::ostringstream x
;
304 if(mlength
> 3600000000000) {
305 x
<< mlength
/ 3600000000000 << ":";
306 mlength
%= 3600000000000;
308 x
<< std::setfill('0') << std::setw(2) << mlength
/ 60000000000 << ":";
309 mlength
%= 60000000000;
310 x
<< std::setfill('0') << std::setw(2) << mlength
/ 1000000000 << ".";
311 mlength
%= 1000000000;
312 x
<< std::setfill('0') << std::setw(3) << mlength
/ 1000000;
313 messages
<< "Rerecords " << _movie
.rerecords
<< " length " << x
.str() << " ("
314 << _movie
.get_frame_count() << " frames)" << std::endl
;
316 if(_movie
.gamename
!= "")
317 messages
<< "Game Name: " << _movie
.gamename
<< std::endl
;
318 for(size_t i
= 0; i
< _movie
.authors
.size(); i
++)
319 messages
<< "Author: " << _movie
.authors
[i
].first
<< "(" << _movie
.authors
[i
].second
<< ")"
324 bool do_load_state(const std::string
& filename
, int lmode
)
326 uint64_t origtime
= get_utime();
327 lua_callback_pre_load(filename
);
328 struct moviefile mfile
;
330 mfile
= moviefile(filename
);
331 } catch(std::bad_alloc
& e
) {
333 } catch(std::exception
& e
) {
334 messages
<< "Can't read movie/savestate '" << filename
<< "': " << e
.what() << std::endl
;
335 lua_callback_err_load(filename
);
339 do_load_state(mfile
, lmode
);
340 uint64_t took
= get_utime() - origtime
;
341 messages
<< "Loaded '" << filename
<< "' in " << took
<< " microseconds." << std::endl
;
342 lua_callback_post_load(filename
, our_movie
.is_savestate
);
343 } catch(std::bad_alloc
& e
) {
345 } catch(std::exception
& e
) {
346 messages
<< "Can't load movie/savestate '" << filename
<< "': " << e
.what() << std::endl
;
347 lua_callback_err_load(filename
);