Add <functional> to files that use std::function
[lsnes.git] / src / core / advdumper.cpp
blob7a9d34674fbcceee35f0a34b0050072c9c112f12
1 #include "core/advdumper.hpp"
2 #include "core/instance.hpp"
3 #include "core/misc.hpp"
4 #include "library/globalwrap.hpp"
5 #include "library/string.hpp"
6 #include "lua/lua.hpp"
8 #include <functional>
9 #include <map>
10 #include <string>
13 namespace
15 globalwrap<std::map<std::string, dumper_factory_base*>> S_dumpers;
16 globalwrap<std::set<dumper_factory_base::notifier*>> S_notifiers;
19 master_dumper::gameinfo::gameinfo() throw(std::bad_alloc)
21 length = 0;
22 rerecords = "0";
25 std::string master_dumper::gameinfo::get_readable_time(unsigned digits) const throw(std::bad_alloc)
27 double bias = 0.5 * pow(10, -static_cast<int>(digits));
28 double len = length + bias;
29 std::ostringstream str;
30 if(length >= 3600) {
31 double hours = floor(len / 3600);
32 str << hours << ":";
33 len -= hours * 3600;
35 double minutes = floor(len / 60);
36 len -= minutes * 60;
37 double seconds = floor(len);
38 len -= seconds;
39 str << std::setw(2) << std::setfill('0') << minutes << ":" << seconds;
40 if(digits > 0)
41 str << ".";
42 while(digits > 0) {
43 len = 10 * len;
44 str << '0' + static_cast<int>(len);
45 len -= floor(len);
46 digits--;
48 return str.str();
51 size_t master_dumper::gameinfo::get_author_count() const throw()
53 return authors.size();
56 std::string master_dumper::gameinfo::get_author_short(size_t idx) const throw(std::bad_alloc)
58 if(idx >= authors.size())
59 return "";
60 const std::pair<std::string, std::string>& x = authors[idx];
61 if(x.second != "")
62 return x.second;
63 else
64 return x.first;
67 std::string master_dumper::gameinfo::get_author_long(size_t idx) const throw(std::bad_alloc)
69 if(idx >= authors.size())
70 return "";
71 const std::pair<std::string, std::string>& x = authors[idx];
72 if(x.first != "") {
73 if(x.second != "")
74 return x.first + " (" + x.second + ")";
75 else
76 return x.first;
77 } else {
78 if(x.second != "")
79 return "(" + x.second + ")";
80 else
81 return "";
85 uint64_t master_dumper::gameinfo::get_rerecords() const throw()
87 uint64_t v = 0;
88 uint64_t max = 0xFFFFFFFFFFFFFFFFULL;
89 for(size_t i = 0; i < rerecords.length(); i++) {
90 if(v < max / 10)
91 //No risk of overflow.
92 v = v * 10 + static_cast<unsigned>(rerecords[i] - '0');
93 else if(v == max / 10) {
94 //THis may overflow.
95 v = v * 10;
96 if(v + static_cast<unsigned>(rerecords[i] - '0') < v)
97 return max;
98 v = v + static_cast<unsigned>(rerecords[i] - '0');
99 } else
100 //Definite overflow.
101 return max;
103 return v;
106 dumper_factory_base::notifier::~notifier() throw()
110 const std::string& dumper_factory_base::id() throw()
112 return d_id;
115 dumper_factory_base::~dumper_factory_base()
117 S_dumpers().erase(d_id);
118 run_notify();
121 std::set<dumper_factory_base*> dumper_factory_base::get_dumper_set() throw(std::bad_alloc)
123 std::set<dumper_factory_base*> d;
124 for(auto i : S_dumpers())
125 d.insert(i.second);
126 return d;
129 dumper_factory_base::dumper_factory_base(const std::string& id) throw(std::bad_alloc)
131 d_id = id;
132 S_dumpers()[d_id] = this;
135 void dumper_factory_base::ctor_notify()
137 run_notify();
140 void dumper_factory_base::add_notifier(dumper_factory_base::notifier& n)
142 S_notifiers().insert(&n);
145 void dumper_factory_base::drop_notifier(dumper_factory_base::notifier& n)
147 S_notifiers().erase(&n);
150 void dumper_factory_base::run_notify()
152 for(auto i : S_notifiers())
153 i->dumpers_updated();
156 const unsigned dumper_factory_base::target_type_mask = 3;
157 const unsigned dumper_factory_base::target_type_file = 0;
158 const unsigned dumper_factory_base::target_type_prefix = 1;
159 const unsigned dumper_factory_base::target_type_special = 2;
161 dumper_base::dumper_base()
163 mdumper = NULL;
164 fbase = NULL;
165 samples_killed = 0;
168 dumper_base::dumper_base(master_dumper& _mdumper, dumper_factory_base& _fbase)
169 : mdumper(&_mdumper), fbase(&_fbase)
171 threads::arlock h(mdumper->lock);
172 mdumper->dumpers[fbase] = this;
173 samples_killed = 0;
176 dumper_base::~dumper_base() throw()
178 if(!mdumper) return;
179 threads::arlock h(mdumper->lock);
180 mdumper->dumpers.erase(fbase);
181 mdumper->statuschange();
184 master_dumper::notifier::~notifier() throw()
188 master_dumper::master_dumper(lua_state& _lua2)
189 : lua2(_lua2)
191 current_rate_n = 48000;
192 current_rate_d = 1;
193 output = &std::cerr;
196 dumper_base* master_dumper::get_instance(dumper_factory_base* f) throw()
198 threads::arlock h(lock);
199 return dumpers.count(f) ? dumpers[f] : NULL;
202 dumper_base* master_dumper::start(dumper_factory_base& factory, const std::string& mode,
203 const std::string& targetname) throw(std::bad_alloc, std::runtime_error)
205 threads::arlock h(lock);
206 auto f = factory.start(*this, mode, targetname);
207 statuschange();
208 return f;
211 void master_dumper::add_notifier(master_dumper::notifier& n)
213 threads::arlock h(lock);
214 notifications.insert(&n);
217 void master_dumper::drop_notifier(master_dumper::notifier& n)
219 threads::arlock h(lock);
220 notifications.erase(&n);
223 void master_dumper::add_dumper(dumper_base& n)
225 threads::arlock h(lock);
226 sdumpers.insert(&n);
229 void master_dumper::drop_dumper(dumper_base& n)
231 threads::arlock h(lock);
232 sdumpers.erase(&n);
235 void master_dumper::statuschange()
237 for(auto i : notifications)
238 i->dump_status_change();
241 std::pair<uint32_t, uint32_t> master_dumper::get_rate()
243 threads::arlock h(lock);
244 return std::make_pair(current_rate_n, current_rate_d);
247 const master_dumper::gameinfo& master_dumper::get_gameinfo()
249 return current_gi;
252 unsigned master_dumper::get_dumper_count() throw()
254 threads::arlock h(lock);
255 return sdumpers.size();
258 void master_dumper::on_frame(struct framebuffer::raw& _frame, uint32_t fps_n, uint32_t fps_d)
260 threads::arlock h(lock);
261 for(auto i : sdumpers)
262 try {
263 i->on_frame(_frame, fps_n, fps_d);
264 } catch(std::exception& e) {
265 (*output) << "Error in on_frame: " << e.what() << std::endl;
266 } catch(...) {
267 (*output) << "Error in on_frame: <unknown error>" << std::endl;
271 void master_dumper::on_sample(short l, short r)
273 threads::arlock h(lock);
274 for(auto i : sdumpers)
275 try {
276 if(__builtin_expect(i->samples_killed, 0)) {
277 i->samples_killed--;
278 continue;
280 i->on_sample(l, r);
281 } catch(std::exception& e) {
282 (*output) << "Error in on_sample: " << e.what() << std::endl;
283 } catch(...) {
284 (*output) << "Error in on_sample: <unknown error>" << std::endl;
288 void master_dumper::on_rate_change(uint32_t n, uint32_t d)
290 threads::arlock h(lock);
291 uint32_t ga = gcd(n, d);
292 n /= ga;
293 d /= ga;
294 if(n != current_rate_n || d != current_rate_d) {
295 current_rate_n = n;
296 current_rate_d = d;
297 } else
298 return;
300 for(auto i : sdumpers)
301 try {
302 i->on_rate_change(current_rate_n, current_rate_d);
303 } catch(std::exception& e) {
304 (*output) << "Error in on_rate_change: " << e.what() << std::endl;
305 } catch(...) {
306 (*output) << "Error in on_rate_change: <unknown error>" << std::endl;
310 void master_dumper::on_gameinfo_change(const gameinfo& gi)
312 threads::arlock h(lock);
313 current_gi = gi;
314 for(auto i : sdumpers)
315 try {
316 i->on_gameinfo_change(current_gi);
317 } catch(std::exception& e) {
318 (*output) << "Error in on_gameinfo_change: " << e.what() << std::endl;
319 } catch(...) {
320 (*output) << "Error in on_gameinfo_change: <unknown error>" << std::endl;
324 void master_dumper::end_dumps()
326 threads::arlock h(lock);
327 while(sdumpers.size() > 0) {
328 auto d = *sdumpers.begin();
329 try {
330 d->on_end();
331 } catch(std::exception& e) {
332 (*output) << "Error in on_end: " << e.what() << std::endl;
333 sdumpers.erase(d);
334 } catch(...) {
335 (*output) << "Error in on_end: <unknown error>" << std::endl;
336 sdumpers.erase(d);
341 void master_dumper::set_output(std::ostream* _output)
343 threads::arlock h(lock);
344 output = _output;
347 template<bool X> bool master_dumper::render_video_hud(struct framebuffer::fb<X>& target,
348 struct framebuffer::raw& source, uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap,
349 uint32_t bgap, std::function<void()> fn)
351 bool lua_kill_video = false;
352 struct lua::render_context lrc;
353 framebuffer::queue rq;
354 lrc.left_gap = lgap;
355 lrc.right_gap = rgap;
356 lrc.bottom_gap = bgap;
357 lrc.top_gap = tgap;
358 lrc.queue = &rq;
359 lrc.width = source.get_width();
360 lrc.height = source.get_height();
361 lua2.callback_do_video(&lrc, lua_kill_video, hscl, vscl);
362 if(fn)
363 fn();
364 target.reallocate(lrc.left_gap + source.get_width() * hscl + lrc.right_gap, lrc.top_gap +
365 source.get_height() * vscl + lrc.bottom_gap, false);
366 target.set_origin(lrc.left_gap, lrc.top_gap);
367 target.copy_from(source, hscl, vscl);
368 rq.run(target);
369 return !lua_kill_video;
372 uint64_t master_dumper::killed_audio_length(uint32_t fps_n, uint32_t fps_d, double& fraction)
374 auto r = get_rate();
375 double x = 1.0 * fps_d * r.first / (fps_n * r.second) + fraction;
376 uint64_t y = x;
377 fraction = x - y;
378 return y;
381 template bool master_dumper::render_video_hud(struct framebuffer::fb<false>& target, struct framebuffer::raw& source,
382 uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap, uint32_t bgap,
383 std::function<void()> fn);
384 template bool master_dumper::render_video_hud(struct framebuffer::fb<true>& target, struct framebuffer::raw& source,
385 uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap, uint32_t bgap,
386 std::function<void()> fn);