Move the wxwidgets stuff to one directory
[lsnes.git] / generic / movie.cpp
blob57e2ec4a2aa7e1be6c42e5344a9fef70cea2440f
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::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
14 namespace
16 void hash_string(uint8_t* res, const std::string& s) throw(std::bad_alloc)
18 std::vector<char> t;
19 t.resize(s.length());
20 std::copy(s.begin(), s.end(), t.begin());
21 sha256::hash(res, t);
24 uint8_t* enlarge(std::vector<uint8_t>& v, size_t amount) throw(std::bad_alloc)
26 size_t i = v.size();
27 v.resize(i + amount);
28 return &v[i];
31 inline void write64(uint8_t* buffer, uint64_t value) throw()
33 buffer[0] = value >> 56;
34 buffer[1] = value >> 48;
35 buffer[2] = value >> 40;
36 buffer[3] = value >> 32;
37 buffer[4] = value >> 24;
38 buffer[5] = value >> 16;
39 buffer[6] = value >> 8;
40 buffer[7] = value;
43 inline void write32(uint8_t* buffer, uint32_t value) throw()
45 buffer[0] = value >> 24;
46 buffer[1] = value >> 16;
47 buffer[2] = value >> 8;
48 buffer[3] = value;
51 inline uint32_t read32(const uint8_t* buffer) throw()
53 return (static_cast<uint32_t>(buffer[0]) << 24) |
54 (static_cast<uint32_t>(buffer[1]) << 16) |
55 (static_cast<uint32_t>(buffer[2]) << 8) |
56 (static_cast<uint32_t>(buffer[3]));
59 inline uint64_t read64(const uint8_t* buffer) throw()
61 return (static_cast<uint64_t>(buffer[0]) << 56) |
62 (static_cast<uint64_t>(buffer[1]) << 48) |
63 (static_cast<uint64_t>(buffer[2]) << 40) |
64 (static_cast<uint64_t>(buffer[3]) << 32) |
65 (static_cast<uint64_t>(buffer[4]) << 24) |
66 (static_cast<uint64_t>(buffer[5]) << 16) |
67 (static_cast<uint64_t>(buffer[6]) << 8) |
68 (static_cast<uint64_t>(buffer[7]));
71 inline void write16s(uint8_t* buffer, int16_t x) throw()
73 uint16_t y = static_cast<uint16_t>(x);
74 buffer[0] = y >> 8;
75 buffer[1] = y;
78 void hash_subframe(sha256& ctx, const controls_t& ctrl) throw()
80 uint8_t buf[2 * TOTAL_CONTROLS];
81 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
82 write16s(buf + 2 * i, ctrl(i));
83 ctx.write(buf, 2 * TOTAL_CONTROLS);
86 //Hashes frame and returns starting subframe of next frame.
87 uint64_t hash_frame(sha256& ctx, std::vector<controls_t>& input, uint64_t first_subframe,
88 uint32_t bound) throw()
90 if(!bound) {
91 //Ignore this frame completely.
92 if(first_subframe >= input.size())
93 return first_subframe;
94 first_subframe++;
95 while(first_subframe < input.size() && !input[first_subframe](CONTROL_FRAME_SYNC))
96 first_subframe++;
97 return first_subframe;
99 if(first_subframe >= input.size()) {
100 //Hash an empty frame.
101 hash_subframe(ctx, controls_t(true));
102 return first_subframe;
105 uint64_t subframes_to_hash = 1;
106 uint64_t last_differing = 1;
107 uint64_t next;
108 controls_t prev = input[first_subframe];
109 prev(CONTROL_FRAME_SYNC) = 0;
111 while(first_subframe + subframes_to_hash < input.size() && !input[first_subframe + subframes_to_hash]
112 (CONTROL_FRAME_SYNC)) {
113 if(!(input[first_subframe + subframes_to_hash] == prev))
114 last_differing = subframes_to_hash + 1;
115 prev = input[first_subframe + subframes_to_hash];
116 subframes_to_hash++;
118 next = first_subframe + subframes_to_hash;
119 subframes_to_hash = last_differing;
120 for(uint64_t i = 0; i < subframes_to_hash && i < bound; i++)
121 hash_subframe(ctx, input[first_subframe + i]);
122 return next;
125 void hash_movie(uint8_t* res, uint64_t current_frame, uint32_t* pollcounters,
126 std::vector<controls_t>& input) throw(std::bad_alloc)
128 sha256 ctx;
129 //If current_frame == 0, hash is empty.
130 if(!current_frame) {
131 ctx.read(res);
132 return;
134 //Hash past frames.
135 uint64_t current_subframe = 0;
136 for(uint64_t i = 1; i < current_frame; i++)
137 current_subframe = hash_frame(ctx, input, current_subframe, 0x7FFFFFFF);
138 //Current frame is special.
139 for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
140 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
141 uint32_t last_seen = 0;
142 for(size_t j = 0; j < polls; j++) {
143 if(current_subframe + j < input.size() && !input[current_subframe + j]
144 (CONTROL_FRAME_SYNC))
145 last_seen = input[current_subframe + j](i);
146 uint8_t buf[2];
147 write16s(buf, last_seen);
148 ctx.write(buf, 2);
151 ctx.read(res);
155 void movie::set_all_DRDY() throw()
157 for(size_t i = 0; i < TOTAL_CONTROLS; i++)
158 pollcounters[i] |= 0x80000000UL;
161 std::string movie::rerecord_count() throw(std::bad_alloc)
163 return rerecords;
166 void movie::rerecord_count(const std::string& count) throw(std::bad_alloc)
168 rerecords = count;
171 std::string movie::project_id() throw(std::bad_alloc)
173 return _project_id;
176 void movie::project_id(const std::string& id) throw(std::bad_alloc)
178 _project_id = id;
181 bool movie::readonly_mode() throw()
183 return readonly;
186 short movie::next_input(unsigned port, unsigned controller, unsigned index) throw(std::bad_alloc, std::logic_error)
188 return next_input(ccindex2(port, controller, index));
191 void movie::set_controls(controls_t controls) throw()
193 current_controls = controls;
196 uint32_t movie::count_changes(uint64_t first_subframe) throw()
198 if(first_subframe >= movie_data.size())
199 return 0;
200 uint32_t ret = 1;
201 while(first_subframe + ret < movie_data.size() && !movie_data[first_subframe + ret](CONTROL_FRAME_SYNC))
202 ret++;
203 return ret;
206 controls_t movie::get_controls() throw()
208 if(!readonly)
209 return current_controls;
210 controls_t c;
211 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
212 if(current_frame == 0)
213 return c;
214 //Otherwise find the last valid frame of input.
215 uint32_t changes = count_changes(current_frame_first_subframe);
216 if(!changes)
217 return c; //End of movie.
218 for(size_t i = 0; i < TOTAL_CONTROLS; i++) {
219 uint32_t polls = pollcounters[i] & 0x7FFFFFFF;
220 uint32_t index = (changes > polls) ? polls : changes - 1;
221 c(i) = movie_data[current_frame_first_subframe + index](i);
223 return c;
226 uint64_t movie::get_current_frame() throw()
228 return current_frame;
231 uint64_t movie::get_lag_frames() throw()
233 return lag_frames;
236 uint64_t movie::get_frame_count() throw()
238 return frames_in_movie;
241 void movie::next_frame() throw(std::bad_alloc)
243 //If all poll counters are zero for all real controls, this frame is lag.
244 bool this_frame_lag = true;
245 for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
246 if(pollcounters[i] & 0x7FFFFFFF)
247 this_frame_lag = false;
248 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
249 if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
250 this_frame_lag = false;
251 //Oh, frame 0 must not be considered lag.
252 if(current_frame && this_frame_lag) {
253 lag_frames++;
254 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
255 if(!readonly) {
256 //If in read-write mode, write a dummy record for the frame. Force sync flag.
257 //As index should be movie_data.size(), it is correct afterwards.
258 controls_t c = current_controls;
259 c(CONTROL_FRAME_SYNC) = 1;
260 movie_data.push_back(c);
261 frames_in_movie++;
265 //Reset the poll counters and DRDY flags.
266 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
267 pollcounters[i] = 0;
269 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
270 //frame 0 and 0 for frame 1.
271 if(current_frame)
272 current_frame_first_subframe = current_frame_first_subframe +
273 count_changes(current_frame_first_subframe);
274 else
275 current_frame_first_subframe = 0;
276 current_frame++;
279 bool movie::get_DRDY(unsigned controlindex) throw(std::logic_error)
281 if(controlindex >= TOTAL_CONTROLS)
282 throw std::logic_error("movie::get_DRDY: Bad index");
283 return ((pollcounters[controlindex] & 0x80000000UL) != 0);
286 bool movie::get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error)
288 return get_DRDY(ccindex2(port, controller, index));
291 short movie::next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error)
293 //Check validity of index.
294 if(controlindex == FLAG_SYNC)
295 return 0;
296 if(controlindex >= TOTAL_CONTROLS)
297 throw std::logic_error("movie::next_input: Invalid control index");
299 //Clear the DRDY flag.
300 pollcounters[controlindex] &= 0x7FFFFFFF;
302 if(readonly) {
303 //In readonly mode...
304 //If at the end of the movie, return released / neutral (but also record the poll)...
305 if(current_frame_first_subframe >= movie_data.size()) {
306 pollcounters[controlindex]++;
307 return 0;
309 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
310 if(current_frame == 0)
311 return 0;
312 //Otherwise find the last valid frame of input.
313 uint32_t changes = count_changes(current_frame_first_subframe);
314 uint32_t polls = (pollcounters[controlindex]++) & 0x7FFFFFFF;
315 uint32_t index = (changes > polls) ? polls : changes - 1;
316 //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;
317 return movie_data[current_frame_first_subframe + index](controlindex);
318 } else {
319 //Readwrite mode.
320 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
321 //Also, frame 0 must not be added to movie file.
322 if(current_frame == 0)
323 return 0;
324 //If at movie end, insert complete input with frame sync set (this is the first subframe).
325 if(current_frame_first_subframe >= movie_data.size()) {
326 controls_t c = current_controls;
327 c(CONTROL_FRAME_SYNC) = 1;
328 movie_data.push_back(c);
329 //current_frame_first_subframe should be movie_data.size(), so it is right.
330 pollcounters[controlindex]++;
331 frames_in_movie++;
332 assert(pollcounters[controlindex] == 1);
333 //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;
334 return movie_data[current_frame_first_subframe](controlindex);
336 short new_value = current_controls(controlindex);
337 //Fortunately, we know this frame is the last one in movie_data.
338 uint32_t pollcounter = pollcounters[controlindex] & 0x7FFFFFFF;
339 uint64_t fetchrow = movie_data.size() - 1;
340 if(current_frame_first_subframe + pollcounter < movie_data.size()) {
341 //The index is within existing size. Change the value and propagate to all subsequent
342 //subframes.
343 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++)
344 movie_data[i](controlindex) = new_value;
345 fetchrow = current_frame_first_subframe + pollcounter;
346 } else if(new_value != movie_data[movie_data.size() - 1](controlindex)) {
347 //The index is not within existing size and value does not match. We need to create a new
348 //subframes(s), copying the last subframe.
349 while(current_frame_first_subframe + pollcounter >= movie_data.size()) {
350 controls_t c = movie_data[movie_data.size() - 1];
351 c(CONTROL_FRAME_SYNC) = 0;
352 movie_data.push_back(c);
354 fetchrow = current_frame_first_subframe + pollcounter;
355 movie_data[current_frame_first_subframe + pollcounter](controlindex) = new_value;
357 pollcounters[controlindex]++;
358 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
359 return new_value;
363 movie::movie() throw(std::bad_alloc)
365 readonly = false;
366 rerecords = "0";
367 _project_id = "";
368 current_frame = 0;
369 frames_in_movie = 0;
370 current_frame_first_subframe = 0;
371 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
372 pollcounters[i] = 0;
373 current_controls(i) = 0;
375 lag_frames = 0;
376 clear_caches();
379 void movie::load(const std::string& rerecs, const std::string& project_id, const std::vector<controls_t>& input)
380 throw(std::bad_alloc, std::runtime_error)
382 if(input.size() > 0 && !input[0](CONTROL_FRAME_SYNC))
383 throw std::runtime_error("First subframe MUST have frame sync flag set");
384 clear_caches();
385 frames_in_movie = 0;
386 for(auto i : input)
387 if(i(CONTROL_FRAME_SYNC))
388 frames_in_movie++;
389 readonly = true;
390 rerecords = rerecs;
391 _project_id = project_id;
392 current_frame = 0;
393 current_frame_first_subframe = 0;
394 for(unsigned i = 0; i < TOTAL_CONTROLS; i++) {
395 pollcounters[i] = 0;
397 lag_frames = 0;
398 movie_data = input;
401 std::vector<controls_t> movie::save() throw(std::bad_alloc)
403 return movie_data;
406 void movie::commit_reset(long delay) throw(std::bad_alloc)
408 if(readonly || delay < 0)
409 return;
410 //If this frame is lagged, we need to write entry for it.
411 bool this_frame_lag = true;
412 for(size_t i = MAX_SYSTEM_CONTROLS; i < TOTAL_CONTROLS; i++)
413 if(pollcounters[i] & 0x7FFFFFFF)
414 this_frame_lag = false;
415 //Hack: Reset frames must not be considered lagged, so we abuse pollcounter bit for reset to mark those.
416 if(pollcounters[CONTROL_SYSTEM_RESET] & 0x7FFFFFFF)
417 this_frame_lag = false;
418 if(this_frame_lag) {
419 controls_t c = current_controls;
420 c(CONTROL_FRAME_SYNC) = 1;
421 movie_data.push_back(c);
422 frames_in_movie++;
423 //Current_frame_first_subframe is correct.
425 //Also set poll counters on reset cycles to avoid special cases elsewhere.
426 pollcounters[CONTROL_SYSTEM_RESET] = 1;
427 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_HI] = 1;
428 pollcounters[CONTROL_SYSTEM_RESET_CYCLES_LO] = 1;
429 //Current frame is always last in rw mode.
430 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET) = 1;
431 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
432 movie_data[current_frame_first_subframe](CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
435 unsigned movie::next_poll_number()
437 unsigned max = 0;
438 for(unsigned i = 0; i < TOTAL_CONTROLS; i++)
439 if(max < (pollcounters[i] & 0x7FFFFFFF))
440 max = (pollcounters[i] & 0x7FFFFFFF);
441 return max + 1;
444 void movie::readonly_mode(bool enable) throw(std::bad_alloc)
446 bool was_in_readonly = readonly;
447 readonly = enable;
448 if(was_in_readonly && !readonly) {
449 clear_caches();
450 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
451 if(current_frame == 0) {
452 //WTF... At before first frame. Blank the entiere movie.
453 frames_in_movie = 0;
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) throw()
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() throw()
626 cached_frame = 1;
627 cached_subframe = 0;
630 controls_t movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
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() throw()
652 movie& movie_logic::get_movie() throw()
654 return mov;
657 long movie_logic::new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::runtime_error)
659 mov.next_frame();
660 controls_t c = update_controls(false);
661 if(!mov.readonly_mode()) {
662 mov.set_controls(c);
663 if(dont_poll)
664 mov.set_all_DRDY();
665 if(c(CONTROL_SYSTEM_RESET)) {
666 long hi = c(CONTROL_SYSTEM_RESET_CYCLES_HI);
667 long lo = c(CONTROL_SYSTEM_RESET_CYCLES_LO);
668 mov.commit_reset(hi * 10000 + lo);
671 return mov.get_reset_status();
674 short movie_logic::input_poll(bool port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error)
676 if(dev >= MAX_CONTROLLERS_PER_PORT || id >= CONTROLLER_CONTROLS)
677 return 0;
678 if(!mov.get_DRDY(port ? 1 : 0, dev, id)) {
679 mov.set_controls(update_controls(true));
680 mov.set_all_DRDY();
682 int16_t in = mov.next_input(port ? 1 : 0, dev, id);
683 //debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame()
684 // << ") giving " << in << std::endl;
685 //debuglog.flush();
686 return in;