bsnes: redump sprite/palette functions
[lsnes.git] / src / library / movie.cpp
bloba12f933e8a3427fbc67fffcd0cb79ec6dc61320c
1 #include "movie.hpp"
2 #include "minmax.hpp"
4 #include <stdexcept>
5 #include <cassert>
6 #include <cstring>
7 #include <fstream>
10 //std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
12 namespace
14 bool movies_compatible(controller_frame_vector& old_movie, controller_frame_vector& new_movie,
15 uint64_t frame, const uint32_t* polls, const std::string& old_projectid,
16 const std::string& new_projectid)
18 //Project IDs have to match.
19 if(old_projectid != new_projectid)
20 return false;
21 return old_movie.compatible(new_movie, frame, polls);
25 movie::~movie()
27 if(movie_data)
28 movie_data->clear_framecount_notification(movie_data_nh);
31 void movie::set_all_DRDY() throw()
33 pollcounters.set_all_DRDY();
36 std::string movie::rerecord_count() throw(std::bad_alloc)
38 return rerecords;
41 void movie::rerecord_count(const std::string& count) throw(std::bad_alloc)
43 rerecords = count;
46 std::string movie::project_id() throw(std::bad_alloc)
48 return _project_id;
51 void movie::project_id(const std::string& id) throw(std::bad_alloc)
53 _project_id = id;
56 bool movie::readonly_mode() throw()
58 return readonly;
61 void movie::set_controls(controller_frame controls) throw()
63 current_controls = controls;
66 uint32_t movie::count_changes(uint64_t first_subframe) throw()
68 return movie_data->subframe_count(first_subframe);
71 controller_frame movie::get_controls() throw()
73 if(!readonly)
74 return current_controls;
75 controller_frame c = movie_data->blank_frame(false);
76 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
77 if(current_frame == 0)
78 return c;
79 //Otherwise find the last valid frame of input.
80 uint32_t changes = count_changes(current_frame_first_subframe);
81 if(!changes)
82 return c; //End of movie.
83 for(size_t i = 0; i < movie_data->get_types().indices(); i++) {
84 uint32_t polls = pollcounters.get_polls(i);
85 uint32_t index = (changes > polls) ? polls : changes - 1;
86 c.axis2(i, (*movie_data)[current_frame_first_subframe + index].axis2(i));
88 return c;
91 uint64_t movie::get_current_frame() throw()
93 return current_frame;
96 uint64_t movie::get_lag_frames() throw()
98 return lag_frames;
101 void movie::next_frame() throw(std::bad_alloc)
103 //Adjust lag count. Frame 0 MUST NOT be considered lag.
104 bool pflag = pflag_handler ? pflag_handler->get_pflag() : false;
105 if(current_frame && !pflag)
106 lag_frames++;
107 else if(pflag_handler)
108 pflag_handler->set_pflag(false);
110 //If all poll counters are zero for all real controls, this frame is lag.
111 bool this_frame_lag = !pollcounters.has_polled();
112 //Oh, frame 0 must not be considered lag.
113 if(current_frame && this_frame_lag) {
114 //debuglog << "Frame " << current_frame << " is lag" << std::endl << std::flush;
115 if(!readonly) {
116 //If in read-write mode, write a dummy record for the frame. Force sync flag.
117 //As index should be movie_data->size(), it is correct afterwards.
118 movie_data->append(current_controls.copy(true));
122 //Reset the poll counters and DRDY flags.
123 pollcounters.clear();
125 //Increment the current frame counter and subframe counter. Note that first subframe is undefined for
126 //frame 0 and 0 for frame 1.
127 if(current_frame)
128 current_frame_first_subframe = current_frame_first_subframe +
129 count_changes(current_frame_first_subframe);
130 else
131 current_frame_first_subframe = 0;
132 current_frame++;
135 bool movie::get_DRDY(unsigned port, unsigned controller, unsigned ctrl) throw(std::logic_error)
137 return pollcounters.get_DRDY(port, controller, ctrl);
140 short movie::next_input(unsigned port, unsigned controller, unsigned ctrl) throw(std::bad_alloc, std::logic_error)
142 pollcounters.clear_DRDY(port, controller, ctrl);
144 if(readonly) {
145 //In readonly mode...
146 //If at the end of the movie, return released / neutral (but also record the poll)...
147 if(current_frame_first_subframe >= movie_data->size()) {
148 pollcounters.increment_polls(port, controller, ctrl);
149 return 0;
151 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
152 if(current_frame == 0)
153 return 0;
154 //Otherwise find the last valid frame of input.
155 uint32_t changes = count_changes(current_frame_first_subframe);
156 uint32_t polls = pollcounters.get_polls(port, controller, ctrl);
157 uint32_t index = (changes > polls) ? polls : changes - 1;
158 int16_t data = (*movie_data)[current_frame_first_subframe + index].axis3(port, controller, ctrl);
159 pollcounters.increment_polls(port, controller, ctrl);
160 return data;
161 } else {
162 //Readwrite mode.
163 //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
164 //Also, frame 0 must not be added to movie file.
165 if(current_frame == 0)
166 return 0;
167 //If at movie end, insert complete input with frame sync set (this is the first subframe).
168 if(current_frame_first_subframe >= movie_data->size()) {
169 movie_data->append(current_controls.copy(true));
170 //current_frame_first_subframe should be movie_data->size(), so it is right.
171 pollcounters.increment_polls(port, controller, ctrl);
172 return (*movie_data)[current_frame_first_subframe].axis3(port, controller, ctrl);
174 short new_value = current_controls.axis3(port, controller, ctrl);
175 //Fortunately, we know this frame is the last one in movie_data.
176 uint32_t pollcounter = pollcounters.get_polls(port, controller, ctrl);
177 if(current_frame_first_subframe + pollcounter < movie_data->size()) {
178 //The index is within existing size. Change the value and propagate to all subsequent
179 //subframes.
180 for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data->size(); i++)
181 (*movie_data)[i].axis3(port, controller, ctrl, new_value);
182 } else if(new_value != (*movie_data)[movie_data->size() - 1].axis3(port, controller, ctrl)) {
183 //The index is not within existing size and value does not match. We need to create a new
184 //subframes(s), copying the last subframe.
185 while(current_frame_first_subframe + pollcounter >= movie_data->size())
186 movie_data->append((*movie_data)[movie_data->size() - 1].copy(false));
187 (*movie_data)[current_frame_first_subframe + pollcounter].axis3(port, controller, ctrl,
188 new_value);
190 pollcounters.increment_polls(port, controller, ctrl);
191 return new_value;
195 movie::movie() throw(std::bad_alloc)
197 movie_data = NULL;
198 seqno = 0;
199 readonly = false;
200 rerecords = "0";
201 _project_id = "";
202 current_frame = 0;
203 current_frame_first_subframe = 0;
204 lag_frames = 0;
205 pflag_handler = NULL;
206 clear_caches();
209 void movie::load(const std::string& rerecs, const std::string& project_id, controller_frame_vector& input)
210 throw(std::bad_alloc, std::runtime_error)
212 if(input.size() > 0 && !input[0].sync())
213 throw std::runtime_error("First subframe MUST have frame sync flag set");
214 seqno++;
215 clear_caches();
216 readonly = true;
217 rerecords = rerecs;
218 _project_id = project_id;
219 current_frame = 0;
220 current_frame_first_subframe = 0;
221 pollcounters = pollcounter_vector(input.get_types());
222 lag_frames = 0;
223 //This is to force internal type of current_controls to become correct.
224 current_controls = input.blank_frame(false);
227 unsigned movie::next_poll_number()
229 return pollcounters.max_polls() + 1;
232 void movie::readonly_mode(bool enable) throw(std::bad_alloc)
234 bool was_in_readonly = readonly;
235 readonly = enable;
236 if(was_in_readonly && !readonly) {
237 clear_caches();
238 //Transitioning to readwrite mode, we have to adjust the length of the movie data.
239 if(current_frame == 0) {
240 //WTF... At before first frame. Blank the entiere movie.
241 movie_data->clear();
242 return;
244 //Fun special case: Current frame is not in movie (current_frame_first_subframe >=
245 //movie_data->size()). In this case, we have to extend the movie data.
246 if(current_frame_first_subframe >= movie_data->size()) {
247 //Yes, this will insert one extra frame... But we will lose it later if it is not needed.
248 while(movie_data->count_frames() < current_frame)
249 movie_data->append(movie_data->blank_frame(true));
250 current_frame_first_subframe = movie_data->size() - 1;
253 //We have to take the part up to furthest currently readable subframe. Also, we need to propagate
254 //forward values with smaller poll counters.
255 uint64_t next_frame_first_subframe = current_frame_first_subframe +
256 count_changes(current_frame_first_subframe);
257 uint64_t max_readable_subframes = current_frame_first_subframe + pollcounters.max_polls();
258 if(max_readable_subframes > next_frame_first_subframe)
259 max_readable_subframes = next_frame_first_subframe;
261 movie_data->resize(max_readable_subframes);
262 next_frame_first_subframe = max_readable_subframes;
263 //Propagate buttons. The only one that needs special handling is sync flag (index 0, tuple 0,0,0).
264 for(size_t i = 1; i < movie_data->get_types().indices(); i++) {
265 uint32_t polls = pollcounters.get_polls(i);
266 polls = polls ? polls : 1;
267 for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
268 (*movie_data)[j].axis2(i, (*movie_data)[current_frame_first_subframe + polls - 1].
269 axis2(i));
274 //Save state of movie code.
275 void movie::save_state(std::string& proj_id, uint64_t& curframe, uint64_t& lagframes, std::vector<uint32_t>& pcounters)
276 throw(std::bad_alloc)
278 pollcounters.save_state(pcounters);
279 proj_id = _project_id;
280 curframe = current_frame;
281 lagframes = lag_frames;
284 //Restore state of movie code. Throws if state is invalid. Flag gives new state of readonly flag.
285 size_t movie::restore_state(uint64_t curframe, uint64_t lagframe, const std::vector<uint32_t>& pcounters, bool ro,
286 controller_frame_vector* old_movie, const std::string& old_projectid) throw(std::bad_alloc,
287 std::runtime_error)
289 if(!pollcounters.check(pcounters))
290 throw std::runtime_error("Wrong number of poll counters");
291 if(old_movie && !movies_compatible(*old_movie, *movie_data, curframe, &pcounters[0], old_projectid,
292 _project_id))
293 throw std::runtime_error("Save is not from this movie");
294 uint64_t tmp_firstsubframe = 0;
295 for(uint64_t i = 1; i < curframe; i++)
296 tmp_firstsubframe = tmp_firstsubframe + count_changes(tmp_firstsubframe);
297 //Checks have passed, copy the data.
298 readonly = true;
299 current_frame = curframe;
300 current_frame_first_subframe = tmp_firstsubframe;
301 lag_frames = lagframe;
302 pollcounters.load_state(pcounters);
303 readonly_mode(ro);
304 return 0;
307 uint64_t movie::frame_subframes(uint64_t frame) throw()
309 if(frame < cached_frame)
310 clear_caches();
311 uint64_t p = cached_subframe;
312 for(uint64_t i = cached_frame; i < frame; i++)
313 p = p + count_changes(p);
314 cached_frame = frame;
315 cached_subframe = p;
316 return count_changes(p);
319 void movie::clear_caches() throw()
321 cached_frame = 1;
322 cached_subframe = 0;
325 controller_frame movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
327 if(frame < cached_frame)
328 clear_caches();
329 uint64_t p = cached_subframe;
330 for(uint64_t i = cached_frame; i < frame; i++)
331 p = p + count_changes(p);
332 cached_frame = frame;
333 cached_subframe = p;
334 uint64_t max = count_changes(p);
335 if(!max) {
336 return movie_data->blank_frame(true);
338 if(max <= subframe)
339 subframe = max - 1;
340 return (*movie_data)[p + subframe];
343 void movie::reset_state() throw()
345 readonly = true;
346 current_frame = 0;
347 current_frame_first_subframe = 0;
348 pollcounters.clear();
349 lag_frames = 0;
350 clear_caches();
353 void movie::fast_save(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
355 pollcounters.save_state(_counters);
356 _frame = current_frame;
357 _ptr = current_frame_first_subframe;
358 _lagc = lag_frames;
361 void movie::fast_load(uint64_t& _frame, uint64_t& _ptr, uint64_t& _lagc, std::vector<uint32_t>& _counters)
363 readonly = true;
364 current_frame = _frame;
365 current_frame_first_subframe = (_ptr <= movie_data->size()) ? _ptr : movie_data->size();
366 lag_frames = _lagc;
367 pollcounters.load_state(_counters);
368 readonly_mode(false);
371 void movie::set_pflag_handler(poll_flag* handler)
373 pflag_handler = handler;
376 int16_t movie::read_subframe_at_index(uint32_t subframe, unsigned port, unsigned controller, unsigned ctrl)
378 //Readwrite, Past the end of movie or before the beginning?
379 if(!readonly || current_frame_first_subframe >= movie_data->size() || current_frame == 0)
380 return 0;
381 uint32_t changes = count_changes(current_frame_first_subframe);
382 uint32_t index = (changes > subframe) ? subframe : changes - 1;
383 return (*movie_data)[current_frame_first_subframe + index].axis3(port, controller, ctrl);
386 void movie::write_subframe_at_index(uint32_t subframe, unsigned port, unsigned controller, unsigned ctrl,
387 int16_t x)
389 if(!readonly || current_frame == 0)
390 return;
391 bool extended = false;
392 while(current_frame > movie_data->count_frames()) {
393 //Extend the movie by a blank frame.
394 extended = true;
395 movie_data->append(movie_data->blank_frame(true));
397 if(extended) {
398 clear_caches();
399 current_frame_first_subframe = movie_data->size() - 1;
401 if(current_frame < movie_data->count_frames()) {
402 //If we are not on the last frame, write is possible if it is not on extension.
403 uint32_t changes = count_changes(current_frame_first_subframe);
404 if(subframe < changes)
405 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
406 } else {
407 //Writing to the last frame. If not on extension, handle like non-last frame.
408 //Note that if movie had to be extended, it was done before, resulting movie like in state with
409 //0 stored subframes.
410 uint32_t changes = count_changes(current_frame_first_subframe);
411 if(subframe < changes)
412 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
413 else {
414 //If there is no frame at all, create one.
415 if(current_frame_first_subframe >= movie_data->size()) {
416 movie_data->append(movie_data->blank_frame(true));
418 //Create needed subframes.
419 while(count_changes(current_frame_first_subframe) <= subframe)
420 movie_data->append(movie_data->blank_frame(false));
421 //Write it.
422 (*movie_data)[current_frame_first_subframe + subframe].axis3(port, controller, ctrl, x);
427 movie::poll_flag::~poll_flag()
431 void movie::set_movie_data(controller_frame_vector* data)
433 uint64_t old_nh = movie_data_nh;
434 controller_frame_vector* old = movie_data;
435 if(data)
436 movie_data_nh = data->set_framecount_notification([this](controller_frame_vector& src,
437 uint64_t old_frames) {
438 //Recompute frame_first_subframe.
439 while(current_frame_first_subframe < movie_data->size() && current_frame > old_frames + 1) {
440 //OK, movie has been extended.
441 current_frame_first_subframe += count_changes(current_frame_first_subframe);
442 old_frames++;
444 //Nobody is this stupid, right?
445 current_frame_first_subframe = min(current_frame_first_subframe,
446 static_cast<uint64_t>(movie_data->size()));
448 movie_data = data;
449 clear_caches();
450 if(old)
451 old->clear_framecount_notification(old_nh);