lsnes rr0-β2
[lsnes.git] / moviedata.cpp
blob12c8d13e048902682e8c4d516f482c0558a63446
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 []() throw(std::bad_alloc, std::runtime_error) {
38 messages << "Game name is '" << our_movie.gamename << "'" << std::endl;
39 });
41 function_ptr_command<const std::string&> set_gamename("set-gamename", "Set the game name",
42 "Syntax: set-gamename <name>\nSets the game name to <name>\n",
43 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
44 our_movie.gamename = args;
45 messages << "Game name changed to '" << our_movie.gamename << "'" << std::endl;
46 });
48 function_ptr_command<> show_authors("show-authors", "Show the run authors",
49 "Syntax: show-authors\nShows the run authors\n",
50 []() throw(std::bad_alloc, std::runtime_error)
52 size_t idx = 0;
53 for(auto i = our_movie.authors.begin(); i != our_movie.authors.end(); i++) {
54 messages << (idx++) << ": " << i->first << "|" << i->second << std::endl;
56 messages << "End of authors list" << std::endl;
57 });
59 function_ptr_command<tokensplitter&> add_author("add-author", "Add an author",
60 "Syntax: add-author <fullname>\nSyntax: add-author |<nickname>\n"
61 "Syntax: add-author <fullname>|<nickname>\nAdds a new author\n",
62 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
63 auto g = split_author(t.tail());
64 our_movie.authors.push_back(g);
65 messages << (our_movie.authors.size() - 1) << ": " << g.first << "|" << g.second << std::endl;
66 });
68 function_ptr_command<tokensplitter&> remove_author("remove-author", "Remove an author",
69 "Syntax: remove-author <id>\nRemoves author with ID <id>\n",
70 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
71 uint64_t index = parse_value<uint64_t>(t.tail());
72 if(index >= our_movie.authors.size())
73 throw std::runtime_error("No such author");
74 our_movie.authors.erase(our_movie.authors.begin() + index);
75 });
77 function_ptr_command<tokensplitter&> edit_author("edit-author", "Edit an author",
78 "Syntax: edit-author <authorid> <fullname>\nSyntax: edit-author <authorid> |<nickname>\n"
79 "Syntax: edit-author <authorid> <fullname>|<nickname>\nEdits author name\n",
80 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
81 uint64_t index = parse_value<uint64_t>(t);
82 if(index >= our_movie.authors.size())
83 throw std::runtime_error("No such author");
84 auto g = split_author(t.tail());
85 our_movie.authors[index] = g;
86 });
88 void warn_hash_mismatch(const std::string& mhash, const loaded_slot& slot,
89 const std::string& name)
91 if(mhash != slot.sha256) {
92 messages << "WARNING: " << name << " hash mismatch!" << std::endl
93 << "\tMovie: " << mhash << std::endl
94 << "\tOur ROM: " << slot.sha256 << std::endl;
99 std::pair<std::string, std::string> split_author(const std::string& author) throw(std::bad_alloc,
100 std::runtime_error)
102 std::string _author = author;
103 std::string fullname;
104 std::string nickname;
105 size_t split = _author.find_first_of("|");
106 if(!split) {
107 fullname = _author;
108 } else {
109 fullname = _author.substr(0, split);
110 nickname = _author.substr(split + 1);
112 if(fullname == "" && nickname == "")
113 throw std::runtime_error("Bad author name");
114 return std::make_pair(fullname, nickname);
118 //Save state.
119 void do_save_state(const std::string& filename) throw(std::bad_alloc,
120 std::runtime_error)
122 lua_callback_pre_save(filename, true);
123 try {
124 uint64_t origtime = get_ticks_msec();
125 our_movie.is_savestate = true;
126 our_movie.sram = save_sram();
127 our_movie.savestate = save_core_state();
128 framebuffer.save(our_movie.screenshot);
129 auto s = movb.get_movie().save_state();
130 our_movie.movie_state.resize(s.size());
131 memcpy(&our_movie.movie_state[0], &s[0], s.size());
132 our_movie.input = movb.get_movie().save();
133 our_movie.save(filename, savecompression);
134 uint64_t took = get_ticks_msec() - origtime;
135 messages << "Saved state '" << filename << "' in " << took << "ms." << std::endl;
136 lua_callback_post_save(filename, true);
137 } catch(std::bad_alloc& e) {
138 throw;
139 } catch(std::exception& e) {
140 messages << "Save failed: " << e.what() << std::endl;
141 lua_callback_err_save(filename);
145 //Save movie.
146 void do_save_movie(const std::string& filename) throw(std::bad_alloc, std::runtime_error)
148 lua_callback_pre_save(filename, false);
149 try {
150 uint64_t origtime = get_ticks_msec();
151 our_movie.is_savestate = false;
152 our_movie.input = movb.get_movie().save();
153 our_movie.save(filename, savecompression);
154 uint64_t took = get_ticks_msec() - origtime;
155 messages << "Saved movie '" << filename << "' in " << took << "ms." << std::endl;
156 lua_callback_post_save(filename, false);
157 } catch(std::bad_alloc& e) {
158 OOM_panic();
159 } catch(std::exception& e) {
160 messages << "Save failed: " << e.what() << std::endl;
161 lua_callback_err_save(filename);
165 //Load state from loaded movie file (does not catch errors).
166 void do_load_state(struct moviefile& _movie, int lmode)
168 bool will_load_state = _movie.is_savestate && lmode != LOAD_STATE_MOVIE;
169 if(gtype::toromtype(_movie.gametype) != our_rom->rtype)
170 throw std::runtime_error("ROM types of movie and loaded ROM don't match");
171 if(gtype::toromregion(_movie.gametype) != our_rom->orig_region && our_rom->orig_region != REGION_AUTO)
172 throw std::runtime_error("NTSC/PAL select of movie and loaded ROM don't match");
174 if(_movie.coreversion != bsnes_core_version) {
175 if(will_load_state) {
176 std::ostringstream x;
177 x << "ERROR: Emulator core version mismatch!" << std::endl
178 << "\tThis version: " << bsnes_core_version << std::endl
179 << "\tFile is from: " << _movie.coreversion << std::endl;
180 throw std::runtime_error(x.str());
181 } else
182 messages << "WARNING: Emulator core version mismatch!" << std::endl
183 << "\tThis version: " << bsnes_core_version << std::endl
184 << "\tFile is from: " << _movie.coreversion << std::endl;
186 warn_hash_mismatch(_movie.rom_sha256, our_rom->rom, "ROM #1");
187 warn_hash_mismatch(_movie.romxml_sha256, our_rom->rom_xml, "XML #1");
188 warn_hash_mismatch(_movie.slota_sha256, our_rom->slota, "ROM #2");
189 warn_hash_mismatch(_movie.slotaxml_sha256, our_rom->slota_xml, "XML #2");
190 warn_hash_mismatch(_movie.slotb_sha256, our_rom->slotb, "ROM #3");
191 warn_hash_mismatch(_movie.slotbxml_sha256, our_rom->slotb_xml, "XML #3");
193 SNES::config.random = false;
194 SNES::config.expansion_port = SNES::System::ExpansionPortDevice::None;
196 movie newmovie;
197 if(lmode == LOAD_STATE_PRESERVE)
198 newmovie = movb.get_movie();
199 else
200 newmovie.load(_movie.rerecords, _movie.projectid, _movie.input);
202 if(will_load_state) {
203 std::vector<unsigned char> tmp;
204 tmp.resize(_movie.movie_state.size());
205 memcpy(&tmp[0], &_movie.movie_state[0], tmp.size());
206 newmovie.restore_state(tmp, true);
209 //Negative return.
210 rrdata::read_base(_movie.projectid);
211 rrdata::add_internal();
212 try {
213 our_rom->region = gtype::toromregion(_movie.gametype);
214 our_rom->load();
216 if(_movie.is_savestate && lmode != LOAD_STATE_MOVIE) {
217 //Load the savestate and movie state.
218 controller_set_port_type(0, _movie.port1);
219 controller_set_port_type(1, _movie.port2);
220 load_core_state(_movie.savestate);
221 framebuffer.load(_movie.screenshot);
222 } else {
223 load_sram(_movie.movie_sram);
224 controller_set_port_type(0, _movie.port1);
225 controller_set_port_type(1, _movie.port2);
226 framebuffer = screen_nosignal;
228 } catch(std::bad_alloc& e) {
229 OOM_panic();
230 } catch(std::exception& e) {
231 system_corrupt = true;
232 framebuffer = screen_corrupt;
233 throw;
236 //Okay, copy the movie data.
237 our_movie = _movie;
238 if(!our_movie.is_savestate || lmode == LOAD_STATE_MOVIE) {
239 our_movie.is_savestate = false;
240 our_movie.host_memory.clear();
242 movb.get_movie() = newmovie;
243 //Activate RW mode if needed.
244 if(lmode == LOAD_STATE_RW)
245 movb.get_movie().readonly_mode(false);
246 if(lmode == LOAD_STATE_DEFAULT && !(movb.get_movie().get_frame_count()))
247 movb.get_movie().readonly_mode(false);
248 messages << "ROM Type ";
249 switch(our_rom->rtype) {
250 case ROMTYPE_SNES:
251 messages << "SNES";
252 break;
253 case ROMTYPE_BSX:
254 messages << "BS-X";
255 break;
256 case ROMTYPE_BSXSLOTTED:
257 messages << "BS-X slotted";
258 break;
259 case ROMTYPE_SUFAMITURBO:
260 messages << "Sufami Turbo";
261 break;
262 case ROMTYPE_SGB:
263 messages << "Super Game Boy";
264 break;
265 default:
266 messages << "Unknown";
267 break;
269 messages << " region ";
270 switch(our_rom->region) {
271 case REGION_PAL:
272 messages << "PAL";
273 break;
274 case REGION_NTSC:
275 messages << "NTSC";
276 break;
277 default:
278 messages << "Unknown";
279 break;
281 messages << std::endl;
282 uint64_t mlength = _movie.get_movie_length();
284 mlength += 999999;
285 std::ostringstream x;
286 if(mlength > 3600000000000) {
287 x << mlength / 3600000000000 << ":";
288 mlength %= 3600000000000;
290 x << std::setfill('0') << std::setw(2) << mlength / 60000000000 << ":";
291 mlength %= 60000000000;
292 x << std::setfill('0') << std::setw(2) << mlength / 1000000000 << ".";
293 mlength %= 1000000000;
294 x << std::setfill('0') << std::setw(3) << mlength / 1000000;
295 messages << "Rerecords " << _movie.rerecords << " length " << x.str() << " ("
296 << _movie.get_frame_count() << " frames)" << std::endl;
298 if(_movie.gamename != "")
299 messages << "Game Name: " << _movie.gamename << std::endl;
300 for(size_t i = 0; i < _movie.authors.size(); i++)
301 messages << "Author: " << _movie.authors[i].first << "(" << _movie.authors[i].second << ")"
302 << std::endl;
305 //Load state
306 void do_load_state(const std::string& filename, int lmode)
308 uint64_t origtime = get_ticks_msec();
309 lua_callback_pre_load(filename);
310 struct moviefile mfile;
311 try {
312 mfile = moviefile(filename);
313 } catch(std::bad_alloc& e) {
314 OOM_panic();
315 } catch(std::exception& e) {
316 messages << "Can't read movie/savestate '" << filename << "': " << e.what() << std::endl;
317 lua_callback_err_load(filename);
318 return;
320 try {
321 do_load_state(mfile, lmode);
322 uint64_t took = get_ticks_msec() - origtime;
323 messages << "Loaded '" << filename << "' in " << took << "ms." << std::endl;
324 lua_callback_post_load(filename, our_movie.is_savestate);
325 } catch(std::bad_alloc& e) {
326 OOM_panic();
327 } catch(std::exception& e) {
328 messages << "Can't load movie/savestate '" << filename << "': " << e.what() << std::endl;
329 lua_callback_err_load(filename);
330 return;