Fix some really retarded code in library/movie
[lsnes.git] / src / library / movie.cpp
blobee07df6aafb05ae490ad2a1c10dbcefd74203e05
1 #include "lsnes.hpp"
3 #include "library/movie.hpp"
5 #include <stdexcept>
6 #include <cassert>
7 #include <cstring>
8 #include <fstream>
11 //std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
13 namespace
15 uint64_t find_next_sync(controller_frame_vector& movie, uint64_t after)
17 if(after >= movie.size())
18 return after;
19 do {
20 after++;
21 } while(after < movie.size() && !movie[after].sync());
22 return after;
25 bool movies_compatible(controller_frame_vector& old_movie, controller_frame_vector& new_movie,
26 uint64_t frame, const uint32_t* polls, const std::string& old_projectid,
27 const std::string& new_projectid)
29 //Project IDs have to match.
30 if(old_projectid != new_projectid)
31 return false;
32 //Types have to match.
33 if(old_movie.get_types() != new_movie.get_types())
34 return false;
35 const port_type_set& pset = new_movie.get_types();
36 //If new movie is before first frame, anything with same project_id is compatible.
37 if(frame == 0)
38 return true;
39 //Scan both movies until frame syncs are seen. Out of bounds reads behave as all neutral but frame
40 //sync done.
41 uint64_t syncs_seen = 0;
42 uint64_t frames_read = 0;
43 while(syncs_seen < frame - 1) {
44 controller_frame oldc = old_movie.blank_frame(true), newc = new_movie.blank_frame(true);
45 if(frames_read < old_movie.size())
46 oldc = old_movie[frames_read];
47 if(frames_read < new_movie.size())
48 newc = new_movie[frames_read];
49 if(oldc != newc)
50 return false; //Mismatch.
51 frames_read++;
52 if(newc.sync())
53 syncs_seen++;
55 //We increment the counter one time too many.
56 frames_read--;
57 //Current frame. We need to compare each control up to poll counter.
58 uint64_t readable_old_subframes = 0, readable_new_subframes = 0;
59 uint64_t oldlen = find_next_sync(old_movie, frames_read);
60 uint64_t newlen = find_next_sync(new_movie, frames_read);
61 if(frames_read < oldlen)
62 readable_old_subframes = oldlen - frames_read;
63 if(frames_read < newlen)
64 readable_new_subframes = newlen - frames_read;
65 //Then rest of the stuff.
66 for(unsigned i = 0; i < pset.indices(); i++) {
67 uint32_t p = polls[i] & 0x7FFFFFFFUL;
68 short ov = 0, nv = 0;
69 for(uint32_t j = 0; j < p; j++) {
70 if(j < readable_old_subframes)
71 ov = old_movie[j + frames_read].axis2(i);
72 if(j < readable_new_subframes)
73 nv = new_movie[j + frames_read].axis2(i);
74 if(ov != nv)
75 return false;
78 return true;
82 void movie::set_all_DRDY() throw()
84 pollcounters.set_all_DRDY();
87 std::string movie::rerecord_count() throw(std::bad_alloc)
89 return rerecords;
92 void movie::rerecord_count(const std::string& count) throw(std::bad_alloc)
94 rerecords = count;
97 std::string movie::project_id() throw(std::bad_alloc)
99 return _project_id;
102 void movie::project_id(const std::string& id) throw(std::bad_alloc)
104 _project_id = id;
107 bool movie::readonly_mode() throw()
109 return readonly;
112 void movie::set_controls(controller_frame controls) throw()
114 current_controls = controls;
117 uint32_t movie::count_changes(uint64_t first_subframe) throw()
119 return movie_data.subframe_count(first_subframe);
122 controller_frame movie::get_controls() throw()
124 if(!readonly)
125 return current_controls;
126 controller_frame c = movie_data.blank_frame(false);
127 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
128 if(current_frame == 0)
129 return c;
130 //Otherwise find the last valid frame of input.
131 uint32_t changes = count_changes(current_frame_first_subframe);
132 if(!changes)
133 return c; //End of movie.
134 if(pollcounters.get_polls(0, 0, 1)) {
135 //Process reset.
136 c.axis3(0, 0, 1, movie_data[current_frame_first_subframe].axis3(0, 0, 1));
137 c.axis3(0, 0, 2, movie_data[current_frame_first_subframe].axis3(0, 0, 2));
138 c.axis3(0, 0, 3, movie_data[current_frame_first_subframe].axis3(0, 0, 3));
140 for(size_t i = 0; i < movie_data.get_types().indices(); i++) {
141 uint32_t polls = pollcounters.get_polls(i);
142 uint32_t index = (changes > polls) ? polls : changes - 1;
143 c.axis2(i, movie_data[current_frame_first_subframe + index].axis2(i));
145 return c;
148 uint64_t movie::get_current_frame() throw()
150 return current_frame;
153 uint64_t movie::get_lag_frames() throw()
155 return lag_frames;
158 uint64_t movie::get_frame_count() throw()
160 return frames_in_movie;
163 void movie::next_frame() throw(std::bad_alloc)
165 //Adjust lag count. Frame 0 MUST NOT be considered lag.
166 //If we don't have valid pflag handler, use the legacy behaviour.
167 unsigned pflag = pflag_handler ? pflag_handler->get_pflag() : 2;
168 if(current_frame && pflag == 0)
169 lag_frames++;
170 else if(pflag < 2 && pflag_handler)
171 pflag_handler->set_pflag(0);
173 //If all poll counters are zero for all real controls, this frame is lag.
174 bool this_frame_lag = !pollcounters.has_polled();
175 //Oh, frame 0 must not be considered lag.
176 if(current_frame && this_frame_lag) {
177 if(pflag == 2)
178 lag_frames++; //Legacy compat. behaviour.
179 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
180 if(!readonly) {
181 //If in read-write mode, write a dummy record for the frame. Force sync flag.
182 //As index should be movie_data.size(), it is correct afterwards.
183 movie_data.append(current_controls.copy(true));
184 frames_in_movie++;
188 //Reset the poll counters and DRDY flags.
189 pollcounters.clear();
191 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
192 //frame 0 and 0 for frame 1.
193 if(current_frame)
194 current_frame_first_subframe = current_frame_first_subframe +
195 count_changes(current_frame_first_subframe);
196 else
197 current_frame_first_subframe = 0;
198 current_frame++;
201 bool movie::get_DRDY(unsigned port, unsigned controller, unsigned ctrl) throw(std::logic_error)
203 return pollcounters.get_DRDY(port, controller, ctrl);
206 short movie::next_input(unsigned port, unsigned controller, unsigned ctrl) throw(std::bad_alloc, std::logic_error)
208 pollcounters.clear_DRDY(port, controller, ctrl);
210 if(readonly) {
211 //In readonly mode...
212 //If at the end of the movie, return released / neutral (but also record the poll)...
213 if(current_frame_first_subframe >= movie_data.size()) {
214 pollcounters.increment_polls(port, controller, ctrl);
215 return 0;
217 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
218 if(current_frame == 0)
219 return 0;
220 //Otherwise find the last valid frame of input.
221 uint32_t changes = count_changes(current_frame_first_subframe);
222 uint32_t polls = pollcounters.increment_polls(port, controller, ctrl);
223 uint32_t index = (changes > polls) ? polls : changes - 1;
224 //debuglog << "Frame=" << current_frame << " Subframe=" << polls << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe + index](controlindex) << " fetchrow=" << current_frame_first_subframe + index << std::endl << std::flush;
225 return movie_data[current_frame_first_subframe + index].axis3(port, controller, ctrl);
226 } else {
227 //Readwrite mode.
228 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
229 //Also, frame 0 must not be added to movie file.
230 if(current_frame == 0)
231 return 0;
232 //If at movie end, insert complete input with frame sync set (this is the first subframe).
233 if(current_frame_first_subframe >= movie_data.size()) {
234 movie_data.append(current_controls.copy(true));
235 //current_frame_first_subframe should be movie_data.size(), so it is right.
236 pollcounters.increment_polls(port, controller, ctrl);
237 frames_in_movie++;
238 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe](controlindex) << " fetchrow=" << current_frame_first_subframe << std::endl << std::flush;
239 return movie_data[current_frame_first_subframe].axis3(port, controller, ctrl);
241 short new_value = current_controls.axis3(port, controller, ctrl);
242 //Fortunately, we know this frame is the last one in movie_data.
243 uint32_t pollcounter = pollcounters.get_polls(port, controller, ctrl);
244 uint64_t fetchrow = movie_data.size() - 1;
245 if(current_frame_first_subframe + pollcounter < movie_data.size()) {
246 //The index is within existing size. Change the value and propagate to all subsequent
247 //subframes.
248 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++)
249 movie_data[i].axis3(port, controller, ctrl, new_value);
250 fetchrow = current_frame_first_subframe + pollcounter;
251 } else if(new_value != movie_data[movie_data.size() - 1].axis3(port, controller, ctrl)) {
252 //The index is not within existing size and value does not match. We need to create a new
253 //subframes(s), copying the last subframe.
254 while(current_frame_first_subframe + pollcounter >= movie_data.size())
255 movie_data.append(movie_data[movie_data.size() - 1].copy(false));
256 fetchrow = current_frame_first_subframe + pollcounter;
257 movie_data[current_frame_first_subframe + pollcounter].axis3(port, controller, ctrl,
258 new_value);
260 pollcounters.increment_polls(port, controller, ctrl);
261 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
262 return new_value;
266 movie::movie() throw(std::bad_alloc)
268 readonly = false;
269 rerecords = "0";
270 _project_id = "";
271 current_frame = 0;
272 frames_in_movie = 0;
273 current_frame_first_subframe = 0;
274 lag_frames = 0;
275 pflag_handler = NULL;
276 clear_caches();
279 void movie::load(const std::string& rerecs, const std::string& project_id, controller_frame_vector& input)
280 throw(std::bad_alloc, std::runtime_error)
282 if(input.size() > 0 && !input[0].sync())
283 throw std::runtime_error("First subframe MUST have frame sync flag set");
284 clear_caches();
285 frames_in_movie = 0;
286 for(size_t i = 0; i < input.size(); i++)
287 if(input[i].sync())
288 frames_in_movie++;
289 readonly = true;
290 rerecords = rerecs;
291 _project_id = project_id;
292 current_frame = 0;
293 current_frame_first_subframe = 0;
294 pollcounters = pollcounter_vector(input.get_types());
295 lag_frames = 0;
296 movie_data = input;
297 //This is to force internal type of current_controls to become correct.
298 current_controls = input.blank_frame(false);
301 controller_frame_vector movie::save() throw(std::bad_alloc)
303 return movie_data;
306 void movie::commit_reset(long delay) throw(std::bad_alloc)
308 if(readonly || delay < 0)
309 return;
310 //If this frame is lagged, we need to write entry for it.
311 if(!pollcounters.has_polled()) {
312 movie_data.append(current_controls.copy(true));
313 frames_in_movie++;
314 //Current_frame_first_subframe is correct.
316 //Also set poll counters on reset cycles to avoid special cases elsewhere.
317 pollcounters.increment_polls(0, 0, 1);
318 //Current frame is always last in rw mode.
319 movie_data[current_frame_first_subframe].axis3(0, 0, 1, 1);
320 movie_data[current_frame_first_subframe].axis3(0, 0, 2, delay / 10000);
321 movie_data[current_frame_first_subframe].axis3(0, 0, 3, delay % 10000);
324 unsigned movie::next_poll_number()
326 return pollcounters.max_polls() + 1;
329 void movie::readonly_mode(bool enable) throw(std::bad_alloc)
331 bool was_in_readonly = readonly;
332 readonly = enable;
333 if(was_in_readonly && !readonly) {
334 clear_caches();
335 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
336 if(current_frame == 0) {
337 //WTF... At before first frame. Blank the entiere movie.
338 frames_in_movie = 0;
339 movie_data.clear();
340 return;
342 //Fun special case: Current frame is not in movie (current_frame_first_subframe >= movie_data.size()).
343 //In this case, we have to extend the movie data.
344 if(current_frame_first_subframe >= movie_data.size()) {
345 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
346 while(frames_in_movie < current_frame) {
347 movie_data.append(movie_data.blank_frame(true));
348 frames_in_movie++;
350 current_frame_first_subframe = movie_data.size() - 1;
353 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
354 //forward values with smaller poll counters.
355 uint64_t next_frame_first_subframe = current_frame_first_subframe +
356 count_changes(current_frame_first_subframe);
357 uint64_t max_readable_subframes = current_frame_first_subframe + pollcounters.max_polls();
358 if(max_readable_subframes > next_frame_first_subframe)
359 max_readable_subframes = next_frame_first_subframe;
361 movie_data.resize(max_readable_subframes);
362 next_frame_first_subframe = max_readable_subframes;
363 //Propagate RESET. This always has poll count of 0 or 1, which always behaves like 1.
364 for(uint64_t j = current_frame_first_subframe + 1; j < next_frame_first_subframe; j++) {
365 movie_data[j].axis3(0, 0, 1, movie_data[current_frame_first_subframe].axis3(0, 0, 1));
366 movie_data[j].axis3(0, 0, 2, movie_data[current_frame_first_subframe].axis3(0, 0, 2));
367 movie_data[j].axis3(0, 0, 3, movie_data[current_frame_first_subframe].axis3(0, 0, 3));
369 //Then the other buttons.
370 for(size_t i = 4; i < movie_data.get_types().indices(); i++) {
371 uint32_t polls = pollcounters.get_polls(i);
372 polls = polls ? polls : 1;
373 for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
374 movie_data[j].axis2(i, movie_data[current_frame_first_subframe + polls - 1].axis2(i));
376 frames_in_movie = current_frame - ((current_frame_first_subframe >= movie_data.size()) ? 1 : 0);
380 //Save state of movie code.
381 void movie::save_state(std::string& proj_id, uint64_t& curframe, uint64_t& lagframes, std::vector<uint32_t>& pcounters)
382 throw(std::bad_alloc)
384 pollcounters.save_state(pcounters);
385 proj_id = _project_id;
386 curframe = current_frame;
387 lagframes = lag_frames;
390 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
391 size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
392 controller_frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
393 std::runtime_error)
395 if(!pollcounters.check(pcounters))
396 throw std::runtime_error("Wrong number of poll counters");
397 if(old_movie && !movies_compatible(*old_movie, movie_data, curframe, &pcounters[0], old_projectid,
398 _project_id))
399 throw std::runtime_error("Save is not from this movie");
400 uint64_t tmp_firstsubframe = 0;
401 for(uint64_t i = 1; i < curframe; i++)
402 tmp_firstsubframe = tmp_firstsubframe + count_changes(tmp_firstsubframe);
403 //Checks have passed, copy the data.
404 readonly = true;
405 current_frame = curframe;
406 current_frame_first_subframe = tmp_firstsubframe;
407 lag_frames = lagframe;
408 pollcounters.load_state(pcounters);
409 readonly_mode(ro);
410 return 0;
413 long movie::get_reset_status() throw()
415 if(current_frame == 0 || current_frame_first_subframe >= movie_data.size())
416 return -1; //No resets out of range.
417 if(!movie_data[current_frame_first_subframe].axis3(0, 0, 1))
418 return -1; //Not a reset.
419 //Also set poll counters on reset cycles to avoid special cases elsewhere.
420 pollcounters.increment_polls(0, 0, 1);
421 long hi = movie_data[current_frame_first_subframe].axis3(0, 0, 2);
422 long lo = movie_data[current_frame_first_subframe].axis3(0, 0, 3);
423 return hi * 10000 + lo;
426 uint64_t movie::frame_subframes(uint64_t frame) throw()
428 if(frame < cached_frame)
429 clear_caches();
430 uint64_t p = cached_subframe;
431 for(uint64_t i = cached_frame; i < frame; i++)
432 p = p + count_changes(p);
433 cached_frame = frame;
434 cached_subframe = p;
435 return count_changes(p);
438 void movie::clear_caches() throw()
440 cached_frame = 1;
441 cached_subframe = 0;
444 controller_frame movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
446 if(frame < cached_frame)
447 clear_caches();
448 uint64_t p = cached_subframe;
449 for(uint64_t i = cached_frame; i < frame; i++)
450 p = p + count_changes(p);
451 cached_frame = frame;
452 cached_subframe = p;
453 uint64_t max = count_changes(p);
454 if(!max) {
455 return movie_data.blank_frame(true);
457 if(max <= subframe)
458 subframe = max - 1;
459 return movie_data[p + subframe];
462 void movie::reset_state() throw()
464 readonly = true;
465 current_frame = 0;
466 current_frame_first_subframe = 0;
467 pollcounters.clear();
468 lag_frames = 0;
469 clear_caches();
472 void movie::fast_save(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
474 pollcounters.save_state(_counters);
475 _frame = current_frame;
476 _ptr = current_frame_first_subframe;
477 _lagc = lag_frames;
480 void movie::fast_load(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
482 readonly = true;
483 current_frame = _frame;
484 current_frame_first_subframe = (_ptr <= movie_data.size()) ? _ptr : movie_data.size();
485 lag_frames = _lagc;
486 pollcounters.load_state(_counters);
487 readonly_mode(false);
490 void movie::set_pflag_handler(poll_flag* handler)
492 pflag_handler = handler;
495 movie::poll_flag::~poll_flag()