Clean up some header files
[lsnes.git] / movie.cpp
blob779de2cf33882ec08549d39dede0e31ae2456179
1 #include "lsnes.hpp"
2 #include "movie.hpp"
3 #include "rom.hpp"
4 #include "misc.hpp"
5 #include <stdexcept>
6 #include <cassert>
7 #include <cstring>
8 #include <fstream>
10 #define FLAG_SYNC CONTROL_FRAME_SYNC
12 std::string global_rerecord_count = "0";
13 //std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
15 namespace
17 void hash_string(uint8_t* res, const std::string& s) throw(std::bad_alloc)
19 std::vector<char> t;
20 t.resize(s.length());
21 std::copy(s.begin(), s.end(), t.begin());
22 sha256::hash(res, t);
25 uint8_t* enlarge(std::vector<uint8_t>& v, size_t amount) throw(std::bad_alloc)
27 size_t i = v.size();
28 v.resize(i + amount);
29 return &v[i];
32 inline void write64(uint8_t* buffer, uint64_t value) throw()
34 buffer[0] = value >> 56;
35 buffer[1] = value >> 48;
36 buffer[2] = value >> 40;
37 buffer[3] = value >> 32;
38 buffer[4] = value >> 24;
39 buffer[5] = value >> 16;
40 buffer[6] = value >> 8;
41 buffer[7] = value;
44 inline void write32(uint8_t* buffer, uint32_t value) throw()
46 buffer[0] = value >> 24;
47 buffer[1] = value >> 16;
48 buffer[2] = value >> 8;
49 buffer[3] = value;
52 inline uint32_t read32(const uint8_t* buffer) throw()
54 return (static_cast<uint32_t>(buffer[0]) << 24) |
55 (static_cast<uint32_t>(buffer[1]) << 16) |
56 (static_cast<uint32_t>(buffer[2]) << 8) |
57 (static_cast<uint32_t>(buffer[3]));
60 inline uint64_t read64(const uint8_t* buffer) throw()
62 return (static_cast<uint64_t>(buffer[0]) << 56) |
63 (static_cast<uint64_t>(buffer[1]) << 48) |
64 (static_cast<uint64_t>(buffer[2]) << 40) |
65 (static_cast<uint64_t>(buffer[3]) << 32) |
66 (static_cast<uint64_t>(buffer[4]) << 24) |
67 (static_cast<uint64_t>(buffer[5]) << 16) |
68 (static_cast<uint64_t>(buffer[6]) << 8) |
69 (static_cast<uint64_t>(buffer[7]));
72 inline void write16s(uint8_t* buffer, int16_t x) throw()
74 uint16_t y = static_cast<uint16_t>(x);
75 buffer[0] = y >> 8;
76 buffer[1] = y;
79 void hash_subframe(sha256& ctx, const controls_t& ctrl) throw()
81 uint8_t buf[2 * TOTAL_CONTROLS];
82 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
83 write16s(buf + 2 * i, ctrl(i));
84 ctx.write(buf, 2 * TOTAL_CONTROLS);
87 //Hashes frame and returns starting subframe of next frame.
88 uint64_t hash_frame(sha256& ctx, std::vector<controls_t>& input, uint64_t first_subframe,
89 uint32_t bound) throw()
91 if(!bound) {
92 //Ignore this frame completely.
93 if(first_subframe >= input.size())
94 return first_subframe;
95 first_subframe++;
96 while(first_subframe < input.size() && !input[first_subframe](CONTROL_FRAME_SYNC))
97 first_subframe++;
98 return first_subframe;
100 if(first_subframe >= input.size()) {
101 //Hash an empty frame.
102 hash_subframe(ctx, controls_t(true));
103 return first_subframe;
106 uint64_t subframes_to_hash = 1;
107 uint64_t last_differing = 1;
108 uint64_t next;
109 controls_t prev = input[first_subframe];
110 prev(CONTROL_FRAME_SYNC) = 0;
112 while(first_subframe + subframes_to_hash < input.size() && !input[first_subframe + subframes_to_hash]
113 (CONTROL_FRAME_SYNC)) {
114 if(!(input[first_subframe + subframes_to_hash] == prev))
115 last_differing = subframes_to_hash + 1;
116 prev = input[first_subframe + subframes_to_hash];
117 subframes_to_hash++;
119 next = first_subframe + subframes_to_hash;
120 subframes_to_hash = last_differing;
121 for(uint64_t i = 0; i < subframes_to_hash && i < bound; i++)
122 hash_subframe(ctx, input[first_subframe + i]);
123 return next;
126 void hash_movie(uint8_t* res, uint64_t current_frame, uint32_t* pollcounters,
127 std::vector<controls_t>& input) throw(std::bad_alloc)
129 sha256 ctx;
130 //If current_frame == 0, hash is empty.
131 if(!current_frame) {
132 ctx.read(res);
133 return;
135 //Hash past frames.
136 uint64_t current_subframe = 0;
137 for(uint64_t i = 1; i < current_frame; i++)
138 current_subframe = hash_frame(ctx, input, current_subframe, 0x7FFFFFFF);
139 //Current frame is special.
140 for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
141 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
142 uint32_t last_seen = 0;
143 for(size_t j = 0; j < polls; j++) {
144 if(current_subframe + j < input.size() && !input[current_subframe + j]
145 (CONTROL_FRAME_SYNC))
146 last_seen = input[current_subframe + j](i);
147 uint8_t buf[2];
148 write16s(buf, last_seen);
149 ctx.write(buf, 2);
152 ctx.read(res);
156 void movie::set_all_DRDY() throw()
158 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
159 pollcounters[i] |= 0x80000000UL;
162 std::string movie::rerecord_count() throw(std::bad_alloc)
164 return rerecords;
167 void movie::rerecord_count(const std::string& count) throw(std::bad_alloc)
169 rerecords = count;
172 std::string movie::project_id() throw(std::bad_alloc)
174 return _project_id;
177 void movie::project_id(const std::string& id) throw(std::bad_alloc)
179 _project_id = id;
182 bool movie::readonly_mode() throw()
184 return readonly;
187 short movie::next_input(unsigned port, unsigned controller, unsigned index) throw(std::bad_alloc, std::logic_error)
189 return next_input(ccindex2(port, controller, index));
192 void movie::set_controls(controls_t controls) throw()
194 current_controls = controls;
197 uint32_t movie::count_changes(uint64_t first_subframe)
199 if(first_subframe >= movie_data.size())
200 return 0;
201 uint32_t ret = 1;
202 while(first_subframe + ret < movie_data.size() && !movie_data[first_subframe + ret](CONTROL_FRAME_SYNC))
203 ret++;
204 return ret;
207 controls_t movie::get_controls() throw()
209 if(!readonly)
210 return current_controls;
211 controls_t c;
212 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
213 if(current_frame == 0)
214 return c;
215 //Otherwise find the last valid frame of input.
216 uint32_t changes = count_changes(current_frame_first_subframe);
217 if(!changes)
218 return c; //End of movie.
219 for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
220 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
221 uint32_t index = (changes > polls) ? polls : changes - 1;
222 c(i) = movie_data[current_frame_first_subframe + index](i);
224 return c;
227 uint64_t movie::get_current_frame() throw()
229 return current_frame;
232 uint64_t movie::get_lag_frames() throw()
234 return lag_frames;
237 uint64_t movie::get_frame_count() throw()
239 return frames_in_movie;
242 void movie::next_frame() throw(std::bad_alloc)
244 //If all poll counters are zero for all real controls, this frame is lag.
245 bool this_frame_lag = true;
246 for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
247 if(pollcounters[i] & 0x7FFFFFFF)
248 this_frame_lag = false;
249 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
250 if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
251 this_frame_lag = false;
252 //Oh, frame 0 must not be considered lag.
253 if(current_frame && this_frame_lag) {
254 lag_frames++;
255 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
256 if(!readonly) {
257 //If in read-write mode, write a dummy record for the frame. Force sync flag.
258 //As index should be movie_data.size(), it is correct afterwards.
259 controls_t c = current_controls;
260 c(CONTROL_FRAME_SYNC) = 1;
261 movie_data.push_back(c);
262 frames_in_movie++;
266 //Reset the poll counters and DRDY flags.
267 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
268 pollcounters[i] = 0;
270 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
271 //frame 0 and 0 for frame 1.
272 if(current_frame)
273 current_frame_first_subframe = current_frame_first_subframe +
274 count_changes(current_frame_first_subframe);
275 else
276 current_frame_first_subframe = 0;
277 current_frame++;
280 bool movie::get_DRDY(unsigned controlindex) throw(std::logic_error)
282 if(controlindex >= TOTAL_CONTROLS)
283 throw std::logic_error("movie::get_DRDY: Bad index");
284 return ((pollcounters[controlindex] & 0x80000000UL) != 0);
287 bool movie::get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error)
289 return get_DRDY(ccindex2(port, controller, index));
292 short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error)
294 //Check validity of index.
295 if(controlindex == FLAG_SYNC)
296 return 0;
297 if(controlindex >= TOTAL_CONTROLS)
298 throw std::logic_error("movie::next_input: Invalid control index");
300 //Clear the DRDY flag.
301 pollcounters[controlindex] &= 0x7FFFFFFF;
303 if(readonly) {
304 //In readonly mode...
305 //If at the end of the movie, return released / neutral (but also record the poll)...
306 if(current_frame_first_subframe >= movie_data.size()) {
307 pollcounters[controlindex]++;
308 return 0;
310 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
311 if(current_frame == 0)
312 return 0;
313 //Otherwise find the last valid frame of input.
314 uint32_t changes = count_changes(current_frame_first_subframe);
315 uint32_t polls = (pollcounters[controlindex]++) & 0x7FFFFFFF;
316 uint32_t index = (changes > polls) ? polls : changes - 1;
317 //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;
318 return movie_data[current_frame_first_subframe + index](controlindex);
319 } else {
320 //Readwrite mode.
321 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
322 //Also, frame 0 must not be added to movie file.
323 if(current_frame == 0)
324 return 0;
325 //If at movie end, insert complete input with frame sync set (this is the first subframe).
326 if(current_frame_first_subframe >= movie_data.size()) {
327 controls_t c = current_controls;
328 c(CONTROL_FRAME_SYNC) = 1;
329 movie_data.push_back(c);
330 //current_frame_first_subframe should be movie_data.size(), so it is right.
331 pollcounters[controlindex]++;
332 frames_in_movie++;
333 assert(pollcounters[controlindex] == 1);
334 //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;
335 return movie_data[current_frame_first_subframe](controlindex);
337 short new_value = current_controls(controlindex);
338 //Fortunately, we know this frame is the last one in movie_data.
339 uint32_t pollcounter = pollcounters[controlindex] & 0x7FFFFFFF;
340 uint64_t fetchrow = movie_data.size() - 1;
341 if(current_frame_first_subframe + pollcounter < movie_data.size()) {
342 //The index is within existing size. Change the value and propagate to all subsequent
343 //subframes.
344 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++)
345 movie_data[i](controlindex) = new_value;
346 fetchrow = current_frame_first_subframe + pollcounter;
347 } else if(new_value != movie_data[movie_data.size() - 1](controlindex)) {
348 //The index is not within existing size and value does not match. We need to create a new
349 //subframes(s), copying the last subframe.
350 while(current_frame_first_subframe + pollcounter >= movie_data.size()) {
351 controls_t c = movie_data[movie_data.size() - 1];
352 c(CONTROL_FRAME_SYNC) = 0;
353 movie_data.push_back(c);
355 fetchrow = current_frame_first_subframe + pollcounter;
356 movie_data[current_frame_first_subframe + pollcounter](controlindex) = new_value;
358 pollcounters[controlindex]++;
359 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
360 return new_value;
364 movie::movie() throw(std::bad_alloc)
366 readonly = false;
367 rerecords = "0";
368 _project_id = "";
369 current_frame = 0;
370 frames_in_movie = 0;
371 current_frame_first_subframe = 0;
372 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
373 pollcounters[i] = 0;
374 current_controls(i) = 0;
376 lag_frames = 0;
377 clear_caches();
380 void movie::load(const std::string& rerecs, const std::string& project_id, const std::vector<controls_t>& input)
381 throw(std::bad_alloc, std::runtime_error)
383 if(input.size() > 0 && !input[0](CONTROL_FRAME_SYNC))
384 throw std::runtime_error("First subframe MUST have frame sync flag set");
385 clear_caches();
386 frames_in_movie = 0;
387 for(auto i = input.begin(); i != input.end(); i++)
388 if((*i)(CONTROL_FRAME_SYNC))
389 frames_in_movie++;
390 readonly = true;
391 rerecords = rerecs;
392 _project_id = project_id;
393 current_frame = 0;
394 current_frame_first_subframe = 0;
395 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
396 pollcounters[i] = 0;
398 lag_frames = 0;
399 movie_data = input;
402 std::vector<controls_t> movie::save() throw(std::bad_alloc)
404 return movie_data;
407 void movie::commit_reset(long delay) throw(std::bad_alloc)
409 if(readonly || delay < 0)
410 return;
411 //If this frame is lagged, we need to write entry for it.
412 bool this_frame_lag = true;
413 for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
414 if(pollcounters[i] & 0x7FFFFFFF)
415 this_frame_lag = false;
416 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
417 if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
418 this_frame_lag = false;
419 if(this_frame_lag) {
420 controls_t c = current_controls;
421 c(CONTROL_FRAME_SYNC) = 1;
422 movie_data.push_back(c);
423 frames_in_movie++;
424 //Current_frame_first_subframe is correct.
426 //Also set poll counters on reset cycles to avoid special cases elsewhere.
427 pollcounters[CONTROL_SYSTEM_RESET] = 1;
428 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
429 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
430 //Current frame is always last in rw mode.
431 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET) = 1;
432 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
433 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
436 unsigned movie::next_poll_number()
438 unsigned max = 0;
439 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
440 if(max < (pollcounters[i] & 0x7FFFFFFF))
441 max = (pollcounters[i] & 0x7FFFFFFF);
442 return max + 1;
445 void movie::readonly_mode(bool enable) throw(std::bad_alloc)
447 bool was_in_readonly = readonly;
448 readonly = enable;
449 if(was_in_readonly && !readonly) {
450 clear_caches();
451 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
452 if(current_frame == 0) {
453 //WTF... At before first frame. Blank the entiere movie.
454 movie_data.clear();
455 return;
457 //Fun special case: Current frame is not in movie (current_frame_first_subframe >= movie_data.size()).
458 //In this case, we have to extend the movie data.
459 if(current_frame_first_subframe >= movie_data.size()) {
460 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
461 while(frames_in_movie < current_frame) {
462 controls_t c(true);
463 movie_data.push_back(c);
464 frames_in_movie++;
466 current_frame_first_subframe = movie_data.size() - 1;
469 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
470 //forward values with smaller poll counters.
471 uint64_t next_frame_first_subframe = current_frame_first_subframe +
472 count_changes(current_frame_first_subframe);
473 uint64_t max_readable_subframes = current_frame_first_subframe;
474 for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
475 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
476 if(current_frame_first_subframe + polls >= next_frame_first_subframe)
477 max_readable_subframes = next_frame_first_subframe;
478 else if(current_frame_first_subframe + polls > max_readable_subframes)
479 max_readable_subframes = current_frame_first_subframe + polls;
481 movie_data.resize(max_readable_subframes);
482 next_frame_first_subframe = max_readable_subframes;
483 for(size_t i = 1; i < TOTAL_CONTROLS; i++) {
484 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
485 if(!polls)
486 polls = 1;
487 for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
488 movie_data[j](i) = movie_data[current_frame_first_subframe + polls - 1](i);
490 frames_in_movie = current_frame - ((current_frame_first_subframe >= movie_data.size()) ? 1 : 0);
494 //Save state of movie code.
495 std::vector<uint8_t> movie::save_state() throw(std::bad_alloc)
497 //debuglog << "--------------------------------------------" << std::endl;
498 //debuglog << "SAVING STATE:" << std::endl;
499 std::vector<uint8_t> ret;
500 hash_string(enlarge(ret, 32), _project_id);
501 write64(enlarge(ret, 8), current_frame);
502 //debuglog << "Current frame is " << current_frame << std::endl;
503 //debuglog << "Poll counters: ";
504 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
505 uint32_t v = pollcounters[i];
506 //debuglog << v;
507 if(v & 0x80000000UL) {
508 //debuglog << "R ";
509 } else
510 ;//debuglog << " ";
511 write32(enlarge(ret, 4), v);
513 //debuglog << std::endl;
515 uint64_t v = lag_frames;
516 //debuglog << "Lag frame count: " << lag_frames << std::endl;
517 write64(enlarge(ret, 8), v);
519 hash_movie(enlarge(ret, 32), current_frame, pollcounters, movie_data);
520 uint8_t hash[32];
521 sha256::hash(hash, ret);
522 memcpy(enlarge(ret, 32), hash, 32);
523 //debuglog << "--------------------------------------------" << std::endl;
524 //debuglog.flush();
525 return ret;
528 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
529 size_t movie::restore_state(const std::vector<uint8_t>& state, bool ro) throw(std::bad_alloc, std::runtime_error)
531 //Check the whole-data checksum.
532 size_t ptr = 0;
533 uint8_t tmp[32];
534 if(state.size() != 112+4*TOTAL_CONTROLS)
535 throw std::runtime_error("Movie save data corrupt: Wrong length");
536 sha256::hash(tmp, &state[0], state.size() - 32);
537 if(memcmp(tmp, &state[state.size() - 32], 32))
538 throw std::runtime_error("Movie save data corrupt: Checksum does not match");
539 //debuglog << "--------------------------------------------" << std::endl;
540 //debuglog << "RESTORING STATE:" << std::endl;
541 //Check project id.
542 hash_string(tmp, _project_id);
543 if(memcmp(tmp, &state[ptr], 32))
544 throw std::runtime_error("Save is not from this movie");
545 ptr += 32;
546 //Read current frame.
547 uint64_t tmp_curframe = read64(&state[ptr]);
548 uint64_t tmp_firstsubframe = 0;
549 for(uint64_t i = 1; i < tmp_curframe; i++)
550 tmp_firstsubframe = tmp_firstsubframe + count_changes(tmp_firstsubframe);
551 ptr += 8;
552 //Read poll counters and drdy flags.
553 uint32_t tmp_pollcount[TOTAL_CONTROLS];
554 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
555 uint32_t v = read32(&state[ptr]);
556 ptr += 4;
557 tmp_pollcount[i] = v;
559 uint64_t tmp_lagframes = read64(&state[ptr]);
560 tmp_lagframes &= 0x7FFFFFFFFFFFFFFFULL;
561 ptr += 8;
562 hash_movie(tmp, tmp_curframe, tmp_pollcount, movie_data);
563 if(memcmp(tmp, &state[ptr], 32))
564 throw std::runtime_error("Save is not from this movie");
566 //Ok, all checks pass. Copy the state. Do this in readonly mode so we can use normal routine to switch
567 //to readwrite mode.
568 readonly = true;
569 current_frame = tmp_curframe;
570 current_frame_first_subframe = tmp_firstsubframe;
571 memcpy(pollcounters, tmp_pollcount, sizeof(tmp_pollcount));
572 lag_frames = tmp_lagframes;
574 //debuglog << "Current frame is " << current_frame << std::endl;
575 //debuglog << "Poll counters: ";
576 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
577 uint32_t v = pollcounters[i];
578 //debuglog << v;
579 if(v & 0x80000000UL) {
580 //debuglog << "R ";
581 } else
582 ;//debuglog << " ";
584 //debuglog << std::endl;
586 //debuglog << "Lag frame count: " << lag_frames << std::endl;
589 //debuglog << "--------------------------------------------" << std::endl;
590 //debuglog.flush();
592 //Move to readwrite mode if needed.
593 readonly_mode(ro);
594 return 0;
597 long movie::get_reset_status() throw()
599 if(current_frame == 0 || current_frame_first_subframe >= movie_data.size())
600 return -1; //No resets out of range.
601 if(!movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET))
602 return -1; //Not a reset.
603 //Also set poll counters on reset cycles to avoid special cases elsewhere.
604 pollcounters[CONTROL_SYSTEM_RESET] = 1;
605 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
606 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
607 long hi = movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI);
608 long lo = movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO);
609 return hi * 10000 + lo;
612 uint64_t movie::frame_subframes(uint64_t frame)
614 if(frame < cached_frame)
615 clear_caches();
616 uint64_t p = cached_subframe;
617 for(uint64_t i = cached_frame; i < frame; i++)
618 p = p + count_changes(p);
619 cached_frame = frame;
620 cached_subframe = p;
621 return count_changes(p);
624 void movie::clear_caches()
626 cached_frame = 1;
627 cached_subframe = 0;
630 controls_t movie::read_subframe(uint64_t frame, uint64_t subframe)
632 if(frame < cached_frame)
633 clear_caches();
634 uint64_t p = cached_subframe;
635 for(uint64_t i = cached_frame; i < frame; i++)
636 p = p + count_changes(p);
637 cached_frame = frame;
638 cached_subframe = p;
639 uint64_t max = count_changes(p);
640 if(!max)
641 return controls_t(true);
642 if(max <= subframe)
643 subframe = max - 1;
644 return movie_data[p + subframe];
648 movie_logic::movie_logic(movie& m) throw()
649 : mov(m)
653 movie_logic::~movie_logic() throw()
657 movie& movie_logic::get_movie() throw()
659 return mov;
662 long movie_logic::new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::runtime_error)
664 mov.next_frame();
665 controls_t c = update_controls(false);
666 if(!mov.readonly_mode()) {
667 mov.set_controls(c);
668 if(dont_poll)
669 mov.set_all_DRDY();
670 if(c(CONTROL_SYSTEM_RESET)) {
671 long hi = c(CONTROL_SYSTEM_RESET_CYCLES_HI);
672 long lo = c(CONTROL_SYSTEM_RESET_CYCLES_LO);
673 mov.commit_reset(hi * 10000 + lo);
676 return mov.get_reset_status();
679 short movie_logic::input_poll(bool port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error)
681 if(dev >= MAX_CONTROLLERS_PER_PORT || id >= CONTROLLER_CONTROLS)
682 return 0;
683 if(!mov.get_DRDY(port ? 1 : 0, dev, id)) {
684 mov.set_controls(update_controls(true));
685 mov.set_all_DRDY();
687 int16_t in = mov.next_input(port ? 1 : 0, dev, id);
688 //debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame()
689 // << ") giving " << in << std::endl;
690 //debuglog.flush();
691 return in;