Migrate the rest to function_ptr_command
[lsnes.git] / moviedata.cpp
blob6e847d185d371174685c21de32ca15795262cf7a
1 #include "moviedata.hpp"
2 #include "command.hpp"
3 #include "lua.hpp"
4 #include "misc.hpp"
5 #include "framebuffer.hpp"
6 #include <iomanip>
7 #include "lsnes.hpp"
8 #include "window.hpp"
9 #include <snes/snes.hpp>
10 #include <ui-libsnes/libsnes.hpp>
11 #include "rrdata.hpp"
12 #include "settings.hpp"
13 #include "controller.hpp"
15 struct moviefile our_movie;
16 struct loaded_rom* our_rom;
17 bool system_corrupt;
18 movie_logic movb;
20 std::vector<char>& get_host_memory()
22 return our_movie.host_memory;
25 movie& get_movie()
27 return movb.get_movie();
30 namespace
32 numeric_setting savecompression("savecompression", 0, 9, 7);
35 function_ptr_command get_gamename("get-gamename", "Get the game name",
36 "Syntax: get-gamename\nPrints the game name\n",
37 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
38 if(args != "")
39 throw std::runtime_error("This command does not take parameters");
40 messages << "Game name is '" << our_movie.gamename << "'" << std::endl;
41 });
43 function_ptr_command set_gamename("set-gamename", "Set the game name",
44 "Syntax: set-gamename <name>\nSets the game name to <name>\n",
45 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
46 our_movie.gamename = args;
47 messages << "Game name changed to '" << our_movie.gamename << "'" << std::endl;
48 });
50 function_ptr_command show_authors("show-authors", "Show the run authors",
51 "Syntax: show-authors\nShows the run authors\n",
52 [](const std::string& args) throw(std::bad_alloc, std::runtime_error)
54 if(args != "")
55 throw std::runtime_error("This command does not take parameters");
56 size_t idx = 0;
57 for(auto i = our_movie.authors.begin(); i != our_movie.authors.end(); i++) {
58 messages << (idx++) << ": " << i->first << "|" << i->second << std::endl;
60 messages << "End of authors list" << std::endl;
61 });
63 function_ptr_command add_author("add-author", "Add an author",
64 "Syntax: add-author <fullname>\nSyntax: add-author |<nickname>\n"
65 "Syntax: add-author <fullname>|<nickname>\nAdds a new author\n",
66 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
67 tokensplitter t(args);
68 auto g = split_author(t.tail());
69 our_movie.authors.push_back(g);
70 messages << (our_movie.authors.size() - 1) << ": " << g.first << "|" << g.second << std::endl;
71 });
73 function_ptr_command remove_author("remove-author", "Remove an author",
74 "Syntax: remove-author <id>\nRemoves author with ID <id>\n",
75 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
76 tokensplitter t(args);
77 uint64_t index = parse_value<uint64_t>(t.tail());
78 if(index >= our_movie.authors.size())
79 throw std::runtime_error("No such author");
80 our_movie.authors.erase(our_movie.authors.begin() + index);
81 });
83 function_ptr_command edit_author("edit-author", "Edit an author",
84 "Syntax: edit-author <authorid> <fullname>\nSyntax: edit-author <authorid> |<nickname>\n"
85 "Syntax: edit-author <authorid> <fullname>|<nickname>\nEdits author name\n",
86 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
87 tokensplitter t(args);
88 uint64_t index = parse_value<uint64_t>(t);
89 if(index >= our_movie.authors.size())
90 throw std::runtime_error("No such author");
91 auto g = split_author(t.tail());
92 our_movie.authors[index] = g;
93 });
95 void warn_hash_mismatch(const std::string& mhash, const loaded_slot& slot,
96 const std::string& name)
98 if(mhash != slot.sha256) {
99 messages << "WARNING: " << name << " hash mismatch!" << std::endl
100 << "\tMovie: " << mhash << std::endl
101 << "\tOur ROM: " << slot.sha256 << std::endl;
106 std::pair<std::string, std::string> split_author(const std::string& author) throw(std::bad_alloc,
107 std::runtime_error)
109 std::string _author = author;
110 std::string fullname;
111 std::string nickname;
112 size_t split = _author.find_first_of("|");
113 if(!split) {
114 fullname = _author;
115 } else {
116 fullname = _author.substr(0, split);
117 nickname = _author.substr(split + 1);
119 if(fullname == "" && nickname == "")
120 throw std::runtime_error("Bad author name");
121 return std::make_pair(fullname, nickname);
125 //Save state.
126 void do_save_state(const std::string& filename) throw(std::bad_alloc,
127 std::runtime_error)
129 lua_callback_pre_save(filename, true);
130 try {
131 uint64_t origtime = get_ticks_msec();
132 our_movie.is_savestate = true;
133 our_movie.sram = save_sram();
134 our_movie.savestate = save_core_state();
135 framebuffer.save(our_movie.screenshot);
136 auto s = movb.get_movie().save_state();
137 our_movie.movie_state.resize(s.size());
138 memcpy(&our_movie.movie_state[0], &s[0], s.size());
139 our_movie.input = movb.get_movie().save();
140 our_movie.save(filename, savecompression);
141 uint64_t took = get_ticks_msec() - origtime;
142 messages << "Saved state '" << filename << "' in " << took << "ms." << std::endl;
143 lua_callback_post_save(filename, true);
144 } catch(std::bad_alloc& e) {
145 throw;
146 } catch(std::exception& e) {
147 messages << "Save failed: " << e.what() << std::endl;
148 lua_callback_err_save(filename);
152 //Save movie.
153 void do_save_movie(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
155 lua_callback_pre_save(filename, false);
156 try {
157 uint64_t origtime = get_ticks_msec();
158 our_movie.is_savestate = false;
159 our_movie.input = movb.get_movie().save();
160 our_movie.save(filename, savecompression);
161 uint64_t took = get_ticks_msec() - origtime;
162 messages << "Saved movie '" << filename << "' in " << took << "ms." << std::endl;
163 lua_callback_post_save(filename, false);
164 } catch(std::bad_alloc& e) {
165 OOM_panic();
166 } catch(std::exception& e) {
167 messages << "Save failed: " << e.what() << std::endl;
168 lua_callback_err_save(filename);
172 //Load state from loaded movie file (does not catch errors).
173 void do_load_state(struct moviefile& _movie, int lmode)
175 bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE;
176 if(gtype::toromtype(_movie.gametype) != our_rom->rtype)
177 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
178 if(gtype::toromregion(_movie.gametype) != our_rom->orig_region && our_rom->orig_region != REGION_AUTO)
179 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
181 if(_movie.coreversion != bsnes_core_version) {
182 if(will_load_state) {
183 std::ostringstream x;
184 x << "ERROR: Emulator core version mismatch!" << std::endl
185 << "\tThis version: " << bsnes_core_version << std::endl
186 << "\tFile is from: " << _movie.coreversion << std::endl;
187 throw std::runtime_error(x.str());
188 } else
189 messages << "WARNING: Emulator core version mismatch!" << std::endl
190 << "\tThis version: " << bsnes_core_version << std::endl
191 << "\tFile is from: " << _movie.coreversion << std::endl;
193 warn_hash_mismatch(_movie.rom_sha256, our_rom->rom, "ROM #1");
194 warn_hash_mismatch(_movie.romxml_sha256, our_rom->rom_xml, "XML #1");
195 warn_hash_mismatch(_movie.slota_sha256, our_rom->slota, "ROM #2");
196 warn_hash_mismatch(_movie.slotaxml_sha256, our_rom->slota_xml, "XML #2");
197 warn_hash_mismatch(_movie.slotb_sha256, our_rom->slotb, "ROM #3");
198 warn_hash_mismatch(_movie.slotbxml_sha256, our_rom->slotb_xml, "XML #3");
200 SNES::config.random = false;
201 SNES::config.expansion_port = SNES::System::ExpansionPortDevice::None;
203 movie newmovie;
204 if(lmode == LOAD_STATE_PRESERVE)
205 newmovie = movb.get_movie();
206 else
207 newmovie.load(_movie.rerecords, _movie.projectid, _movie.input);
209 if(will_load_state) {
210 std::vector<unsigned char> tmp;
211 tmp.resize(_movie.movie_state.size());
212 memcpy(&tmp[0], &_movie.movie_state[0], tmp.size());
213 newmovie.restore_state(tmp, true);
216 //Negative return.
217 rrdata::read_base(_movie.projectid);
218 rrdata::add_internal();
219 try {
220 our_rom->region = gtype::toromregion(_movie.gametype);
221 our_rom->load();
223 if(_movie.is_savestate && lmode != LOAD_STATE_MOVIE) {
224 //Load the savestate and movie state.
225 controller_set_port_type(0, _movie.port1);
226 controller_set_port_type(1, _movie.port2);
227 load_core_state(_movie.savestate);
228 framebuffer.load(_movie.screenshot);
229 } else {
230 load_sram(_movie.movie_sram);
231 controller_set_port_type(0, _movie.port1);
232 controller_set_port_type(1, _movie.port2);
233 framebuffer = screen_nosignal;
235 } catch(std::bad_alloc& e) {
236 OOM_panic();
237 } catch(std::exception& e) {
238 system_corrupt = true;
239 framebuffer = screen_corrupt;
240 throw;
243 //Okay, copy the movie data.
244 our_movie = _movie;
245 if(!our_movie.is_savestate || lmode == LOAD_STATE_MOVIE) {
246 our_movie.is_savestate = false;
247 our_movie.host_memory.clear();
249 movb.get_movie() = newmovie;
250 //Activate RW mode if needed.
251 if(lmode == LOAD_STATE_RW)
252 movb.get_movie().readonly_mode(false);
253 if(lmode == LOAD_STATE_DEFAULT && !(movb.get_movie().get_frame_count()))
254 movb.get_movie().readonly_mode(false);
255 messages << "ROM Type ";
256 switch(our_rom->rtype) {
257 case ROMTYPE_SNES:
258 messages << "SNES";
259 break;
260 case ROMTYPE_BSX:
261 messages << "BS-X";
262 break;
263 case ROMTYPE_BSXSLOTTED:
264 messages << "BS-X slotted";
265 break;
266 case ROMTYPE_SUFAMITURBO:
267 messages << "Sufami Turbo";
268 break;
269 case ROMTYPE_SGB:
270 messages << "Super Game Boy";
271 break;
272 default:
273 messages << "Unknown";
274 break;
276 messages << " region ";
277 switch(our_rom->region) {
278 case REGION_PAL:
279 messages << "PAL";
280 break;
281 case REGION_NTSC:
282 messages << "NTSC";
283 break;
284 default:
285 messages << "Unknown";
286 break;
288 messages << std::endl;
289 uint64_t mlength = _movie.get_movie_length();
291 mlength += 999999;
292 std::ostringstream x;
293 if(mlength > 3600000000000) {
294 x << mlength / 3600000000000 << ":";
295 mlength %= 3600000000000;
297 x << std::setfill('0') << std::setw(2) << mlength / 60000000000 << ":";
298 mlength %= 60000000000;
299 x << std::setfill('0') << std::setw(2) << mlength / 1000000000 << ".";
300 mlength %= 1000000000;
301 x << std::setfill('0') << std::setw(3) << mlength / 1000000;
302 messages << "Rerecords " << _movie.rerecords << " length " << x.str() << " ("
303 << _movie.get_frame_count() << " frames)" << std::endl;
305 if(_movie.gamename != "")
306 messages << "Game Name: " << _movie.gamename << std::endl;
307 for(size_t i = 0; i < _movie.authors.size(); i++)
308 messages << "Author: " << _movie.authors[i].first << "(" << _movie.authors[i].second << ")"
309 << std::endl;
312 //Load state
313 void do_load_state(const std::string& filename, int lmode)
315 uint64_t origtime = get_ticks_msec();
316 lua_callback_pre_load(filename);
317 struct moviefile mfile;
318 try {
319 mfile = moviefile(filename);
320 } catch(std::bad_alloc& e) {
321 OOM_panic();
322 } catch(std::exception& e) {
323 messages << "Can't read movie/savestate '" << filename << "': " << e.what() << std::endl;
324 lua_callback_err_load(filename);
325 return;
327 try {
328 do_load_state(mfile, lmode);
329 uint64_t took = get_ticks_msec() - origtime;
330 messages << "Loaded '" << filename << "' in " << took << "ms." << std::endl;
331 lua_callback_post_load(filename, our_movie.is_savestate);
332 } catch(std::bad_alloc& e) {
333 OOM_panic();
334 } catch(std::exception& e) {
335 messages << "Can't load movie/savestate '" << filename << "': " << e.what() << std::endl;
336 lua_callback_err_load(filename);
337 return;