3 #include "core/emucore.hpp"
4 #include "core/misc.hpp"
5 #include "core/movie.hpp"
6 #include "core/rom.hpp"
14 //std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
18 uint64_t find_next_sync(controller_frame_vector
& movie
, uint64_t after
)
20 if(after
>= movie
.size())
24 } while(after
< movie
.size() && !movie
[after
].sync());
28 bool movies_compatible(controller_frame_vector
& old_movie
, controller_frame_vector
& new_movie
,
29 uint64_t frame
, const uint32_t* polls
, const std::string
& old_projectid
,
30 const std::string
& new_projectid
)
32 //Project IDs have to match.
33 if(old_projectid
!= new_projectid
)
35 //Types have to match.
36 if(old_movie
.get_types() != new_movie
.get_types())
38 const port_type_set
& pset
= new_movie
.get_types();
39 //If new movie is before first frame, anything with same project_id is compatible.
42 //Scan both movies until frame syncs are seen. Out of bounds reads behave as all neutral but frame
44 uint64_t syncs_seen
= 0;
45 uint64_t frames_read
= 0;
46 while(syncs_seen
< frame
- 1) {
47 controller_frame oldc
= old_movie
.blank_frame(true), newc
= new_movie
.blank_frame(true);
48 if(frames_read
< old_movie
.size())
49 oldc
= old_movie
[frames_read
];
50 if(frames_read
< new_movie
.size())
51 newc
= new_movie
[frames_read
];
53 return false; //Mismatch.
58 //We increment the counter one time too many.
60 //Current frame. We need to compare each control up to poll counter.
61 uint64_t readable_old_subframes
= 0, readable_new_subframes
= 0;
62 uint64_t oldlen
= find_next_sync(old_movie
, frames_read
);
63 uint64_t newlen
= find_next_sync(new_movie
, frames_read
);
64 if(frames_read
< oldlen
)
65 readable_old_subframes
= oldlen
- frames_read
;
66 if(frames_read
< newlen
)
67 readable_new_subframes
= newlen
- frames_read
;
68 //Then rest of the stuff.
69 for(unsigned i
= 0; i
< pset
.indices(); i
++) {
70 uint32_t p
= polls
[i
] & 0x7FFFFFFFUL
;
72 for(uint32_t j
= 0; j
< p
; j
++) {
73 if(j
< readable_old_subframes
)
74 ov
= old_movie
[j
+ frames_read
].axis2(i
);
75 if(j
< readable_new_subframes
)
76 nv
= new_movie
[j
+ frames_read
].axis2(i
);
85 void movie::set_all_DRDY() throw()
87 pollcounters
.set_all_DRDY();
90 std::string
movie::rerecord_count() throw(std::bad_alloc
)
95 void movie::rerecord_count(const std::string
& count
) throw(std::bad_alloc
)
100 std::string
movie::project_id() throw(std::bad_alloc
)
105 void movie::project_id(const std::string
& id
) throw(std::bad_alloc
)
110 bool movie::readonly_mode() throw()
115 void movie::set_controls(controller_frame controls
) throw()
117 current_controls
= controls
;
120 uint32_t movie::count_changes(uint64_t first_subframe
) throw()
122 return movie_data
.subframe_count(first_subframe
);
125 controller_frame
movie::get_controls() throw()
128 return current_controls
;
129 controller_frame c
= movie_data
.blank_frame(false);
130 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
131 if(current_frame
== 0)
133 //Otherwise find the last valid frame of input.
134 uint32_t changes
= count_changes(current_frame_first_subframe
);
136 return c
; //End of movie.
137 if(pollcounters
.get_polls(0, 0, 1)) {
139 c
.axis3(0, 0, 1, movie_data
[current_frame_first_subframe
].axis3(0, 0, 1));
140 c
.axis3(0, 0, 2, movie_data
[current_frame_first_subframe
].axis3(0, 0, 2));
141 c
.axis3(0, 0, 3, movie_data
[current_frame_first_subframe
].axis3(0, 0, 3));
143 for(size_t i
= 0; i
< movie_data
.get_types().indices(); i
++) {
144 uint32_t polls
= pollcounters
.get_polls(i
);
145 uint32_t index
= (changes
> polls
) ? polls
: changes
- 1;
146 c
.axis2(i
, movie_data
[current_frame_first_subframe
+ index
].axis2(i
));
151 uint64_t movie::get_current_frame() throw()
153 return current_frame
;
156 uint64_t movie::get_lag_frames() throw()
161 uint64_t movie::get_frame_count() throw()
163 return frames_in_movie
;
166 void movie::next_frame() throw(std::bad_alloc
)
168 //Adjust lag count. Frame 0 MUST NOT be considered lag.
169 unsigned pflag
= core_get_poll_flag();
170 if(current_frame
&& pflag
== 0)
173 core_set_poll_flag(0);
175 //If all poll counters are zero for all real controls, this frame is lag.
176 bool this_frame_lag
= !pollcounters
.has_polled();
177 //Oh, frame 0 must not be considered lag.
178 if(current_frame
&& this_frame_lag
) {
180 lag_frames
++; //Legacy compat. behaviour.
181 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
183 //If in read-write mode, write a dummy record for the frame. Force sync flag.
184 //As index should be movie_data.size(), it is correct afterwards.
185 movie_data
.append(current_controls
.copy(true));
190 //Reset the poll counters and DRDY flags.
191 pollcounters
.clear();
193 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
194 //frame 0 and 0 for frame 1.
196 current_frame_first_subframe
= current_frame_first_subframe
+
197 count_changes(current_frame_first_subframe
);
199 current_frame_first_subframe
= 0;
203 bool movie::get_DRDY(unsigned port
, unsigned controller
, unsigned ctrl
) throw(std::logic_error
)
205 return pollcounters
.get_DRDY(port
, controller
, ctrl
);
208 short movie::next_input(unsigned port
, unsigned controller
, unsigned ctrl
) throw(std::bad_alloc
, std::logic_error
)
210 pollcounters
.clear_DRDY(port
, controller
, ctrl
);
213 //In readonly mode...
214 //If at the end of the movie, return released / neutral (but also record the poll)...
215 if(current_frame_first_subframe
>= movie_data
.size()) {
216 pollcounters
.increment_polls(port
, controller
, ctrl
);
219 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
220 if(current_frame
== 0)
222 //Otherwise find the last valid frame of input.
223 uint32_t changes
= count_changes(current_frame_first_subframe
);
224 uint32_t polls
= pollcounters
.increment_polls(port
, controller
, ctrl
);
225 uint32_t index
= (changes
> polls
) ? polls
: changes
- 1;
226 //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;
227 return movie_data
[current_frame_first_subframe
+ index
].axis3(port
, controller
, ctrl
);
230 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
231 //Also, frame 0 must not be added to movie file.
232 if(current_frame
== 0)
234 //If at movie end, insert complete input with frame sync set (this is the first subframe).
235 if(current_frame_first_subframe
>= movie_data
.size()) {
236 movie_data
.append(current_controls
.copy(true));
237 //current_frame_first_subframe should be movie_data.size(), so it is right.
238 pollcounters
.increment_polls(port
, controller
, ctrl
);
240 //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;
241 return movie_data
[current_frame_first_subframe
].axis3(port
, controller
, ctrl
);
243 short new_value
= current_controls
.axis3(port
, controller
, ctrl
);
244 //Fortunately, we know this frame is the last one in movie_data.
245 uint32_t pollcounter
= pollcounters
.get_polls(port
, controller
, ctrl
);
246 uint64_t fetchrow
= movie_data
.size() - 1;
247 if(current_frame_first_subframe
+ pollcounter
< movie_data
.size()) {
248 //The index is within existing size. Change the value and propagate to all subsequent
250 for(uint64_t i
= current_frame_first_subframe
+ pollcounter
; i
< movie_data
.size(); i
++)
251 movie_data
[i
].axis3(port
, controller
, ctrl
, new_value
);
252 fetchrow
= current_frame_first_subframe
+ pollcounter
;
253 } else if(new_value
!= movie_data
[movie_data
.size() - 1].axis3(port
, controller
, ctrl
)) {
254 //The index is not within existing size and value does not match. We need to create a new
255 //subframes(s), copying the last subframe.
256 while(current_frame_first_subframe
+ pollcounter
>= movie_data
.size())
257 movie_data
.append(movie_data
[movie_data
.size() - 1].copy(false));
258 fetchrow
= current_frame_first_subframe
+ pollcounter
;
259 movie_data
[current_frame_first_subframe
+ pollcounter
].axis3(port
, controller
, ctrl
,
262 pollcounters
.increment_polls(port
, controller
, ctrl
);
263 //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
268 movie::movie() throw(std::bad_alloc
)
275 current_frame_first_subframe
= 0;
280 void movie::load(const std::string
& rerecs
, const std::string
& project_id
, controller_frame_vector
& input
)
281 throw(std::bad_alloc
, std::runtime_error
)
283 if(input
.size() > 0 && !input
[0].sync())
284 throw std::runtime_error("First subframe MUST have frame sync flag set");
287 for(size_t i
= 0; i
< input
.size(); i
++)
292 _project_id
= project_id
;
294 current_frame_first_subframe
= 0;
295 pollcounters
= pollcounter_vector(input
.get_types());
298 //This is to force internal type of current_controls to become correct.
299 current_controls
= input
.blank_frame(false);
302 controller_frame_vector
movie::save() throw(std::bad_alloc
)
307 void movie::commit_reset(long delay
) throw(std::bad_alloc
)
309 if(readonly
|| delay
< 0)
311 //If this frame is lagged, we need to write entry for it.
312 if(!pollcounters
.has_polled()) {
313 movie_data
.append(current_controls
.copy(true));
315 //Current_frame_first_subframe is correct.
317 //Also set poll counters on reset cycles to avoid special cases elsewhere.
318 pollcounters
.increment_polls(0, 0, 1);
319 //Current frame is always last in rw mode.
320 movie_data
[current_frame_first_subframe
].axis3(0, 0, 1, 1);
321 movie_data
[current_frame_first_subframe
].axis3(0, 0, 2, delay
/ 10000);
322 movie_data
[current_frame_first_subframe
].axis3(0, 0, 3, delay
% 10000);
325 unsigned movie::next_poll_number()
327 return pollcounters
.max_polls() + 1;
330 void movie::readonly_mode(bool enable
) throw(std::bad_alloc
)
332 bool was_in_readonly
= readonly
;
334 if(was_in_readonly
&& !readonly
) {
336 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
337 if(current_frame
== 0) {
338 //WTF... At before first frame. Blank the entiere movie.
343 //Fun special case: Current frame is not in movie (current_frame_first_subframe >= movie_data.size()).
344 //In this case, we have to extend the movie data.
345 if(current_frame_first_subframe
>= movie_data
.size()) {
346 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
347 while(frames_in_movie
< current_frame
) {
348 movie_data
.append(movie_data
.blank_frame(true));
351 current_frame_first_subframe
= movie_data
.size() - 1;
354 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
355 //forward values with smaller poll counters.
356 uint64_t next_frame_first_subframe
= current_frame_first_subframe
+
357 count_changes(current_frame_first_subframe
);
358 uint64_t max_readable_subframes
= current_frame_first_subframe
+ pollcounters
.max_polls();
359 if(max_readable_subframes
> next_frame_first_subframe
)
360 max_readable_subframes
= next_frame_first_subframe
;
362 movie_data
.resize(max_readable_subframes
);
363 next_frame_first_subframe
= max_readable_subframes
;
364 //Propagate RESET. This always has poll count of 0 or 1, which always behaves like 1.
365 for(uint64_t j
= current_frame_first_subframe
+ 1; j
< next_frame_first_subframe
; j
++) {
366 movie_data
[j
].axis3(0, 0, 1, movie_data
[current_frame_first_subframe
].axis3(0, 0, 1));
367 movie_data
[j
].axis3(0, 0, 2, movie_data
[current_frame_first_subframe
].axis3(0, 0, 2));
368 movie_data
[j
].axis3(0, 0, 3, movie_data
[current_frame_first_subframe
].axis3(0, 0, 3));
370 //Then the other buttons.
371 for(size_t i
= 4; i
< movie_data
.get_types().indices(); i
++) {
372 uint32_t polls
= pollcounters
.get_polls(i
);
373 polls
= polls
? polls
: 1;
374 for(uint64_t j
= current_frame_first_subframe
+ polls
; j
< next_frame_first_subframe
; j
++)
375 movie_data
[j
].axis2(i
, movie_data
[current_frame_first_subframe
+ polls
- 1].axis2(i
));
377 frames_in_movie
= current_frame
- ((current_frame_first_subframe
>= movie_data
.size()) ? 1 : 0);
381 //Save state of movie code.
382 void movie::save_state(std::string
& proj_id
, uint64_t& curframe
, uint64_t& lagframes
, std::vector
<uint32_t>& pcounters
)
383 throw(std::bad_alloc
)
385 pollcounters
.save_state(pcounters
);
386 proj_id
= _project_id
;
387 curframe
= current_frame
;
388 lagframes
= lag_frames
;
391 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
392 size_t movie::restore_state(uint64_t curframe
, uint64_t lagframe
, const std::vector
<uint32_t>& pcounters
, bool ro
,
393 controller_frame_vector
* old_movie
, const std::string
& old_projectid
) throw(std::bad_alloc
,
396 if(!pollcounters
.check(pcounters
))
397 throw std::runtime_error("Wrong number of poll counters");
398 if(old_movie
&& !movies_compatible(*old_movie
, movie_data
, curframe
, &pcounters
[0], old_projectid
,
400 throw std::runtime_error("Save is not from this movie");
401 uint64_t tmp_firstsubframe
= 0;
402 for(uint64_t i
= 1; i
< curframe
; i
++)
403 tmp_firstsubframe
= tmp_firstsubframe
+ count_changes(tmp_firstsubframe
);
404 //Checks have passed, copy the data.
406 current_frame
= curframe
;
407 current_frame_first_subframe
= tmp_firstsubframe
;
408 lag_frames
= lagframe
;
409 pollcounters
.load_state(pcounters
);
414 long movie::get_reset_status() throw()
416 if(current_frame
== 0 || current_frame_first_subframe
>= movie_data
.size())
417 return -1; //No resets out of range.
418 if(!movie_data
[current_frame_first_subframe
].axis3(0, 0, 1))
419 return -1; //Not a reset.
420 //Also set poll counters on reset cycles to avoid special cases elsewhere.
421 pollcounters
.increment_polls(0, 0, 1);
422 long hi
= movie_data
[current_frame_first_subframe
].axis3(0, 0, 2);
423 long lo
= movie_data
[current_frame_first_subframe
].axis3(0, 0, 3);
424 return hi
* 10000 + lo
;
427 uint64_t movie::frame_subframes(uint64_t frame
) throw()
429 if(frame
< cached_frame
)
431 uint64_t p
= cached_subframe
;
432 for(uint64_t i
= cached_frame
; i
< frame
; i
++)
433 p
= p
+ count_changes(p
);
434 cached_frame
= frame
;
436 return count_changes(p
);
439 void movie::clear_caches() throw()
445 controller_frame
movie::read_subframe(uint64_t frame
, uint64_t subframe
) throw()
447 if(frame
< cached_frame
)
449 uint64_t p
= cached_subframe
;
450 for(uint64_t i
= cached_frame
; i
< frame
; i
++)
451 p
= p
+ count_changes(p
);
452 cached_frame
= frame
;
454 uint64_t max
= count_changes(p
);
456 return movie_data
.blank_frame(true);
460 return movie_data
[p
+ subframe
];
463 void movie::reset_state() throw()
467 current_frame_first_subframe
= 0;
468 pollcounters
.clear();
473 void movie::fast_save(uint64_t& _frame
, uint64_t& _ptr
, uint64_t& _lagc
, std::vector
<uint32_t>& _counters
)
475 pollcounters
.save_state(_counters
);
476 _frame
= current_frame
;
477 _ptr
= current_frame_first_subframe
;
481 void movie::fast_load(uint64_t& _frame
, uint64_t& _ptr
, uint64_t& _lagc
, std::vector
<uint32_t>& _counters
)
484 current_frame
= _frame
;
485 current_frame_first_subframe
= (_ptr
<= movie_data
.size()) ? _ptr
: movie_data
.size();
487 pollcounters
.load_state(_counters
);
488 readonly_mode(false);
492 movie_logic::movie_logic() throw()
496 movie
& movie_logic::get_movie() throw()
501 long movie_logic::new_frame_starting(bool dont_poll
) throw(std::bad_alloc
, std::runtime_error
)
504 controller_frame c
= update_controls(false);
505 if(!mov
.readonly_mode()) {
509 if(c
.axis3(0, 0, 1)) {
510 long hi
= c
.axis3(0, 0, 2);
511 long lo
= c
.axis3(0, 0, 3);
512 mov
.commit_reset(hi
* 10000 + lo
);
514 } else if(!dont_poll
)
516 return mov
.get_reset_status();
519 short movie_logic::input_poll(unsigned port
, unsigned dev
, unsigned id
) throw(std::bad_alloc
, std::runtime_error
)
521 if(!mov
.get_DRDY(port
, dev
, id
)) {
522 mov
.set_controls(update_controls(true));
525 int16_t in
= mov
.next_input(port
, dev
, id
);
526 //std::cerr << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame "
527 // << mov.get_current_frame() << ") giving " << in << std::endl;