Small whitespace cleanup
[lsnes.git] / src / library / movie.cpp
blobf3ce5d76ed408734e0b0d6ed3cba0daecd3eb968
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() throw(std::bad_alloc)
39 return rerecords;
42 void movie::rerecord_count(const std::string& count) throw(std::bad_alloc)
44 rerecords = count;
47 std::string movie::project_id() throw(std::bad_alloc)
49 return _project_id;
52 void movie::project_id(const std::string& id) throw(std::bad_alloc)
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() throw(std::bad_alloc)
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) throw(std::logic_error)
138 return pollcounters.get_DRDY(port, controller, ctrl);
141 short movie::next_input(unsigned port, unsigned controller, unsigned ctrl) throw(std::bad_alloc, std::logic_error)
143 pollcounters.clear_DRDY(port, controller, ctrl);
145 if(readonly) {
146 //In readonly mode...
147 //If at the end of the movie, return released / neutral (but also record the poll)...
148 if(current_frame_first_subframe >= movie_data->size()) {
149 pollcounters.increment_polls(port, controller, ctrl);
150 return 0;
152 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
153 if(current_frame == 0)
154 return 0;
155 //Otherwise find the last valid frame of input.
156 uint32_t changes = count_changes(current_frame_first_subframe);
157 uint32_t polls = pollcounters.get_polls(port, controller, ctrl);
158 uint32_t index = (changes > polls) ? polls : changes - 1;
159 int16_t data = (*movie_data)[current_frame_first_subframe + index].axis3(port, controller, ctrl);
160 pollcounters.increment_polls(port, controller, ctrl);
161 return data;
162 } else {
163 //Readwrite mode.
164 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
165 //Also, frame 0 must not be added to movie file.
166 if(current_frame == 0)
167 return 0;
168 //If at movie end, insert complete input with frame sync set (this is the first subframe).
169 if(current_frame_first_subframe >= movie_data->size()) {
170 movie_data->append(current_controls.copy(true));
171 //current_frame_first_subframe should be movie_data->size(), so it is right.
172 pollcounters.increment_polls(port, controller, ctrl);
173 return (*movie_data)[current_frame_first_subframe].axis3(port, controller, ctrl);
175 short new_value = current_controls.axis3(port, controller, ctrl);
176 //Fortunately, we know this frame is the last one in movie_data.
177 uint32_t pollcounter = pollcounters.get_polls(port, controller, ctrl);
178 if(current_frame_first_subframe + pollcounter < movie_data->size()) {
179 //The index is within existing size. Change the value and propagate to all subsequent
180 //subframes.
181 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data->size(); i++)
182 (*movie_data)[i].axis3(port, controller, ctrl, new_value);
183 } else if(new_value != (*movie_data)[movie_data->size() - 1].axis3(port, controller, ctrl)) {
184 //The index is not within existing size and value does not match. We need to create a new
185 //subframes(s), copying the last subframe.
186 while(current_frame_first_subframe + pollcounter >= movie_data->size())
187 movie_data->append((*movie_data)[movie_data->size() - 1].copy(false));
188 (*movie_data)[current_frame_first_subframe + pollcounter].axis3(port, controller, ctrl,
189 new_value);
191 pollcounters.increment_polls(port, controller, ctrl);
192 return new_value;
196 movie::movie() throw(std::bad_alloc)
197 : _listener(*this), tracker(memtracker::singleton(), movie_id, sizeof(*this))
199 movie_data = NULL;
200 seqno = 0;
201 readonly = false;
202 rerecords = "0";
203 _project_id = "";
204 current_frame = 0;
205 current_frame_first_subframe = 0;
206 lag_frames = 0;
207 pflag_handler = NULL;
208 clear_caches();
211 void movie::load(const std::string& rerecs, const std::string& project_id, portctrl::frame_vector& input)
212 throw(std::bad_alloc, std::runtime_error)
214 if(input.size() > 0 && !input[0].sync())
215 throw std::runtime_error("First subframe MUST have frame sync flag set");
216 seqno++;
217 clear_caches();
218 readonly = true;
219 rerecords = rerecs;
220 _project_id = project_id;
221 current_frame = 0;
222 current_frame_first_subframe = 0;
223 pollcounters = portctrl::counters(input.get_types());
224 lag_frames = 0;
225 //This is to force internal type of current_controls to become correct.
226 current_controls = input.blank_frame(false);
229 unsigned movie::next_poll_number()
231 return pollcounters.max_polls() + 1;
234 void movie::readonly_mode(bool enable) throw(std::bad_alloc)
236 bool was_in_readonly = readonly;
237 readonly = enable;
238 if(was_in_readonly && !readonly) {
239 clear_caches();
240 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
241 if(current_frame == 0) {
242 //WTF... At before first frame. Blank the entiere movie.
243 movie_data->clear();
244 return;
246 //Fun special case: Current frame is not in movie (current_frame_first_subframe >=
247 //movie_data->size()). In this case, we have to extend the movie data.
248 if(current_frame_first_subframe >= movie_data->size()) {
249 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
250 while(movie_data->count_frames() < current_frame)
251 movie_data->append(movie_data->blank_frame(true));
252 current_frame_first_subframe = movie_data->size() - 1;
255 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
256 //forward values with smaller poll counters.
257 uint64_t next_frame_first_subframe = current_frame_first_subframe +
258 count_changes(current_frame_first_subframe);
259 uint64_t max_readable_subframes = current_frame_first_subframe + pollcounters.max_polls();
260 if(max_readable_subframes > next_frame_first_subframe)
261 max_readable_subframes = next_frame_first_subframe;
263 movie_data->resize(max_readable_subframes);
264 next_frame_first_subframe = max_readable_subframes;
265 //Propagate buttons. The only one that needs special handling is sync flag (index 0, tuple 0,0,0).
266 for(size_t i = 1; i < movie_data->get_types().indices(); i++) {
267 uint32_t polls = pollcounters.get_polls(i);
268 polls = polls ? polls : 1;
269 for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
270 (*movie_data)[j].axis2(i, (*movie_data)[current_frame_first_subframe + polls - 1].
271 axis2(i));
276 //Save state of movie code.
277 void movie::save_state(std::string& proj_id, uint64_t& curframe, uint64_t& lagframes, std::vector<uint32_t>& pcounters)
278 throw(std::bad_alloc)
280 pollcounters.save_state(pcounters);
281 proj_id = _project_id;
282 curframe = current_frame;
283 lagframes = lag_frames;
286 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
287 size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
288 portctrl::frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
289 std::runtime_error)
291 if(!pollcounters.check(pcounters))
292 throw std::runtime_error("Wrong number of poll counters");
293 if(old_movie && !movies_compatible(*old_movie, *movie_data, curframe, &pcounters[0], old_projectid,
294 _project_id))
295 throw std::runtime_error("Save is not from this movie");
296 uint64_t tmp_firstsubframe = 0;
297 for(uint64_t i = 1; i < curframe; i++)
298 tmp_firstsubframe = tmp_firstsubframe + count_changes(tmp_firstsubframe);
299 //Checks have passed, copy the data.
300 readonly = true;
301 current_frame = curframe;
302 current_frame_first_subframe = tmp_firstsubframe;
303 lag_frames = lagframe;
304 pollcounters.load_state(pcounters);
305 readonly_mode(ro);
306 return 0;
309 uint64_t movie::frame_subframes(uint64_t frame) throw()
311 if(frame < cached_frame)
312 clear_caches();
313 uint64_t p = cached_subframe;
314 for(uint64_t i = cached_frame; i < frame; i++)
315 p = p + count_changes(p);
316 cached_frame = frame;
317 cached_subframe = p;
318 return count_changes(p);
321 void movie::clear_caches() throw()
323 cached_frame = 1;
324 cached_subframe = 0;
327 portctrl::frame movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
329 if(frame < cached_frame)
330 clear_caches();
331 uint64_t p = cached_subframe;
332 for(uint64_t i = cached_frame; i < frame; i++)
333 p = p + count_changes(p);
334 cached_frame = frame;
335 cached_subframe = p;
336 uint64_t max = count_changes(p);
337 if(!max) {
338 return movie_data->blank_frame(true);
340 if(max <= subframe)
341 subframe = max - 1;
342 return (*movie_data)[p + subframe];
345 void movie::reset_state() throw()
347 readonly = true;
348 current_frame = 0;
349 current_frame_first_subframe = 0;
350 pollcounters.clear();
351 lag_frames = 0;
352 clear_caches();
355 void movie::fast_save(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
357 pollcounters.save_state(_counters);
358 _frame = current_frame;
359 _ptr = current_frame_first_subframe;
360 _lagc = lag_frames;
363 void movie::fast_load(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
365 readonly = true;
366 current_frame = _frame;
367 current_frame_first_subframe = (_ptr <= movie_data->size()) ? _ptr : movie_data->size();
368 lag_frames = _lagc;
369 pollcounters.load_state(_counters);
370 readonly_mode(false);
373 void movie::set_pflag_handler(poll_flag* handler)
375 pflag_handler = handler;
378 int16_t movie::read_subframe_at_index(uint32_t subframe, unsigned port, unsigned controller, unsigned ctrl)
380 //Readwrite, Past the end of movie or before the beginning?
381 if(!readonly || current_frame_first_subframe >= movie_data->size() || current_frame == 0)
382 return 0;
383 uint32_t changes = count_changes(current_frame_first_subframe);
384 uint32_t index = (changes > subframe) ? subframe : changes - 1;
385 return (*movie_data)[current_frame_first_subframe + index].axis3(port, controller, ctrl);
388 void movie::write_subframe_at_index(uint32_t subframe, unsigned port, unsigned controller, unsigned ctrl,
389 int16_t x)
391 if(!readonly || current_frame == 0)
392 return;
393 bool extended = false;
394 while(current_frame > movie_data->count_frames()) {
395 //Extend the movie by a blank frame.
396 extended = true;
397 movie_data->append(movie_data->blank_frame(true));
399 if(extended) {
400 clear_caches();
401 current_frame_first_subframe = movie_data->size() - 1;
403 if(current_frame < movie_data->count_frames()) {
404 //If we are not on the last frame, write is possible if it is not on extension.
405 uint32_t changes = count_changes(current_frame_first_subframe);
406 if(subframe < changes)
407 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
408 } else {
409 //Writing to the last frame. If not on extension, handle like non-last frame.
410 //Note that if movie had to be extended, it was done before, resulting movie like in state with
411 //0 stored subframes.
412 uint32_t changes = count_changes(current_frame_first_subframe);
413 if(subframe < changes)
414 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
415 else {
416 //If there is no frame at all, create one.
417 if(current_frame_first_subframe >= movie_data->size()) {
418 movie_data->append(movie_data->blank_frame(true));
420 //Create needed subframes.
421 while(count_changes(current_frame_first_subframe) <= subframe)
422 movie_data->append(movie_data->blank_frame(false));
423 //Write it.
424 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
429 movie::poll_flag::~poll_flag()
433 void movie::set_movie_data(portctrl::frame_vector* data)
435 portctrl::frame_vector* old = movie_data;
436 if(data)
437 data->set_framecount_notification(_listener);
438 movie_data = data;
439 clear_caches();
440 if(old)
441 old->clear_framecount_notification(_listener);
444 movie::fchange_listener::fchange_listener(movie& m)
445 : mov(m)
449 movie::fchange_listener::~fchange_listener()
453 void movie::fchange_listener::notify(portctrl::frame_vector& src, uint64_t old)
455 //Recompute frame_first_subframe.
456 while(mov.current_frame_first_subframe < mov.movie_data->size() && mov.current_frame > old + 1) {
457 //OK, movie has been extended.
458 mov.current_frame_first_subframe += mov.count_changes(mov.current_frame_first_subframe);
459 old++;
461 //Nobody is this stupid, right?
462 mov.current_frame_first_subframe = min(mov.current_frame_first_subframe,
463 static_cast<uint64_t>(mov.movie_data->size()));