Allow specifying ROM type in file load dialog
[lsnes.git] / src / library / movie.cpp
blob6953277a847cb73a8f981887804cfdd586700970
1 #include "movie.hpp"
2 #include "minmax.hpp"
4 #include <stdexcept>
5 #include <cassert>
6 #include <cstring>
7 #include <fstream>
10 //std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
12 namespace
14 uint64_t find_next_sync(controller_frame_vector& movie, uint64_t after)
16 if(after >= movie.size())
17 return after;
18 do {
19 after++;
20 } while(after < movie.size() && !movie[after].sync());
21 return after;
24 bool movies_compatible(controller_frame_vector& old_movie, controller_frame_vector& new_movie,
25 uint64_t frame, const uint32_t* polls, const std::string& old_projectid,
26 const std::string& new_projectid)
28 //Project IDs have to match.
29 if(old_projectid != new_projectid)
30 return false;
31 //Types have to match.
32 if(old_movie.get_types() != new_movie.get_types())
33 return false;
34 const port_type_set& pset = new_movie.get_types();
35 //If new movie is before first frame, anything with same project_id is compatible.
36 if(frame == 0)
37 return true;
38 //Scan both movies until frame syncs are seen. Out of bounds reads behave as all neutral but frame
39 //sync done.
40 uint64_t syncs_seen = 0;
41 uint64_t frames_read = 0;
42 while(syncs_seen < frame - 1) {
43 controller_frame oldc = old_movie.blank_frame(true), newc = new_movie.blank_frame(true);
44 if(frames_read < old_movie.size())
45 oldc = old_movie[frames_read];
46 if(frames_read < new_movie.size())
47 newc = new_movie[frames_read];
48 if(oldc != newc)
49 return false; //Mismatch.
50 frames_read++;
51 if(newc.sync())
52 syncs_seen++;
54 //We increment the counter one time too many.
55 frames_read--;
56 //Current frame. We need to compare each control up to poll counter.
57 uint64_t readable_old_subframes = 0, readable_new_subframes = 0;
58 uint64_t oldlen = find_next_sync(old_movie, frames_read);
59 uint64_t newlen = find_next_sync(new_movie, frames_read);
60 if(frames_read < oldlen)
61 readable_old_subframes = oldlen - frames_read;
62 if(frames_read < newlen)
63 readable_new_subframes = newlen - frames_read;
64 //Then rest of the stuff.
65 for(unsigned i = 0; i < pset.indices(); i++) {
66 uint32_t p = polls[i] & 0x7FFFFFFFUL;
67 short ov = 0, nv = 0;
68 for(uint32_t j = 0; j < p; j++) {
69 if(j < readable_old_subframes)
70 ov = old_movie[j + frames_read].axis2(i);
71 if(j < readable_new_subframes)
72 nv = new_movie[j + frames_read].axis2(i);
73 if(ov != nv)
74 return false;
77 return true;
81 void movie::set_all_DRDY() throw()
83 pollcounters.set_all_DRDY();
86 std::string movie::rerecord_count() throw(std::bad_alloc)
88 return rerecords;
91 void movie::rerecord_count(const std::string& count) throw(std::bad_alloc)
93 rerecords = count;
96 std::string movie::project_id() throw(std::bad_alloc)
98 return _project_id;
101 void movie::project_id(const std::string& id) throw(std::bad_alloc)
103 _project_id = id;
106 bool movie::readonly_mode() throw()
108 return readonly;
111 void movie::set_controls(controller_frame controls) throw()
113 current_controls = controls;
116 uint32_t movie::count_changes(uint64_t first_subframe) throw()
118 return movie_data.subframe_count(first_subframe);
121 controller_frame movie::get_controls() throw()
123 if(!readonly)
124 return current_controls;
125 controller_frame c = movie_data.blank_frame(false);
126 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
127 if(current_frame == 0)
128 return c;
129 //Otherwise find the last valid frame of input.
130 uint32_t changes = count_changes(current_frame_first_subframe);
131 if(!changes)
132 return c; //End of movie.
133 for(size_t i = 0; i < movie_data.get_types().indices(); i++) {
134 uint32_t polls = pollcounters.get_polls(i);
135 uint32_t index = (changes > polls) ? polls : changes - 1;
136 c.axis2(i, movie_data[current_frame_first_subframe + index].axis2(i));
138 return c;
141 uint64_t movie::get_current_frame() throw()
143 return current_frame;
146 uint64_t movie::get_lag_frames() throw()
148 return lag_frames;
151 uint64_t movie::get_frame_count() throw()
153 return frames_in_movie;
156 void movie::next_frame() throw(std::bad_alloc)
158 //Adjust lag count. Frame 0 MUST NOT be considered lag.
159 bool pflag = pflag_handler ? pflag_handler->get_pflag() : false;
160 if(current_frame && !pflag)
161 lag_frames++;
162 else if(pflag_handler)
163 pflag_handler->set_pflag(false);
165 //If all poll counters are zero for all real controls, this frame is lag.
166 bool this_frame_lag = !pollcounters.has_polled();
167 //Oh, frame 0 must not be considered lag.
168 if(current_frame && this_frame_lag) {
169 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
170 if(!readonly) {
171 //If in read-write mode, write a dummy record for the frame. Force sync flag.
172 //As index should be movie_data.size(), it is correct afterwards.
173 movie_data.append(current_controls.copy(true));
174 frames_in_movie++;
178 //Reset the poll counters and DRDY flags.
179 pollcounters.clear();
181 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
182 //frame 0 and 0 for frame 1.
183 if(current_frame)
184 current_frame_first_subframe = current_frame_first_subframe +
185 count_changes(current_frame_first_subframe);
186 else
187 current_frame_first_subframe = 0;
188 current_frame++;
191 bool movie::get_DRDY(unsigned port, unsigned controller, unsigned ctrl) throw(std::logic_error)
193 return pollcounters.get_DRDY(port, controller, ctrl);
196 short movie::next_input(unsigned port, unsigned controller, unsigned ctrl) throw(std::bad_alloc, std::logic_error)
198 pollcounters.clear_DRDY(port, controller, ctrl);
200 if(readonly) {
201 //In readonly mode...
202 //If at the end of the movie, return released / neutral (but also record the poll)...
203 if(current_frame_first_subframe >= movie_data.size()) {
204 pollcounters.increment_polls(port, controller, ctrl);
205 return 0;
207 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
208 if(current_frame == 0)
209 return 0;
210 //Otherwise find the last valid frame of input.
211 uint32_t changes = count_changes(current_frame_first_subframe);
212 uint32_t polls = pollcounters.get_polls(port, controller, ctrl);
213 uint32_t index = (changes > polls) ? polls : changes - 1;
214 int16_t data = movie_data[current_frame_first_subframe + index].axis3(port, controller, ctrl);
215 pollcounters.increment_polls(port, controller, ctrl);
216 return data;
217 } else {
218 //Readwrite mode.
219 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
220 //Also, frame 0 must not be added to movie file.
221 if(current_frame == 0)
222 return 0;
223 //If at movie end, insert complete input with frame sync set (this is the first subframe).
224 if(current_frame_first_subframe >= movie_data.size()) {
225 movie_data.append(current_controls.copy(true));
226 //current_frame_first_subframe should be movie_data.size(), so it is right.
227 pollcounters.increment_polls(port, controller, ctrl);
228 frames_in_movie++;
229 return movie_data[current_frame_first_subframe].axis3(port, controller, ctrl);
231 short new_value = current_controls.axis3(port, controller, ctrl);
232 //Fortunately, we know this frame is the last one in movie_data.
233 uint32_t pollcounter = pollcounters.get_polls(port, controller, ctrl);
234 if(current_frame_first_subframe + pollcounter < movie_data.size()) {
235 //The index is within existing size. Change the value and propagate to all subsequent
236 //subframes.
237 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++)
238 movie_data[i].axis3(port, controller, ctrl, new_value);
239 } else if(new_value != movie_data[movie_data.size() - 1].axis3(port, controller, ctrl)) {
240 //The index is not within existing size and value does not match. We need to create a new
241 //subframes(s), copying the last subframe.
242 while(current_frame_first_subframe + pollcounter >= movie_data.size())
243 movie_data.append(movie_data[movie_data.size() - 1].copy(false));
244 movie_data[current_frame_first_subframe + pollcounter].axis3(port, controller, ctrl,
245 new_value);
247 pollcounters.increment_polls(port, controller, ctrl);
248 return new_value;
252 movie::movie() throw(std::bad_alloc)
254 seqno = 0;
255 readonly = false;
256 rerecords = "0";
257 _project_id = "";
258 current_frame = 0;
259 frames_in_movie = 0;
260 current_frame_first_subframe = 0;
261 lag_frames = 0;
262 pflag_handler = NULL;
263 clear_caches();
266 void movie::load(const std::string& rerecs, const std::string& project_id, controller_frame_vector& input)
267 throw(std::bad_alloc, std::runtime_error)
269 if(input.size() > 0 && !input[0].sync())
270 throw std::runtime_error("First subframe MUST have frame sync flag set");
271 seqno++;
272 clear_caches();
273 frames_in_movie = 0;
274 for(size_t i = 0; i < input.size(); i++)
275 if(input[i].sync())
276 frames_in_movie++;
277 readonly = true;
278 rerecords = rerecs;
279 _project_id = project_id;
280 current_frame = 0;
281 current_frame_first_subframe = 0;
282 pollcounters = pollcounter_vector(input.get_types());
283 lag_frames = 0;
284 movie_data = input;
285 //This is to force internal type of current_controls to become correct.
286 current_controls = input.blank_frame(false);
289 controller_frame_vector movie::save() throw(std::bad_alloc)
291 return movie_data;
294 unsigned movie::next_poll_number()
296 return pollcounters.max_polls() + 1;
299 void movie::readonly_mode(bool enable) throw(std::bad_alloc)
301 bool was_in_readonly = readonly;
302 readonly = enable;
303 if(was_in_readonly && !readonly) {
304 clear_caches();
305 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
306 if(current_frame == 0) {
307 //WTF... At before first frame. Blank the entiere movie.
308 frames_in_movie = 0;
309 movie_data.clear();
310 return;
312 //Fun special case: Current frame is not in movie (current_frame_first_subframe >= movie_data.size()).
313 //In this case, we have to extend the movie data.
314 if(current_frame_first_subframe >= movie_data.size()) {
315 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
316 while(frames_in_movie < current_frame) {
317 movie_data.append(movie_data.blank_frame(true));
318 frames_in_movie++;
320 current_frame_first_subframe = movie_data.size() - 1;
323 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
324 //forward values with smaller poll counters.
325 uint64_t next_frame_first_subframe = current_frame_first_subframe +
326 count_changes(current_frame_first_subframe);
327 uint64_t max_readable_subframes = current_frame_first_subframe + pollcounters.max_polls();
328 if(max_readable_subframes > next_frame_first_subframe)
329 max_readable_subframes = next_frame_first_subframe;
331 movie_data.resize(max_readable_subframes);
332 next_frame_first_subframe = max_readable_subframes;
333 //Propagate buttons. The only one that needs special handling is sync flag (index 0, tuple 0,0,0).
334 for(size_t i = 1; i < movie_data.get_types().indices(); i++) {
335 uint32_t polls = pollcounters.get_polls(i);
336 polls = polls ? polls : 1;
337 for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
338 movie_data[j].axis2(i, movie_data[current_frame_first_subframe + polls - 1].axis2(i));
340 frames_in_movie = current_frame - ((current_frame_first_subframe >= movie_data.size()) ? 1 : 0);
344 //Save state of movie code.
345 void movie::save_state(std::string& proj_id, uint64_t& curframe, uint64_t& lagframes, std::vector<uint32_t>& pcounters)
346 throw(std::bad_alloc)
348 pollcounters.save_state(pcounters);
349 proj_id = _project_id;
350 curframe = current_frame;
351 lagframes = lag_frames;
354 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
355 size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
356 controller_frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
357 std::runtime_error)
359 if(!pollcounters.check(pcounters))
360 throw std::runtime_error("Wrong number of poll counters");
361 if(old_movie && !movies_compatible(*old_movie, movie_data, curframe, &pcounters[0], old_projectid,
362 _project_id))
363 throw std::runtime_error("Save is not from this movie");
364 uint64_t tmp_firstsubframe = 0;
365 for(uint64_t i = 1; i < curframe; i++)
366 tmp_firstsubframe = tmp_firstsubframe + count_changes(tmp_firstsubframe);
367 //Checks have passed, copy the data.
368 readonly = true;
369 current_frame = curframe;
370 current_frame_first_subframe = tmp_firstsubframe;
371 lag_frames = lagframe;
372 pollcounters.load_state(pcounters);
373 readonly_mode(ro);
374 return 0;
377 uint64_t movie::frame_subframes(uint64_t frame) throw()
379 if(frame < cached_frame)
380 clear_caches();
381 uint64_t p = cached_subframe;
382 for(uint64_t i = cached_frame; i < frame; i++)
383 p = p + count_changes(p);
384 cached_frame = frame;
385 cached_subframe = p;
386 return count_changes(p);
389 void movie::clear_caches() throw()
391 cached_frame = 1;
392 cached_subframe = 0;
395 controller_frame movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
397 if(frame < cached_frame)
398 clear_caches();
399 uint64_t p = cached_subframe;
400 for(uint64_t i = cached_frame; i < frame; i++)
401 p = p + count_changes(p);
402 cached_frame = frame;
403 cached_subframe = p;
404 uint64_t max = count_changes(p);
405 if(!max) {
406 return movie_data.blank_frame(true);
408 if(max <= subframe)
409 subframe = max - 1;
410 return movie_data[p + subframe];
413 void movie::reset_state() throw()
415 readonly = true;
416 current_frame = 0;
417 current_frame_first_subframe = 0;
418 pollcounters.clear();
419 lag_frames = 0;
420 clear_caches();
423 void movie::fast_save(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
425 pollcounters.save_state(_counters);
426 _frame = current_frame;
427 _ptr = current_frame_first_subframe;
428 _lagc = lag_frames;
431 void movie::fast_load(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
433 readonly = true;
434 current_frame = _frame;
435 current_frame_first_subframe = (_ptr <= movie_data.size()) ? _ptr : movie_data.size();
436 lag_frames = _lagc;
437 pollcounters.load_state(_counters);
438 readonly_mode(false);
441 movie& movie::operator=(const movie& m)
443 seqno++;
444 readonly = m.readonly;
445 rerecords = m.rerecords;
446 _project_id = m._project_id;
447 movie_data = m.movie_data;
448 current_frame = m.current_frame;
449 current_frame_first_subframe = m.current_frame_first_subframe;
450 pollcounters = m.pollcounters;
451 current_controls = m.current_controls;
452 lag_frames = m.lag_frames;
453 frames_in_movie = m.frames_in_movie;
454 cached_frame = m.cached_frame;
455 cached_subframe = m.cached_subframe;
456 return *this;
459 void movie::adjust_frame_count(int64_t adjust)
461 uint64_t old_frames = frames_in_movie;
462 frames_in_movie += adjust;
463 //If current_frame_first_subframe is in part extended, recompute it.
464 if(current_frame > old_frames + 1) {
465 current_frame_first_subframe = 0;
466 if(current_frame > 0)
467 for(uint64_t i = 0; i < current_frame - 1; i++)
468 current_frame_first_subframe += count_changes(current_frame_first_subframe);
470 //Nobody is this stupid, right?
471 current_frame_first_subframe = min(current_frame_first_subframe, static_cast<uint64_t>(movie_data.size()));
474 void movie::set_pflag_handler(poll_flag* handler)
476 pflag_handler = handler;
479 movie::poll_flag::~poll_flag()