Actually call on_reset callback
[lsnes.git] / src / library / movie.cpp
blob03543529cef2df583ea7578f169b262483b329c2
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 const char* movie_id = "Movies";
15 bool movies_compatible(portctrl::frame_vector& old_movie, portctrl::frame_vector& new_movie,
16 uint64_t frame, const uint32_t* polls, const std::string& old_projectid,
17 const std::string& new_projectid)
19 //Project IDs have to match.
20 if(old_projectid != new_projectid)
21 return false;
22 return old_movie.compatible(new_movie, frame, polls);
26 movie::~movie()
28 if(movie_data)
29 movie_data->clear_framecount_notification(_listener);
32 void movie::set_all_DRDY() throw()
34 pollcounters.set_all_DRDY();
37 std::string movie::rerecord_count()
39 return rerecords;
42 void movie::rerecord_count(const std::string& count)
44 rerecords = count;
47 std::string movie::project_id()
49 return _project_id;
52 void movie::project_id(const std::string& id)
54 _project_id = id;
57 bool movie::readonly_mode() throw()
59 return readonly;
62 void movie::set_controls(portctrl::frame controls) throw()
64 current_controls = controls;
67 uint32_t movie::count_changes(uint64_t first_subframe) throw()
69 return movie_data->subframe_count(first_subframe);
72 portctrl::frame movie::get_controls() throw()
74 if(!readonly)
75 return current_controls;
76 portctrl::frame c = movie_data->blank_frame(false);
77 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
78 if(current_frame == 0)
79 return c;
80 //Otherwise find the last valid frame of input.
81 uint32_t changes = count_changes(current_frame_first_subframe);
82 if(!changes)
83 return c; //End of movie.
84 for(size_t i = 0; i < movie_data->get_types().indices(); i++) {
85 uint32_t polls = pollcounters.get_polls(i);
86 uint32_t index = (changes > polls) ? polls : changes - 1;
87 c.axis2(i, (*movie_data)[current_frame_first_subframe + index].axis2(i));
89 return c;
92 uint64_t movie::get_current_frame() throw()
94 return current_frame;
97 uint64_t movie::get_lag_frames() throw()
99 return lag_frames;
102 void movie::next_frame()
104 //Adjust lag count. Frame 0 MUST NOT be considered lag.
105 bool pflag = pflag_handler ? pflag_handler->get_pflag() : false;
106 if(current_frame && !pflag)
107 lag_frames++;
108 else if(pflag_handler)
109 pflag_handler->set_pflag(false);
111 //If all poll counters are zero for all real controls, this frame is lag.
112 bool this_frame_lag = !pollcounters.has_polled();
113 //Oh, frame 0 must not be considered lag.
114 if(current_frame && this_frame_lag) {
115 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
116 if(!readonly) {
117 //If in read-write mode, write a dummy record for the frame. Force sync flag.
118 //As index should be movie_data->size(), it is correct afterwards.
119 movie_data->append(current_controls.copy(true));
123 //Reset the poll counters and DRDY flags.
124 pollcounters.clear();
126 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
127 //frame 0 and 0 for frame 1.
128 if(current_frame)
129 current_frame_first_subframe = current_frame_first_subframe +
130 count_changes(current_frame_first_subframe);
131 else
132 current_frame_first_subframe = 0;
133 current_frame++;
136 bool movie::get_DRDY(unsigned port, unsigned controller, unsigned ctrl)
138 return pollcounters.get_DRDY(port, controller, ctrl);
141 void movie::set_frob_with_value(std::function<void(unsigned, unsigned, unsigned, short&)> func)
143 frob_with_value = func;
146 short movie::next_input(unsigned port, unsigned controller, unsigned ctrl)
148 pollcounters.clear_DRDY(port, controller, ctrl);
150 if(readonly) {
151 //In readonly mode...
152 //If at the end of the movie, return released / neutral (but also record the poll)...
153 if(current_frame_first_subframe >= movie_data->size()) {
154 pollcounters.increment_polls(port, controller, ctrl);
155 return 0;
157 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
158 if(current_frame == 0)
159 return 0;
160 //Otherwise find the last valid frame of input.
161 uint32_t changes = count_changes(current_frame_first_subframe);
162 uint32_t polls = pollcounters.get_polls(port, controller, ctrl);
163 uint32_t index = (changes > polls) ? polls : changes - 1;
164 int16_t data = (*movie_data)[current_frame_first_subframe + index].axis3(port, controller, ctrl);
165 pollcounters.increment_polls(port, controller, ctrl);
166 return data;
167 } else {
168 //Readwrite mode.
169 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
170 //Also, frame 0 must not be added to movie file.
171 if(current_frame == 0)
172 return 0;
173 //If at movie end, insert complete input with frame sync set (this is the first subframe).
174 if(current_frame_first_subframe >= movie_data->size()) {
175 movie_data->append(current_controls.copy(true));
176 //current_frame_first_subframe should be movie_data->size(), so it is right.
177 pollcounters.increment_polls(port, controller, ctrl);
178 return (*movie_data)[current_frame_first_subframe].axis3(port, controller, ctrl);
180 short new_value = current_controls.axis3(port, controller, ctrl);
181 frob_with_value(port, controller, ctrl, new_value);
182 //Fortunately, we know this frame is the last one in movie_data.
183 uint32_t pollcounter = pollcounters.get_polls(port, controller, ctrl);
184 if(current_frame_first_subframe + pollcounter < movie_data->size()) {
185 //The index is within existing size. Change the value and propagate to all subsequent
186 //subframes.
187 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data->size(); i++)
188 (*movie_data)[i].axis3(port, controller, ctrl, new_value);
189 } else if(new_value != (*movie_data)[movie_data->size() - 1].axis3(port, controller, ctrl)) {
190 //The index is not within existing size and value does not match. We need to create a new
191 //subframes(s), copying the last subframe.
192 while(current_frame_first_subframe + pollcounter >= movie_data->size())
193 movie_data->append((*movie_data)[movie_data->size() - 1].copy(false));
194 (*movie_data)[current_frame_first_subframe + pollcounter].axis3(port, controller, ctrl,
195 new_value);
197 pollcounters.increment_polls(port, controller, ctrl);
198 return new_value;
202 movie::movie()
203 : _listener(*this), tracker(memtracker::singleton(), movie_id, sizeof(*this))
205 frob_with_value = [](unsigned a, unsigned b, unsigned c, short& d){};
206 movie_data = NULL;
207 seqno = 0;
208 readonly = false;
209 rerecords = "0";
210 _project_id = "";
211 current_frame = 0;
212 current_frame_first_subframe = 0;
213 lag_frames = 0;
214 pflag_handler = NULL;
215 clear_caches();
218 void movie::load(const std::string& rerecs, const std::string& project_id, portctrl::frame_vector& input)
220 if(input.size() > 0 && !input[0].sync())
221 throw std::runtime_error("First subframe MUST have frame sync flag set");
222 seqno++;
223 clear_caches();
224 readonly = true;
225 rerecords = rerecs;
226 _project_id = project_id;
227 current_frame = 0;
228 current_frame_first_subframe = 0;
229 pollcounters = portctrl::counters(input.get_types());
230 lag_frames = 0;
231 //This is to force internal type of current_controls to become correct.
232 current_controls = input.blank_frame(false);
235 unsigned movie::next_poll_number()
237 return pollcounters.max_polls() + 1;
240 void movie::readonly_mode(bool enable)
242 bool was_in_readonly = readonly;
243 readonly = enable;
244 if(was_in_readonly && !readonly) {
245 clear_caches();
246 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
247 if(current_frame == 0) {
248 //WTF... At before first frame. Blank the entiere movie.
249 movie_data->clear();
250 return;
252 //Fun special case: Current frame is not in movie (current_frame_first_subframe >=
253 //movie_data->size()). In this case, we have to extend the movie data.
254 if(current_frame_first_subframe >= movie_data->size()) {
255 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
256 while(movie_data->count_frames() < current_frame)
257 movie_data->append(movie_data->blank_frame(true));
258 current_frame_first_subframe = movie_data->size() - 1;
261 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
262 //forward values with smaller poll counters.
263 uint64_t next_frame_first_subframe = current_frame_first_subframe +
264 count_changes(current_frame_first_subframe);
265 uint64_t max_readable_subframes = current_frame_first_subframe + pollcounters.max_polls();
266 if(max_readable_subframes > next_frame_first_subframe)
267 max_readable_subframes = next_frame_first_subframe;
269 movie_data->resize(max_readable_subframes);
270 next_frame_first_subframe = max_readable_subframes;
271 //Propagate buttons. The only one that needs special handling is sync flag (index 0, tuple 0,0,0).
272 for(size_t i = 1; i < movie_data->get_types().indices(); i++) {
273 uint32_t polls = pollcounters.get_polls(i);
274 polls = polls ? polls : 1;
275 for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
276 (*movie_data)[j].axis2(i, (*movie_data)[current_frame_first_subframe + polls - 1].
277 axis2(i));
282 //Save state of movie code.
283 void movie::save_state(std::string& proj_id, uint64_t& curframe, uint64_t& lagframes, std::vector<uint32_t>& pcounters)
285 pollcounters.save_state(pcounters);
286 proj_id = _project_id;
287 curframe = current_frame;
288 lagframes = lag_frames;
291 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
292 size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
293 portctrl::frame_vector* old_movie, const std::string& old_projectid)
295 if(!pollcounters.check(pcounters))
296 throw std::runtime_error("Wrong number of poll counters");
297 if(old_movie && !movies_compatible(*old_movie, *movie_data, curframe, &pcounters[0], old_projectid,
298 _project_id))
299 throw std::runtime_error("Save is not from this movie");
300 uint64_t tmp_firstsubframe = 0;
301 for(uint64_t i = 1; i < curframe; i++)
302 tmp_firstsubframe = tmp_firstsubframe + count_changes(tmp_firstsubframe);
303 //Checks have passed, copy the data.
304 readonly = true;
305 current_frame = curframe;
306 current_frame_first_subframe = tmp_firstsubframe;
307 lag_frames = lagframe;
308 pollcounters.load_state(pcounters);
309 readonly_mode(ro);
310 return 0;
313 uint64_t movie::frame_subframes(uint64_t frame) throw()
315 if(!frame) return 0;
316 if(frame > movie_data->size()) return 0;
317 if(frame < cached_frame)
318 clear_caches();
319 uint64_t p = cached_subframe;
320 for(uint64_t i = cached_frame; i < frame; i++)
321 p = p + count_changes(p);
322 cached_frame = frame;
323 cached_subframe = p;
324 return count_changes(p);
327 void movie::clear_caches() throw()
329 cached_frame = 1;
330 cached_subframe = 0;
333 portctrl::frame movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
335 if(frame < cached_frame)
336 clear_caches();
337 uint64_t p = cached_subframe;
338 for(uint64_t i = cached_frame; i < frame; i++)
339 p = p + count_changes(p);
340 cached_frame = frame;
341 cached_subframe = p;
342 uint64_t max = count_changes(p);
343 if(!max) {
344 return movie_data->blank_frame(true);
346 if(max <= subframe)
347 subframe = max - 1;
348 return (*movie_data)[p + subframe];
351 void movie::reset_state() throw()
353 readonly = true;
354 current_frame = 0;
355 current_frame_first_subframe = 0;
356 pollcounters.clear();
357 lag_frames = 0;
358 clear_caches();
361 void movie::fast_save(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
363 pollcounters.save_state(_counters);
364 _frame = current_frame;
365 _ptr = current_frame_first_subframe;
366 _lagc = lag_frames;
369 void movie::fast_load(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
371 readonly = true;
372 current_frame = _frame;
373 current_frame_first_subframe = (_ptr <= movie_data->size()) ? _ptr : movie_data->size();
374 lag_frames = _lagc;
375 pollcounters.load_state(_counters);
376 readonly_mode(false);
379 void movie::set_pflag_handler(poll_flag* handler)
381 pflag_handler = handler;
384 int16_t movie::read_subframe_at_index(uint32_t subframe, unsigned port, unsigned controller, unsigned ctrl)
386 //Readwrite, Past the end of movie or before the beginning?
387 if(!readonly || current_frame_first_subframe >= movie_data->size() || current_frame == 0)
388 return 0;
389 uint32_t changes = count_changes(current_frame_first_subframe);
390 uint32_t index = (changes > subframe) ? subframe : changes - 1;
391 return (*movie_data)[current_frame_first_subframe + index].axis3(port, controller, ctrl);
394 void movie::write_subframe_at_index(uint32_t subframe, unsigned port, unsigned controller, unsigned ctrl,
395 int16_t x)
397 if(!readonly || current_frame == 0)
398 return;
399 bool extended = false;
400 while(current_frame > movie_data->count_frames()) {
401 //Extend the movie by a blank frame.
402 extended = true;
403 movie_data->append(movie_data->blank_frame(true));
405 if(extended) {
406 clear_caches();
407 current_frame_first_subframe = movie_data->size() - 1;
409 if(current_frame < movie_data->count_frames()) {
410 //If we are not on the last frame, write is possible if it is not on extension.
411 uint32_t changes = count_changes(current_frame_first_subframe);
412 if(subframe < changes)
413 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
414 } else {
415 //Writing to the last frame. If not on extension, handle like non-last frame.
416 //Note that if movie had to be extended, it was done before, resulting movie like in state with
417 //0 stored subframes.
418 uint32_t changes = count_changes(current_frame_first_subframe);
419 if(subframe < changes)
420 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
421 else {
422 //If there is no frame at all, create one.
423 if(current_frame_first_subframe >= movie_data->size()) {
424 movie_data->append(movie_data->blank_frame(true));
426 //Create needed subframes.
427 while(count_changes(current_frame_first_subframe) <= subframe)
428 movie_data->append(movie_data->blank_frame(false));
429 //Write it.
430 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
435 movie::poll_flag::~poll_flag()
439 void movie::set_movie_data(portctrl::frame_vector* data)
441 portctrl::frame_vector* old = movie_data;
442 if(data)
443 data->set_framecount_notification(_listener);
444 movie_data = data;
445 clear_caches();
446 if(old)
447 old->clear_framecount_notification(_listener);
450 movie::fchange_listener::fchange_listener(movie& m)
451 : mov(m)
455 movie::fchange_listener::~fchange_listener()
459 void movie::fchange_listener::notify(portctrl::frame_vector& src, uint64_t old)
461 //Recompute frame_first_subframe.
462 while(mov.current_frame_first_subframe < mov.movie_data->size() && mov.current_frame > old + 1) {
463 //OK, movie has been extended.
464 mov.current_frame_first_subframe += mov.count_changes(mov.current_frame_first_subframe);
465 old++;
467 //Nobody is this stupid, right?
468 mov.current_frame_first_subframe = min(mov.current_frame_first_subframe,
469 static_cast<uint64_t>(mov.movie_data->size()));