Since window is singleton anyway, get rid of window* parameters
[lsnes.git] / moviedata.cpp
blobe4ddcc4e8988f14cf71601c71cd7d9a30deb8cc1
1 #include "moviedata.hpp"
2 #include "window.hpp"
3 #include "command.hpp"
4 #include "lua.hpp"
5 #include "framebuffer.hpp"
6 #include <iomanip>
7 #include "lsnes.hpp"
8 #include <snes/snes.hpp>
9 #include <ui-libsnes/libsnes.hpp>
10 #include "rrdata.hpp"
11 #include "settings.hpp"
12 #include "controller.hpp"
14 struct moviefile our_movie;
15 struct loaded_rom* our_rom;
16 bool system_corrupt;
17 movie_logic movb;
19 std::vector<char>& get_host_memory()
21 return our_movie.host_memory;
24 movie& get_movie()
26 return movb.get_movie();
29 namespace
31 numeric_setting savecompression("savecompression", 0, 9, 7);
33 class get_gamename_cmd : public command
35 public:
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)
39 if(args != "")
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";
49 } getnamec;
51 class print_authors_cmd : public command
53 public:
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)
57 if(args != "")
58 throw std::runtime_error("This command does not take parameters");
59 size_t idx = 0;
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";
71 } getauthorc;
73 class add_author_command : public command
75 public:
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());
81 std::string full = f;
82 std::string nick = f;
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";
96 } addauthorc;
98 class remove_author_command : public command
100 public:
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";
116 } removeauthorc;
118 class edit_author_command : public command
120 public:
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;
133 return;
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";
145 } editauthorc;
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;
158 //Save state.
159 void do_save_state(const std::string& filename) throw(std::bad_alloc,
160 std::runtime_error)
162 lua_callback_pre_save(filename, true);
163 try {
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) {
178 throw;
179 } catch(std::exception& e) {
180 window::message(std::string("Save failed: ") + e.what());
181 lua_callback_err_save(filename);
185 //Save movie.
186 void do_save_movie(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
188 lua_callback_pre_save(filename, false);
189 try {
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) {
198 OOM_panic();
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());
221 } else
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;
236 movie newmovie;
237 if(lmode == LOAD_STATE_PRESERVE)
238 newmovie = movb.get_movie();
239 else
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);
249 //Negative return.
250 rrdata::read_base(_movie.projectid);
251 rrdata::add_internal();
252 try {
253 our_rom->region = gtype::toromregion(_movie.gametype);
254 our_rom->load();
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);
262 } else {
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) {
269 OOM_panic();
270 } catch(std::exception& e) {
271 system_corrupt = true;
272 framebuffer = screen_corrupt;
273 throw;
276 //Okay, copy the movie data.
277 our_movie = _movie;
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) {
290 case ROMTYPE_SNES:
291 window::out() << "SNES";
292 break;
293 case ROMTYPE_BSX:
294 window::out() << "BS-X";
295 break;
296 case ROMTYPE_BSXSLOTTED:
297 window::out() << "BS-X slotted";
298 break;
299 case ROMTYPE_SUFAMITURBO:
300 window::out() << "Sufami Turbo";
301 break;
302 case ROMTYPE_SGB:
303 window::out() << "Super Game Boy";
304 break;
305 default:
306 window::out() << "Unknown";
307 break;
309 window::out() << " region ";
310 switch(our_rom->region) {
311 case REGION_PAL:
312 window::out() << "PAL";
313 break;
314 case REGION_NTSC:
315 window::out() << "NTSC";
316 break;
317 default:
318 window::out() << "Unknown";
319 break;
321 window::out() << std::endl;
322 uint64_t mlength = _movie.get_movie_length();
324 mlength += 999999;
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 << ")"
342 << std::endl;
345 //Load state
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;
351 try {
352 mfile = moviefile(filename);
353 } catch(std::bad_alloc& e) {
354 OOM_panic();
355 } catch(std::exception& e) {
356 window::message("Can't read movie/savestate '" + filename + "': " + e.what());
357 lua_callback_err_load(filename);
358 return;
360 try {
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) {
366 OOM_panic();
367 } catch(std::exception& e) {
368 window::message("Can't load movie/savestate '" + filename + "': " + e.what());
369 lua_callback_err_load(filename);
370 return;