1 #include "moviedata.hpp"
5 #include "framebuffer.hpp"
8 #include <snes/snes.hpp>
9 #include <ui-libsnes/libsnes.hpp>
11 #include "settings.hpp"
12 #include "controller.hpp"
14 struct moviefile our_movie
;
15 struct loaded_rom
* our_rom
;
19 std::vector
<char>& get_host_memory()
21 return our_movie
.host_memory
;
26 return movb
.get_movie();
31 numeric_setting
savecompression("savecompression", 0, 9, 7);
33 class get_gamename_cmd
: public command
36 get_gamename_cmd() throw(std::bad_alloc
) : command("get-gamename") {}
37 void invoke(const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
)
40 throw std::runtime_error("This command does not take parameters");
41 window::out() << "Game name is '" << our_movie
.gamename
<< "'" << std::endl
;
43 std::string
get_short_help() throw(std::bad_alloc
) { return "Get the game name"; }
44 std::string
get_long_help() throw(std::bad_alloc
)
46 return "Syntax: get-gamename\n"
47 "Prints the game name\n";
51 class print_authors_cmd
: public command
54 print_authors_cmd() throw(std::bad_alloc
) : command("show-authors") {}
55 void invoke(const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
)
58 throw std::runtime_error("This command does not take parameters");
60 for(auto i
= our_movie
.authors
.begin(); i
!= our_movie
.authors
.end(); i
++) {
61 window::out() << (idx
++) << ": " << i
->first
<< "|" << i
->second
<< std::endl
;
63 window::out() << "End of authors list" << std::endl
;
65 std::string
get_short_help() throw(std::bad_alloc
) { return "Show the run authors"; }
66 std::string
get_long_help() throw(std::bad_alloc
)
68 return "Syntax: show-authors\n"
69 "Shows the run authors\n";
73 class add_author_command
: public command
76 add_author_command() throw(std::bad_alloc
) : command("add-author") {}
77 void invoke(const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
)
79 tokensplitter
t(args
);
80 fieldsplitter
f(t
.tail());
83 if(full
== "" && nick
== "")
84 throw std::runtime_error("Bad author name");
85 our_movie
.authors
.push_back(std::make_pair(full
, nick
));
86 window::out() << (our_movie
.authors
.size() - 1) << ": " << full
<< "|" << nick
<< std::endl
;
88 std::string
get_short_help() throw(std::bad_alloc
) { return "Add an author"; }
89 std::string
get_long_help() throw(std::bad_alloc
)
91 return "Syntax: add-author <fullname>\n"
92 "Syntax: add-author |<nickname>\n"
93 "Syntax: add-author <fullname>|<nickname>\n"
94 "Adds a new author\n";
98 class remove_author_command
: public command
101 remove_author_command() throw(std::bad_alloc
) : command("remove-author") {}
102 void invoke(const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
)
104 tokensplitter
t(args
);
105 uint64_t index
= parse_value
<uint64_t>(t
.tail());
106 if(index
>= our_movie
.authors
.size())
107 throw std::runtime_error("No such author");
108 our_movie
.authors
.erase(our_movie
.authors
.begin() + index
);
110 std::string
get_short_help() throw(std::bad_alloc
) { return "Remove an author"; }
111 std::string
get_long_help() throw(std::bad_alloc
)
113 return "Syntax: remove-author <id>\n"
114 "Removes author with ID <id>\n";
118 class edit_author_command
: public command
121 edit_author_command() throw(std::bad_alloc
) : command("edit-author") {}
122 void invoke(const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
)
124 tokensplitter
t(args
);
125 uint64_t index
= parse_value
<uint64_t>(t
);
126 if(index
>= our_movie
.authors
.size())
127 throw std::runtime_error("No such author");
128 fieldsplitter
f(t
.tail());
129 std::string full
= f
;
130 std::string nick
= f
;
131 if(full
== "" && nick
== "") {
132 window::out() << "syntax: edit-author <authornum> <author>" << std::endl
;
135 our_movie
.authors
[index
] = std::make_pair(full
, nick
);
137 std::string
get_short_help() throw(std::bad_alloc
) { return "Edit an author"; }
138 std::string
get_long_help() throw(std::bad_alloc
)
140 return "Syntax: edit-author <authorid> <fullname>\n"
141 "Syntax: edit-author <authorid> |<nickname>\n"
142 "Syntax: edit-author <authorid> <fullname>|<nickname>\n"
143 "Edits author name\n";
147 void warn_hash_mismatch(const std::string
& mhash
, const loaded_slot
& slot
,
148 const std::string
& name
)
150 if(mhash
!= slot
.sha256
) {
151 window::out() << "WARNING: " << name
<< " hash mismatch!" << std::endl
152 << "\tMovie: " << mhash
<< std::endl
153 << "\tOur ROM: " << slot
.sha256
<< std::endl
;
159 void do_save_state(const std::string
& filename
) throw(std::bad_alloc
,
162 lua_callback_pre_save(filename
, true);
164 uint64_t origtime
= get_ticks_msec();
165 our_movie
.is_savestate
= true;
166 our_movie
.sram
= save_sram();
167 our_movie
.savestate
= save_core_state();
168 framebuffer
.save(our_movie
.screenshot
);
169 auto s
= movb
.get_movie().save_state();
170 our_movie
.movie_state
.resize(s
.size());
171 memcpy(&our_movie
.movie_state
[0], &s
[0], s
.size());
172 our_movie
.input
= movb
.get_movie().save();
173 our_movie
.save(filename
, savecompression
);
174 uint64_t took
= get_ticks_msec() - origtime
;
175 window::out() << "Saved state '" << filename
<< "' in " << took
<< "ms." << std::endl
;
176 lua_callback_post_save(filename
, true);
177 } catch(std::bad_alloc
& e
) {
179 } catch(std::exception
& e
) {
180 window::message(std::string("Save failed: ") + e
.what());
181 lua_callback_err_save(filename
);
186 void do_save_movie(const std::string
& filename
) throw(std::bad_alloc
, std::runtime_error
)
188 lua_callback_pre_save(filename
, false);
190 uint64_t origtime
= get_ticks_msec();
191 our_movie
.is_savestate
= false;
192 our_movie
.input
= movb
.get_movie().save();
193 our_movie
.save(filename
, savecompression
);
194 uint64_t took
= get_ticks_msec() - origtime
;
195 window::out() << "Saved movie '" << filename
<< "' in " << took
<< "ms." << std::endl
;
196 lua_callback_post_save(filename
, false);
197 } catch(std::bad_alloc
& e
) {
199 } catch(std::exception
& e
) {
200 window::message(std::string("Save failed: ") + e
.what());
201 lua_callback_err_save(filename
);
205 //Load state from loaded movie file (does not catch errors).
206 void do_load_state(struct moviefile
& _movie
, int lmode
)
208 bool will_load_state
= _movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
;
209 if(gtype::toromtype(_movie
.gametype
) != our_rom
->rtype
)
210 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
211 if(gtype::toromregion(_movie
.gametype
) != our_rom
->orig_region
&& our_rom
->orig_region
!= REGION_AUTO
)
212 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
214 if(_movie
.coreversion
!= bsnes_core_version
) {
215 if(will_load_state
) {
216 std::ostringstream x
;
217 x
<< "ERROR: Emulator core version mismatch!" << std::endl
218 << "\tThis version: " << bsnes_core_version
<< std::endl
219 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
220 throw std::runtime_error(x
.str());
222 window::out() << "WARNING: Emulator core version mismatch!" << std::endl
223 << "\tThis version: " << bsnes_core_version
<< std::endl
224 << "\tFile is from: " << _movie
.coreversion
<< std::endl
;
226 warn_hash_mismatch(_movie
.rom_sha256
, our_rom
->rom
, "ROM #1");
227 warn_hash_mismatch(_movie
.romxml_sha256
, our_rom
->rom_xml
, "XML #1");
228 warn_hash_mismatch(_movie
.slota_sha256
, our_rom
->slota
, "ROM #2");
229 warn_hash_mismatch(_movie
.slotaxml_sha256
, our_rom
->slota_xml
, "XML #2");
230 warn_hash_mismatch(_movie
.slotb_sha256
, our_rom
->slotb
, "ROM #3");
231 warn_hash_mismatch(_movie
.slotbxml_sha256
, our_rom
->slotb_xml
, "XML #3");
233 SNES::config
.random
= false;
234 SNES::config
.expansion_port
= SNES::System::ExpansionPortDevice::None
;
237 if(lmode
== LOAD_STATE_PRESERVE
)
238 newmovie
= movb
.get_movie();
240 newmovie
.load(_movie
.rerecords
, _movie
.projectid
, _movie
.input
);
242 if(will_load_state
) {
243 std::vector
<unsigned char> tmp
;
244 tmp
.resize(_movie
.movie_state
.size());
245 memcpy(&tmp
[0], &_movie
.movie_state
[0], tmp
.size());
246 newmovie
.restore_state(tmp
, true);
250 rrdata::read_base(_movie
.projectid
);
251 rrdata::add_internal();
253 our_rom
->region
= gtype::toromregion(_movie
.gametype
);
256 if(_movie
.is_savestate
&& lmode
!= LOAD_STATE_MOVIE
) {
257 //Load the savestate and movie state.
258 controller_set_port_type(0, _movie
.port1
);
259 controller_set_port_type(1, _movie
.port2
);
260 load_core_state(_movie
.savestate
);
261 framebuffer
.load(_movie
.screenshot
);
263 load_sram(_movie
.movie_sram
);
264 controller_set_port_type(0, _movie
.port1
);
265 controller_set_port_type(1, _movie
.port2
);
266 framebuffer
= screen_nosignal
;
268 } catch(std::bad_alloc
& e
) {
270 } catch(std::exception
& e
) {
271 system_corrupt
= true;
272 framebuffer
= screen_corrupt
;
276 //Okay, copy the movie data.
278 if(!our_movie
.is_savestate
|| lmode
== LOAD_STATE_MOVIE
) {
279 our_movie
.is_savestate
= false;
280 our_movie
.host_memory
.clear();
282 movb
.get_movie() = newmovie
;
283 //Activate RW mode if needed.
284 if(lmode
== LOAD_STATE_RW
)
285 movb
.get_movie().readonly_mode(false);
286 if(lmode
== LOAD_STATE_DEFAULT
&& !(movb
.get_movie().get_frame_count()))
287 movb
.get_movie().readonly_mode(false);
288 window::out() << "ROM Type ";
289 switch(our_rom
->rtype
) {
291 window::out() << "SNES";
294 window::out() << "BS-X";
296 case ROMTYPE_BSXSLOTTED
:
297 window::out() << "BS-X slotted";
299 case ROMTYPE_SUFAMITURBO
:
300 window::out() << "Sufami Turbo";
303 window::out() << "Super Game Boy";
306 window::out() << "Unknown";
309 window::out() << " region ";
310 switch(our_rom
->region
) {
312 window::out() << "PAL";
315 window::out() << "NTSC";
318 window::out() << "Unknown";
321 window::out() << std::endl
;
322 uint64_t mlength
= _movie
.get_movie_length();
325 std::ostringstream x
;
326 if(mlength
> 3600000000000) {
327 x
<< mlength
/ 3600000000000 << ":";
328 mlength
%= 3600000000000;
330 x
<< std::setfill('0') << std::setw(2) << mlength
/ 60000000000 << ":";
331 mlength
%= 60000000000;
332 x
<< std::setfill('0') << std::setw(2) << mlength
/ 1000000000 << ".";
333 mlength
%= 1000000000;
334 x
<< std::setfill('0') << std::setw(3) << mlength
/ 1000000;
335 window::out() << "Rerecords " << _movie
.rerecords
<< " length " << x
.str() << " ("
336 << _movie
.get_frame_count() << " frames)" << std::endl
;
338 if(_movie
.gamename
!= "")
339 window::out() << "Game Name: " << _movie
.gamename
<< std::endl
;
340 for(size_t i
= 0; i
< _movie
.authors
.size(); i
++)
341 window::out() << "Author: " << _movie
.authors
[i
].first
<< "(" << _movie
.authors
[i
].second
<< ")"
346 void do_load_state(const std::string
& filename
, int lmode
)
348 uint64_t origtime
= get_ticks_msec();
349 lua_callback_pre_load(filename
);
350 struct moviefile mfile
;
352 mfile
= moviefile(filename
);
353 } catch(std::bad_alloc
& e
) {
355 } catch(std::exception
& e
) {
356 window::message("Can't read movie/savestate '" + filename
+ "': " + e
.what());
357 lua_callback_err_load(filename
);
361 do_load_state(mfile
, lmode
);
362 uint64_t took
= get_ticks_msec() - origtime
;
363 window::out() << "Loaded '" << filename
<< "' in " << took
<< "ms." << std::endl
;
364 lua_callback_post_load(filename
, our_movie
.is_savestate
);
365 } catch(std::bad_alloc
& e
) {
367 } catch(std::exception
& e
) {
368 window::message("Can't load movie/savestate '" + filename
+ "': " + e
.what());
369 lua_callback_err_load(filename
);