Fix zero luma corner case
[lsnes.git] / src / core / movie.cpp
blobb69b2fd925b255fc6828fe3a3bdb5566a2eb32f6
1 #include "lsnes.hpp"
3 #include "core/misc.hpp"
4 #include "core/movie.hpp"
5 #include "core/rom.hpp"
7 #include <stdexcept>
8 #include <cassert>
9 #include <cstring>
10 #include <fstream>
12 #define FLAG_SYNC CONTROL_FRAME_SYNC
14 //std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
16 namespace
18 void hash_string(uint8_t* res, const std::string& s) throw(std::bad_alloc)
20 std::vector<char> t;
21 t.resize(s.length());
22 std::copy(s.begin(), s.end(), t.begin());
23 sha256::hash(res, t);
26 uint8_t* enlarge(std::vector<uint8_t>& v, size_t amount) throw(std::bad_alloc)
28 size_t i = v.size();
29 v.resize(i + amount);
30 return &v[i];
33 inline void write64(uint8_t* buffer, uint64_t value) throw()
35 buffer[0] = value >> 56;
36 buffer[1] = value >> 48;
37 buffer[2] = value >> 40;
38 buffer[3] = value >> 32;
39 buffer[4] = value >> 24;
40 buffer[5] = value >> 16;
41 buffer[6] = value >> 8;
42 buffer[7] = value;
45 inline void write32(uint8_t* buffer, uint32_t value) throw()
47 buffer[0] = value >> 24;
48 buffer[1] = value >> 16;
49 buffer[2] = value >> 8;
50 buffer[3] = value;
53 inline uint32_t read32(const uint8_t* buffer) throw()
55 return (static_cast<uint32_t>(buffer[0]) << 24) |
56 (static_cast<uint32_t>(buffer[1]) << 16) |
57 (static_cast<uint32_t>(buffer[2]) << 8) |
58 (static_cast<uint32_t>(buffer[3]));
61 inline uint64_t read64(const uint8_t* buffer) throw()
63 return (static_cast<uint64_t>(buffer[0]) << 56) |
64 (static_cast<uint64_t>(buffer[1]) << 48) |
65 (static_cast<uint64_t>(buffer[2]) << 40) |
66 (static_cast<uint64_t>(buffer[3]) << 32) |
67 (static_cast<uint64_t>(buffer[4]) << 24) |
68 (static_cast<uint64_t>(buffer[5]) << 16) |
69 (static_cast<uint64_t>(buffer[6]) << 8) |
70 (static_cast<uint64_t>(buffer[7]));
73 inline void write16s(uint8_t* buffer, int16_t x) throw()
75 uint16_t y = static_cast<uint16_t>(x);
76 buffer[0] = y >> 8;
77 buffer[1] = y;
80 void hash_subframe(sha256& ctx, const controls_t& ctrl) throw()
82 uint8_t buf[2 * TOTAL_CONTROLS];
83 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
84 write16s(buf + 2 * i, ctrl(i));
85 ctx.write(buf, 2 * TOTAL_CONTROLS);
88 //Hashes frame and returns starting subframe of next frame.
89 uint64_t hash_frame(sha256& ctx, std::vector<controls_t>& input, uint64_t first_subframe,
90 uint32_t bound) throw()
92 if(!bound) {
93 //Ignore this frame completely.
94 if(first_subframe >= input.size())
95 return first_subframe;
96 first_subframe++;
97 while(first_subframe < input.size() && !input[first_subframe](CONTROL_FRAME_SYNC))
98 first_subframe++;
99 return first_subframe;
101 if(first_subframe >= input.size()) {
102 //Hash an empty frame.
103 hash_subframe(ctx, controls_t(true));
104 return first_subframe;
107 uint64_t subframes_to_hash = 1;
108 uint64_t last_differing = 1;
109 uint64_t next;
110 controls_t prev = input[first_subframe];
111 prev(CONTROL_FRAME_SYNC) = 0;
113 while(first_subframe + subframes_to_hash < input.size() && !input[first_subframe + subframes_to_hash]
114 (CONTROL_FRAME_SYNC)) {
115 if(!(input[first_subframe + subframes_to_hash] == prev))
116 last_differing = subframes_to_hash + 1;
117 prev = input[first_subframe + subframes_to_hash];
118 subframes_to_hash++;
120 next = first_subframe + subframes_to_hash;
121 subframes_to_hash = last_differing;
122 for(uint64_t i = 0; i < subframes_to_hash && i < bound; i++)
123 hash_subframe(ctx, input[first_subframe + i]);
124 return next;
127 void hash_movie(uint8_t* res, uint64_t current_frame, uint32_t* pollcounters,
128 std::vector<controls_t>& input) throw(std::bad_alloc)
130 sha256 ctx;
131 //If current_frame == 0, hash is empty.
132 if(!current_frame) {
133 ctx.read(res);
134 return;
136 //Hash past frames.
137 uint64_t current_subframe = 0;
138 for(uint64_t i = 1; i < current_frame; i++)
139 current_subframe = hash_frame(ctx, input, current_subframe, 0x7FFFFFFF);
140 //Current frame is special.
141 for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
142 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
143 uint32_t last_seen = 0;
144 for(size_t j = 0; j < polls; j++) {
145 if(current_subframe + j < input.size() && !input[current_subframe + j]
146 (CONTROL_FRAME_SYNC))
147 last_seen = input[current_subframe + j](i);
148 uint8_t buf[2];
149 write16s(buf, last_seen);
150 ctx.write(buf, 2);
153 ctx.read(res);
157 void movie::set_all_DRDY() throw()
159 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
160 pollcounters[i] |= 0x80000000UL;
163 std::string movie::rerecord_count() throw(std::bad_alloc)
165 return rerecords;
168 void movie::rerecord_count(const std::string& count) throw(std::bad_alloc)
170 rerecords = count;
173 std::string movie::project_id() throw(std::bad_alloc)
175 return _project_id;
178 void movie::project_id(const std::string& id) throw(std::bad_alloc)
180 _project_id = id;
183 bool movie::readonly_mode() throw()
185 return readonly;
188 short movie::next_input(unsigned port, unsigned controller, unsigned index) throw(std::bad_alloc, std::logic_error)
190 return next_input(ccindex2(port, controller, index));
193 void movie::set_controls(controls_t controls) throw()
195 current_controls = controls;
198 uint32_t movie::count_changes(uint64_t first_subframe) throw()
200 if(first_subframe >= movie_data.size())
201 return 0;
202 uint32_t ret = 1;
203 while(first_subframe + ret < movie_data.size() && !movie_data[first_subframe + ret](CONTROL_FRAME_SYNC))
204 ret++;
205 return ret;
208 controls_t movie::get_controls() throw()
210 if(!readonly)
211 return current_controls;
212 controls_t c;
213 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
214 if(current_frame == 0)
215 return c;
216 //Otherwise find the last valid frame of input.
217 uint32_t changes = count_changes(current_frame_first_subframe);
218 if(!changes)
219 return c; //End of movie.
220 for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
221 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
222 uint32_t index = (changes > polls) ? polls : changes - 1;
223 c(i) = movie_data[current_frame_first_subframe + index](i);
225 return c;
228 uint64_t movie::get_current_frame() throw()
230 return current_frame;
233 uint64_t movie::get_lag_frames() throw()
235 return lag_frames;
238 uint64_t movie::get_frame_count() throw()
240 return frames_in_movie;
243 void movie::next_frame() throw(std::bad_alloc)
245 //If all poll counters are zero for all real controls, this frame is lag.
246 bool this_frame_lag = true;
247 for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
248 if(pollcounters[i] & 0x7FFFFFFF)
249 this_frame_lag = false;
250 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
251 if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
252 this_frame_lag = false;
253 //Oh, frame 0 must not be considered lag.
254 if(current_frame && this_frame_lag) {
255 lag_frames++;
256 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
257 if(!readonly) {
258 //If in read-write mode, write a dummy record for the frame. Force sync flag.
259 //As index should be movie_data.size(), it is correct afterwards.
260 controls_t c = current_controls;
261 c(CONTROL_FRAME_SYNC) = 1;
262 movie_data.push_back(c);
263 frames_in_movie++;
267 //Reset the poll counters and DRDY flags.
268 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
269 pollcounters[i] = 0;
271 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
272 //frame 0 and 0 for frame 1.
273 if(current_frame)
274 current_frame_first_subframe = current_frame_first_subframe +
275 count_changes(current_frame_first_subframe);
276 else
277 current_frame_first_subframe = 0;
278 current_frame++;
281 bool movie::get_DRDY(unsigned controlindex) throw(std::logic_error)
283 if(controlindex >= TOTAL_CONTROLS)
284 throw std::logic_error("movie::get_DRDY: Bad index");
285 return ((pollcounters[controlindex] & 0x80000000UL) != 0);
288 bool movie::get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error)
290 return get_DRDY(ccindex2(port, controller, index));
293 short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error)
295 //Check validity of index.
296 if(controlindex == FLAG_SYNC)
297 return 0;
298 if(controlindex >= TOTAL_CONTROLS)
299 throw std::logic_error("movie::next_input: Invalid control index");
301 //Clear the DRDY flag.
302 pollcounters[controlindex] &= 0x7FFFFFFF;
304 if(readonly) {
305 //In readonly mode...
306 //If at the end of the movie, return released / neutral (but also record the poll)...
307 if(current_frame_first_subframe >= movie_data.size()) {
308 pollcounters[controlindex]++;
309 return 0;
311 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
312 if(current_frame == 0)
313 return 0;
314 //Otherwise find the last valid frame of input.
315 uint32_t changes = count_changes(current_frame_first_subframe);
316 uint32_t polls = (pollcounters[controlindex]++) & 0x7FFFFFFF;
317 uint32_t index = (changes > polls) ? polls : changes - 1;
318 //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;
319 return movie_data[current_frame_first_subframe + index](controlindex);
320 } else {
321 //Readwrite mode.
322 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
323 //Also, frame 0 must not be added to movie file.
324 if(current_frame == 0)
325 return 0;
326 //If at movie end, insert complete input with frame sync set (this is the first subframe).
327 if(current_frame_first_subframe >= movie_data.size()) {
328 controls_t c = current_controls;
329 c(CONTROL_FRAME_SYNC) = 1;
330 movie_data.push_back(c);
331 //current_frame_first_subframe should be movie_data.size(), so it is right.
332 pollcounters[controlindex]++;
333 frames_in_movie++;
334 assert(pollcounters[controlindex] == 1);
335 //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;
336 return movie_data[current_frame_first_subframe](controlindex);
338 short new_value = current_controls(controlindex);
339 //Fortunately, we know this frame is the last one in movie_data.
340 uint32_t pollcounter = pollcounters[controlindex] & 0x7FFFFFFF;
341 uint64_t fetchrow = movie_data.size() - 1;
342 if(current_frame_first_subframe + pollcounter < movie_data.size()) {
343 //The index is within existing size. Change the value and propagate to all subsequent
344 //subframes.
345 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++)
346 movie_data[i](controlindex) = new_value;
347 fetchrow = current_frame_first_subframe + pollcounter;
348 } else if(new_value != movie_data[movie_data.size() - 1](controlindex)) {
349 //The index is not within existing size and value does not match. We need to create a new
350 //subframes(s), copying the last subframe.
351 while(current_frame_first_subframe + pollcounter >= movie_data.size()) {
352 controls_t c = movie_data[movie_data.size() - 1];
353 c(CONTROL_FRAME_SYNC) = 0;
354 movie_data.push_back(c);
356 fetchrow = current_frame_first_subframe + pollcounter;
357 movie_data[current_frame_first_subframe + pollcounter](controlindex) = new_value;
359 pollcounters[controlindex]++;
360 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
361 return new_value;
365 movie::movie() throw(std::bad_alloc)
367 readonly = false;
368 rerecords = "0";
369 _project_id = "";
370 current_frame = 0;
371 frames_in_movie = 0;
372 current_frame_first_subframe = 0;
373 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
374 pollcounters[i] = 0;
375 current_controls(i) = 0;
377 lag_frames = 0;
378 clear_caches();
381 void movie::load(const std::string& rerecs, const std::string& project_id, const std::vector<controls_t>& input)
382 throw(std::bad_alloc, std::runtime_error)
384 if(input.size() > 0 && !input[0](CONTROL_FRAME_SYNC))
385 throw std::runtime_error("First subframe MUST have frame sync flag set");
386 clear_caches();
387 frames_in_movie = 0;
388 for(auto i : input)
389 if(i(CONTROL_FRAME_SYNC))
390 frames_in_movie++;
391 readonly = true;
392 rerecords = rerecs;
393 _project_id = project_id;
394 current_frame = 0;
395 current_frame_first_subframe = 0;
396 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
397 pollcounters[i] = 0;
399 lag_frames = 0;
400 movie_data = input;
403 std::vector<controls_t> movie::save() throw(std::bad_alloc)
405 return movie_data;
408 void movie::commit_reset(long delay) throw(std::bad_alloc)
410 if(readonly || delay < 0)
411 return;
412 //If this frame is lagged, we need to write entry for it.
413 bool this_frame_lag = true;
414 for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
415 if(pollcounters[i] & 0x7FFFFFFF)
416 this_frame_lag = false;
417 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
418 if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
419 this_frame_lag = false;
420 if(this_frame_lag) {
421 controls_t c = current_controls;
422 c(CONTROL_FRAME_SYNC) = 1;
423 movie_data.push_back(c);
424 frames_in_movie++;
425 //Current_frame_first_subframe is correct.
427 //Also set poll counters on reset cycles to avoid special cases elsewhere.
428 pollcounters[CONTROL_SYSTEM_RESET] = 1;
429 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
430 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
431 //Current frame is always last in rw mode.
432 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET) = 1;
433 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
434 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
437 unsigned movie::next_poll_number()
439 unsigned max = 0;
440 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
441 if(max < (pollcounters[i] & 0x7FFFFFFF))
442 max = (pollcounters[i] & 0x7FFFFFFF);
443 return max + 1;
446 void movie::readonly_mode(bool enable) throw(std::bad_alloc)
448 bool was_in_readonly = readonly;
449 readonly = enable;
450 if(was_in_readonly && !readonly) {
451 clear_caches();
452 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
453 if(current_frame == 0) {
454 //WTF... At before first frame. Blank the entiere movie.
455 frames_in_movie = 0;
456 movie_data.clear();
457 return;
459 //Fun special case: Current frame is not in movie (current_frame_first_subframe >= movie_data.size()).
460 //In this case, we have to extend the movie data.
461 if(current_frame_first_subframe >= movie_data.size()) {
462 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
463 while(frames_in_movie < current_frame) {
464 controls_t c(true);
465 movie_data.push_back(c);
466 frames_in_movie++;
468 current_frame_first_subframe = movie_data.size() - 1;
471 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
472 //forward values with smaller poll counters.
473 uint64_t next_frame_first_subframe = current_frame_first_subframe +
474 count_changes(current_frame_first_subframe);
475 uint64_t max_readable_subframes = current_frame_first_subframe;
476 for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
477 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
478 if(current_frame_first_subframe + polls >= next_frame_first_subframe)
479 max_readable_subframes = next_frame_first_subframe;
480 else if(current_frame_first_subframe + polls > max_readable_subframes)
481 max_readable_subframes = current_frame_first_subframe + polls;
483 movie_data.resize(max_readable_subframes);
484 next_frame_first_subframe = max_readable_subframes;
485 for(size_t i = 1; i < TOTAL_CONTROLS; i++) {
486 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
487 if(!polls)
488 polls = 1;
489 for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
490 movie_data[j](i) = movie_data[current_frame_first_subframe + polls - 1](i);
492 frames_in_movie = current_frame - ((current_frame_first_subframe >= movie_data.size()) ? 1 : 0);
496 //Save state of movie code.
497 std::vector<uint8_t> movie::save_state() throw(std::bad_alloc)
499 //debuglog << "--------------------------------------------" << std::endl;
500 //debuglog << "SAVING STATE:" << std::endl;
501 std::vector<uint8_t> ret;
502 hash_string(enlarge(ret, 32), _project_id);
503 write64(enlarge(ret, 8), current_frame);
504 //debuglog << "Current frame is " << current_frame << std::endl;
505 //debuglog << "Poll counters: ";
506 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
507 uint32_t v = pollcounters[i];
508 //debuglog << v;
509 if(v & 0x80000000UL) {
510 //debuglog << "R ";
511 } else
512 ;//debuglog << " ";
513 write32(enlarge(ret, 4), v);
515 //debuglog << std::endl;
517 uint64_t v = lag_frames;
518 //debuglog << "Lag frame count: " << lag_frames << std::endl;
519 write64(enlarge(ret, 8), v);
521 hash_movie(enlarge(ret, 32), current_frame, pollcounters, movie_data);
522 uint8_t hash[32];
523 sha256::hash(hash, ret);
524 memcpy(enlarge(ret, 32), hash, 32);
525 //debuglog << "--------------------------------------------" << std::endl;
526 //debuglog.flush();
527 return ret;
530 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
531 size_t movie::restore_state(const std::vector<uint8_t>& state, bool ro) throw(std::bad_alloc, std::runtime_error)
533 //Check the whole-data checksum.
534 size_t ptr = 0;
535 uint8_t tmp[32];
536 if(state.size() != 112+4*TOTAL_CONTROLS)
537 throw std::runtime_error("Movie save data corrupt: Wrong length");
538 sha256::hash(tmp, &state[0], state.size() - 32);
539 if(memcmp(tmp, &state[state.size() - 32], 32))
540 throw std::runtime_error("Movie save data corrupt: Checksum does not match");
541 //debuglog << "--------------------------------------------" << std::endl;
542 //debuglog << "RESTORING STATE:" << std::endl;
543 //Check project id.
544 hash_string(tmp, _project_id);
545 if(memcmp(tmp, &state[ptr], 32))
546 throw std::runtime_error("Save is not from this movie");
547 ptr += 32;
548 //Read current frame.
549 uint64_t tmp_curframe = read64(&state[ptr]);
550 uint64_t tmp_firstsubframe = 0;
551 for(uint64_t i = 1; i < tmp_curframe; i++)
552 tmp_firstsubframe = tmp_firstsubframe + count_changes(tmp_firstsubframe);
553 ptr += 8;
554 //Read poll counters and drdy flags.
555 uint32_t tmp_pollcount[TOTAL_CONTROLS];
556 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
557 uint32_t v = read32(&state[ptr]);
558 ptr += 4;
559 tmp_pollcount[i] = v;
561 uint64_t tmp_lagframes = read64(&state[ptr]);
562 tmp_lagframes &= 0x7FFFFFFFFFFFFFFFULL;
563 ptr += 8;
564 hash_movie(tmp, tmp_curframe, tmp_pollcount, movie_data);
565 if(memcmp(tmp, &state[ptr], 32))
566 throw std::runtime_error("Save is not from this movie");
568 //Ok, all checks pass. Copy the state. Do this in readonly mode so we can use normal routine to switch
569 //to readwrite mode.
570 readonly = true;
571 current_frame = tmp_curframe;
572 current_frame_first_subframe = tmp_firstsubframe;
573 memcpy(pollcounters, tmp_pollcount, sizeof(tmp_pollcount));
574 lag_frames = tmp_lagframes;
576 //debuglog << "Current frame is " << current_frame << std::endl;
577 //debuglog << "Poll counters: ";
578 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
579 uint32_t v = pollcounters[i];
580 //debuglog << v;
581 if(v & 0x80000000UL) {
582 //debuglog << "R ";
583 } else
584 ;//debuglog << " ";
586 //debuglog << std::endl;
588 //debuglog << "Lag frame count: " << lag_frames << std::endl;
591 //debuglog << "--------------------------------------------" << std::endl;
592 //debuglog.flush();
594 //Move to readwrite mode if needed.
595 readonly_mode(ro);
596 return 0;
599 long movie::get_reset_status() throw()
601 if(current_frame == 0 || current_frame_first_subframe >= movie_data.size())
602 return -1; //No resets out of range.
603 if(!movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET))
604 return -1; //Not a reset.
605 //Also set poll counters on reset cycles to avoid special cases elsewhere.
606 pollcounters[CONTROL_SYSTEM_RESET] = 1;
607 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
608 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
609 long hi = movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI);
610 long lo = movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO);
611 return hi * 10000 + lo;
614 uint64_t movie::frame_subframes(uint64_t frame) throw()
616 if(frame < cached_frame)
617 clear_caches();
618 uint64_t p = cached_subframe;
619 for(uint64_t i = cached_frame; i < frame; i++)
620 p = p + count_changes(p);
621 cached_frame = frame;
622 cached_subframe = p;
623 return count_changes(p);
626 void movie::clear_caches() throw()
628 cached_frame = 1;
629 cached_subframe = 0;
632 controls_t movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
634 if(frame < cached_frame)
635 clear_caches();
636 uint64_t p = cached_subframe;
637 for(uint64_t i = cached_frame; i < frame; i++)
638 p = p + count_changes(p);
639 cached_frame = frame;
640 cached_subframe = p;
641 uint64_t max = count_changes(p);
642 if(!max)
643 return controls_t(true);
644 if(max <= subframe)
645 subframe = max - 1;
646 return movie_data[p + subframe];
650 movie_logic::movie_logic() throw()
654 movie& movie_logic::get_movie() throw()
656 return mov;
659 long movie_logic::new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::runtime_error)
661 mov.next_frame();
662 controls_t c = update_controls(false);
663 if(!mov.readonly_mode()) {
664 mov.set_controls(c);
665 if(dont_poll)
666 mov.set_all_DRDY();
667 if(c(CONTROL_SYSTEM_RESET)) {
668 long hi = c(CONTROL_SYSTEM_RESET_CYCLES_HI);
669 long lo = c(CONTROL_SYSTEM_RESET_CYCLES_LO);
670 mov.commit_reset(hi * 10000 + lo);
673 return mov.get_reset_status();
676 short movie_logic::input_poll(bool port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error)
678 if(dev >= MAX_CONTROLLERS_PER_PORT || id >= CONTROLLER_CONTROLS)
679 return 0;
680 if(!mov.get_DRDY(port ? 1 : 0, dev, id)) {
681 mov.set_controls(update_controls(true));
682 mov.set_all_DRDY();
684 int16_t in = mov.next_input(port ? 1 : 0, dev, id);
685 //debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame()
686 // << ") giving " << in << std::endl;
687 //debuglog.flush();
688 return in;