Implicitly redirect cmdhelp includes to corresponding JSON files
[lsnes.git] / src / library / movie.cpp
blob16887c44368118f708138541ff365c0444e48be8
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 bool movies_compatible(portctrl::frame_vector& old_movie, portctrl::frame_vector& new_movie,
15 uint64_t frame, const uint32_t* polls, const std::string& old_projectid,
16 const std::string& new_projectid)
18 //Project IDs have to match.
19 if(old_projectid != new_projectid)
20 return false;
21 return old_movie.compatible(new_movie, frame, polls);
25 movie::~movie()
27 if(movie_data)
28 movie_data->clear_framecount_notification(_listener);
31 void movie::set_all_DRDY() throw()
33 pollcounters.set_all_DRDY();
36 std::string movie::rerecord_count() throw(std::bad_alloc)
38 return rerecords;
41 void movie::rerecord_count(const std::string& count) throw(std::bad_alloc)
43 rerecords = count;
46 std::string movie::project_id() throw(std::bad_alloc)
48 return _project_id;
51 void movie::project_id(const std::string& id) throw(std::bad_alloc)
53 _project_id = id;
56 bool movie::readonly_mode() throw()
58 return readonly;
61 void movie::set_controls(portctrl::frame controls) throw()
63 current_controls = controls;
66 uint32_t movie::count_changes(uint64_t first_subframe) throw()
68 return movie_data->subframe_count(first_subframe);
71 portctrl::frame movie::get_controls() throw()
73 if(!readonly)
74 return current_controls;
75 portctrl::frame c = movie_data->blank_frame(false);
76 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
77 if(current_frame == 0)
78 return c;
79 //Otherwise find the last valid frame of input.
80 uint32_t changes = count_changes(current_frame_first_subframe);
81 if(!changes)
82 return c; //End of movie.
83 for(size_t i = 0; i < movie_data->get_types().indices(); i++) {
84 uint32_t polls = pollcounters.get_polls(i);
85 uint32_t index = (changes > polls) ? polls : changes - 1;
86 c.axis2(i, (*movie_data)[current_frame_first_subframe + index].axis2(i));
88 return c;
91 uint64_t movie::get_current_frame() throw()
93 return current_frame;
96 uint64_t movie::get_lag_frames() throw()
98 return lag_frames;
101 void movie::next_frame() throw(std::bad_alloc)
103 //Adjust lag count. Frame 0 MUST NOT be considered lag.
104 bool pflag = pflag_handler ? pflag_handler->get_pflag() : false;
105 if(current_frame && !pflag)
106 lag_frames++;
107 else if(pflag_handler)
108 pflag_handler->set_pflag(false);
110 //If all poll counters are zero for all real controls, this frame is lag.
111 bool this_frame_lag = !pollcounters.has_polled();
112 //Oh, frame 0 must not be considered lag.
113 if(current_frame && this_frame_lag) {
114 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
115 if(!readonly) {
116 //If in read-write mode, write a dummy record for the frame. Force sync flag.
117 //As index should be movie_data->size(), it is correct afterwards.
118 movie_data->append(current_controls.copy(true));
122 //Reset the poll counters and DRDY flags.
123 pollcounters.clear();
125 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
126 //frame 0 and 0 for frame 1.
127 if(current_frame)
128 current_frame_first_subframe = current_frame_first_subframe +
129 count_changes(current_frame_first_subframe);
130 else
131 current_frame_first_subframe = 0;
132 current_frame++;
135 bool movie::get_DRDY(unsigned port, unsigned controller, unsigned ctrl) throw(std::logic_error)
137 return pollcounters.get_DRDY(port, controller, ctrl);
140 short movie::next_input(unsigned port, unsigned controller, unsigned ctrl) throw(std::bad_alloc, std::logic_error)
142 pollcounters.clear_DRDY(port, controller, ctrl);
144 if(readonly) {
145 //In readonly mode...
146 //If at the end of the movie, return released / neutral (but also record the poll)...
147 if(current_frame_first_subframe >= movie_data->size()) {
148 pollcounters.increment_polls(port, controller, ctrl);
149 return 0;
151 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
152 if(current_frame == 0)
153 return 0;
154 //Otherwise find the last valid frame of input.
155 uint32_t changes = count_changes(current_frame_first_subframe);
156 uint32_t polls = pollcounters.get_polls(port, controller, ctrl);
157 uint32_t index = (changes > polls) ? polls : changes - 1;
158 int16_t data = (*movie_data)[current_frame_first_subframe + index].axis3(port, controller, ctrl);
159 pollcounters.increment_polls(port, controller, ctrl);
160 return data;
161 } else {
162 //Readwrite mode.
163 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
164 //Also, frame 0 must not be added to movie file.
165 if(current_frame == 0)
166 return 0;
167 //If at movie end, insert complete input with frame sync set (this is the first subframe).
168 if(current_frame_first_subframe >= movie_data->size()) {
169 movie_data->append(current_controls.copy(true));
170 //current_frame_first_subframe should be movie_data->size(), so it is right.
171 pollcounters.increment_polls(port, controller, ctrl);
172 return (*movie_data)[current_frame_first_subframe].axis3(port, controller, ctrl);
174 short new_value = current_controls.axis3(port, controller, ctrl);
175 //Fortunately, we know this frame is the last one in movie_data.
176 uint32_t pollcounter = pollcounters.get_polls(port, controller, ctrl);
177 if(current_frame_first_subframe + pollcounter < movie_data->size()) {
178 //The index is within existing size. Change the value and propagate to all subsequent
179 //subframes.
180 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data->size(); i++)
181 (*movie_data)[i].axis3(port, controller, ctrl, new_value);
182 } else if(new_value != (*movie_data)[movie_data->size() - 1].axis3(port, controller, ctrl)) {
183 //The index is not within existing size and value does not match. We need to create a new
184 //subframes(s), copying the last subframe.
185 while(current_frame_first_subframe + pollcounter >= movie_data->size())
186 movie_data->append((*movie_data)[movie_data->size() - 1].copy(false));
187 (*movie_data)[current_frame_first_subframe + pollcounter].axis3(port, controller, ctrl,
188 new_value);
190 pollcounters.increment_polls(port, controller, ctrl);
191 return new_value;
195 movie::movie() throw(std::bad_alloc)
196 : _listener(*this)
198 movie_data = NULL;
199 seqno = 0;
200 readonly = false;
201 rerecords = "0";
202 _project_id = "";
203 current_frame = 0;
204 current_frame_first_subframe = 0;
205 lag_frames = 0;
206 pflag_handler = NULL;
207 clear_caches();
210 void movie::load(const std::string& rerecs, const std::string& project_id, portctrl::frame_vector& input)
211 throw(std::bad_alloc, std::runtime_error)
213 if(input.size() > 0 && !input[0].sync())
214 throw std::runtime_error("First subframe MUST have frame sync flag set");
215 seqno++;
216 clear_caches();
217 readonly = true;
218 rerecords = rerecs;
219 _project_id = project_id;
220 current_frame = 0;
221 current_frame_first_subframe = 0;
222 pollcounters = portctrl::counters(input.get_types());
223 lag_frames = 0;
224 //This is to force internal type of current_controls to become correct.
225 current_controls = input.blank_frame(false);
228 unsigned movie::next_poll_number()
230 return pollcounters.max_polls() + 1;
233 void movie::readonly_mode(bool enable) throw(std::bad_alloc)
235 bool was_in_readonly = readonly;
236 readonly = enable;
237 if(was_in_readonly && !readonly) {
238 clear_caches();
239 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
240 if(current_frame == 0) {
241 //WTF... At before first frame. Blank the entiere movie.
242 movie_data->clear();
243 return;
245 //Fun special case: Current frame is not in movie (current_frame_first_subframe >=
246 //movie_data->size()). In this case, we have to extend the movie data.
247 if(current_frame_first_subframe >= movie_data->size()) {
248 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
249 while(movie_data->count_frames() < current_frame)
250 movie_data->append(movie_data->blank_frame(true));
251 current_frame_first_subframe = movie_data->size() - 1;
254 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
255 //forward values with smaller poll counters.
256 uint64_t next_frame_first_subframe = current_frame_first_subframe +
257 count_changes(current_frame_first_subframe);
258 uint64_t max_readable_subframes = current_frame_first_subframe + pollcounters.max_polls();
259 if(max_readable_subframes > next_frame_first_subframe)
260 max_readable_subframes = next_frame_first_subframe;
262 movie_data->resize(max_readable_subframes);
263 next_frame_first_subframe = max_readable_subframes;
264 //Propagate buttons. The only one that needs special handling is sync flag (index 0, tuple 0,0,0).
265 for(size_t i = 1; i < movie_data->get_types().indices(); i++) {
266 uint32_t polls = pollcounters.get_polls(i);
267 polls = polls ? polls : 1;
268 for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
269 (*movie_data)[j].axis2(i, (*movie_data)[current_frame_first_subframe + polls - 1].
270 axis2(i));
275 //Save state of movie code.
276 void movie::save_state(std::string& proj_id, uint64_t& curframe, uint64_t& lagframes, std::vector<uint32_t>& pcounters)
277 throw(std::bad_alloc)
279 pollcounters.save_state(pcounters);
280 proj_id = _project_id;
281 curframe = current_frame;
282 lagframes = lag_frames;
285 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
286 size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
287 portctrl::frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
288 std::runtime_error)
290 if(!pollcounters.check(pcounters))
291 throw std::runtime_error("Wrong number of poll counters");
292 if(old_movie && !movies_compatible(*old_movie, *movie_data, curframe, &pcounters[0], old_projectid,
293 _project_id))
294 throw std::runtime_error("Save is not from this movie");
295 uint64_t tmp_firstsubframe = 0;
296 for(uint64_t i = 1; i < curframe; i++)
297 tmp_firstsubframe = tmp_firstsubframe + count_changes(tmp_firstsubframe);
298 //Checks have passed, copy the data.
299 readonly = true;
300 current_frame = curframe;
301 current_frame_first_subframe = tmp_firstsubframe;
302 lag_frames = lagframe;
303 pollcounters.load_state(pcounters);
304 readonly_mode(ro);
305 return 0;
308 uint64_t movie::frame_subframes(uint64_t frame) throw()
310 if(frame < cached_frame)
311 clear_caches();
312 uint64_t p = cached_subframe;
313 for(uint64_t i = cached_frame; i < frame; i++)
314 p = p + count_changes(p);
315 cached_frame = frame;
316 cached_subframe = p;
317 return count_changes(p);
320 void movie::clear_caches() throw()
322 cached_frame = 1;
323 cached_subframe = 0;
326 portctrl::frame movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
328 if(frame < cached_frame)
329 clear_caches();
330 uint64_t p = cached_subframe;
331 for(uint64_t i = cached_frame; i < frame; i++)
332 p = p + count_changes(p);
333 cached_frame = frame;
334 cached_subframe = p;
335 uint64_t max = count_changes(p);
336 if(!max) {
337 return movie_data->blank_frame(true);
339 if(max <= subframe)
340 subframe = max - 1;
341 return (*movie_data)[p + subframe];
344 void movie::reset_state() throw()
346 readonly = true;
347 current_frame = 0;
348 current_frame_first_subframe = 0;
349 pollcounters.clear();
350 lag_frames = 0;
351 clear_caches();
354 void movie::fast_save(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
356 pollcounters.save_state(_counters);
357 _frame = current_frame;
358 _ptr = current_frame_first_subframe;
359 _lagc = lag_frames;
362 void movie::fast_load(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
364 readonly = true;
365 current_frame = _frame;
366 current_frame_first_subframe = (_ptr <= movie_data->size()) ? _ptr : movie_data->size();
367 lag_frames = _lagc;
368 pollcounters.load_state(_counters);
369 readonly_mode(false);
372 void movie::set_pflag_handler(poll_flag* handler)
374 pflag_handler = handler;
377 int16_t movie::read_subframe_at_index(uint32_t subframe, unsigned port, unsigned controller, unsigned ctrl)
379 //Readwrite, Past the end of movie or before the beginning?
380 if(!readonly || current_frame_first_subframe >= movie_data->size() || current_frame == 0)
381 return 0;
382 uint32_t changes = count_changes(current_frame_first_subframe);
383 uint32_t index = (changes > subframe) ? subframe : changes - 1;
384 return (*movie_data)[current_frame_first_subframe + index].axis3(port, controller, ctrl);
387 void movie::write_subframe_at_index(uint32_t subframe, unsigned port, unsigned controller, unsigned ctrl,
388 int16_t x)
390 if(!readonly || current_frame == 0)
391 return;
392 bool extended = false;
393 while(current_frame > movie_data->count_frames()) {
394 //Extend the movie by a blank frame.
395 extended = true;
396 movie_data->append(movie_data->blank_frame(true));
398 if(extended) {
399 clear_caches();
400 current_frame_first_subframe = movie_data->size() - 1;
402 if(current_frame < movie_data->count_frames()) {
403 //If we are not on the last frame, write is possible if it is not on extension.
404 uint32_t changes = count_changes(current_frame_first_subframe);
405 if(subframe < changes)
406 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
407 } else {
408 //Writing to the last frame. If not on extension, handle like non-last frame.
409 //Note that if movie had to be extended, it was done before, resulting movie like in state with
410 //0 stored subframes.
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 //If there is no frame at all, create one.
416 if(current_frame_first_subframe >= movie_data->size()) {
417 movie_data->append(movie_data->blank_frame(true));
419 //Create needed subframes.
420 while(count_changes(current_frame_first_subframe) <= subframe)
421 movie_data->append(movie_data->blank_frame(false));
422 //Write it.
423 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
428 movie::poll_flag::~poll_flag()
432 void movie::set_movie_data(portctrl::frame_vector* data)
434 portctrl::frame_vector* old = movie_data;
435 if(data)
436 data->set_framecount_notification(_listener);
437 movie_data = data;
438 clear_caches();
439 if(old)
440 old->clear_framecount_notification(_listener);
443 movie::fchange_listener::fchange_listener(movie& m)
444 : mov(m)
448 movie::fchange_listener::~fchange_listener()
452 void movie::fchange_listener::notify(portctrl::frame_vector& src, uint64_t old)
454 //Recompute frame_first_subframe.
455 while(mov.current_frame_first_subframe < mov.movie_data->size() && mov.current_frame > old + 1) {
456 //OK, movie has been extended.
457 mov.current_frame_first_subframe += mov.count_changes(mov.current_frame_first_subframe);
458 old++;
460 //Nobody is this stupid, right?
461 mov.current_frame_first_subframe = min(mov.current_frame_first_subframe,
462 static_cast<uint64_t>(mov.movie_data->size()));