Evdev joystick plugin
[lsnes.git] / generic / moviedata.cpp
blobe46d84c9b758690a52592d9946a7838884ebf387
1 #include "moviedata.hpp"
2 #include "command.hpp"
3 #include "framerate.hpp"
4 #include "lua.hpp"
5 #include "misc.hpp"
6 #include "framebuffer.hpp"
7 #include <iomanip>
8 #include "lsnes.hpp"
9 #include "window.hpp"
10 #include <snes/snes.hpp>
11 #include <ui-libsnes/libsnes.hpp>
12 #include "rrdata.hpp"
13 #include "settings.hpp"
14 #include "controller.hpp"
16 struct moviefile our_movie;
17 struct loaded_rom* our_rom;
18 bool system_corrupt;
19 movie_logic movb;
21 extern "C"
23 time_t __wrap_time(time_t* t)
25 time_t v = static_cast<time_t>(our_movie.rtc_second);
26 if(t)
27 *t = v;
28 return v;
32 std::vector<char>& get_host_memory()
34 return our_movie.host_memory;
37 movie& get_movie()
39 return movb.get_movie();
42 namespace
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;
51 });
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;
58 });
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)
64 size_t idx = 0;
65 for(auto i : our_movie.authors) {
66 messages << (idx++) << ": " << i.first << "|" << i.second << std::endl;
68 messages << "End of authors list" << std::endl;
69 });
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;
78 });
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);
87 });
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;
98 });
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,
112 std::runtime_error)
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()) {
119 fullname = _author;
120 } else {
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);
130 //Save state.
131 void do_save_state(const std::string& filename) throw(std::bad_alloc,
132 std::runtime_error)
134 lua_callback_pre_save(filename, true);
135 try {
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) {
150 throw;
151 } catch(std::exception& e) {
152 messages << "Save failed: " << e.what() << std::endl;
153 lua_callback_err_save(filename);
157 //Save movie.
158 void do_save_movie(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
160 lua_callback_pre_save(filename, false);
161 try {
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) {
170 OOM_panic();
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());
196 } else
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;
211 movie newmovie;
212 if(lmode == LOAD_STATE_PRESERVE)
213 newmovie = movb.get_movie();
214 else
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);
224 //Negative return.
225 rrdata::read_base(_movie.projectid);
226 rrdata::read(_movie.c_rrdata);
227 rrdata::add_internal();
228 try {
229 our_rom->region = gtype::toromregion(_movie.gametype);
230 our_rom->load();
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);
238 } else {
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) {
245 OOM_panic();
246 } catch(std::exception& e) {
247 system_corrupt = true;
248 framebuffer = screen_corrupt;
249 throw;
252 //Okay, copy the movie data.
253 our_movie = _movie;
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) {
268 case ROMTYPE_SNES:
269 messages << "SNES";
270 break;
271 case ROMTYPE_BSX:
272 messages << "BS-X";
273 break;
274 case ROMTYPE_BSXSLOTTED:
275 messages << "BS-X slotted";
276 break;
277 case ROMTYPE_SUFAMITURBO:
278 messages << "Sufami Turbo";
279 break;
280 case ROMTYPE_SGB:
281 messages << "Super Game Boy";
282 break;
283 default:
284 messages << "Unknown";
285 break;
287 messages << " region ";
288 switch(our_rom->region) {
289 case REGION_PAL:
290 messages << "PAL";
291 break;
292 case REGION_NTSC:
293 messages << "NTSC";
294 break;
295 default:
296 messages << "Unknown";
297 break;
299 messages << std::endl;
300 uint64_t mlength = _movie.get_movie_length();
302 mlength += 999999;
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 << ")"
320 << std::endl;
323 //Load state
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;
329 try {
330 mfile = moviefile(filename);
331 } catch(std::bad_alloc& e) {
332 OOM_panic();
333 } catch(std::exception& e) {
334 messages << "Can't read movie/savestate '" << filename << "': " << e.what() << std::endl;
335 lua_callback_err_load(filename);
336 return false;
338 try {
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) {
344 OOM_panic();
345 } catch(std::exception& e) {
346 messages << "Can't load movie/savestate '" << filename << "': " << e.what() << std::endl;
347 lua_callback_err_load(filename);
348 return false;
350 return true;