Lua: Fix type confusion between signed and unsigned
[lsnes.git] / src / core / advdumper.cpp
blob71d790429ed2b79869f4a4eab11d9722512a27e1
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 <map>
9 #include <string>
12 namespace
14 globalwrap<std::map<std::string, dumper_factory_base*>> S_dumpers;
15 globalwrap<std::set<dumper_factory_base::notifier*>> S_notifiers;
18 master_dumper::gameinfo::gameinfo() throw(std::bad_alloc)
20 length = 0;
21 rerecords = "0";
24 std::string master_dumper::gameinfo::get_readable_time(unsigned digits) const throw(std::bad_alloc)
26 double bias = 0.5 * pow(10, -static_cast<int>(digits));
27 double len = length + bias;
28 std::ostringstream str;
29 if(length >= 3600) {
30 double hours = floor(len / 3600);
31 str << hours << ":";
32 len -= hours * 3600;
34 double minutes = floor(len / 60);
35 len -= minutes * 60;
36 double seconds = floor(len);
37 len -= seconds;
38 str << std::setw(2) << std::setfill('0') << minutes << ":" << seconds;
39 if(digits > 0)
40 str << ".";
41 while(digits > 0) {
42 len = 10 * len;
43 str << '0' + static_cast<int>(len);
44 len -= floor(len);
45 digits--;
47 return str.str();
50 size_t master_dumper::gameinfo::get_author_count() const throw()
52 return authors.size();
55 std::string master_dumper::gameinfo::get_author_short(size_t idx) const throw(std::bad_alloc)
57 if(idx >= authors.size())
58 return "";
59 const std::pair<std::string, std::string>& x = authors[idx];
60 if(x.second != "")
61 return x.second;
62 else
63 return x.first;
66 std::string master_dumper::gameinfo::get_author_long(size_t idx) const throw(std::bad_alloc)
68 if(idx >= authors.size())
69 return "";
70 const std::pair<std::string, std::string>& x = authors[idx];
71 if(x.first != "") {
72 if(x.second != "")
73 return x.first + " (" + x.second + ")";
74 else
75 return x.first;
76 } else {
77 if(x.second != "")
78 return "(" + x.second + ")";
79 else
80 return "";
84 uint64_t master_dumper::gameinfo::get_rerecords() const throw()
86 uint64_t v = 0;
87 uint64_t max = 0xFFFFFFFFFFFFFFFFULL;
88 for(size_t i = 0; i < rerecords.length(); i++) {
89 if(v < max / 10)
90 //No risk of overflow.
91 v = v * 10 + static_cast<unsigned>(rerecords[i] - '0');
92 else if(v == max / 10) {
93 //THis may overflow.
94 v = v * 10;
95 if(v + static_cast<unsigned>(rerecords[i] - '0') < v)
96 return max;
97 v = v + static_cast<unsigned>(rerecords[i] - '0');
98 } else
99 //Definite overflow.
100 return max;
102 return v;
105 dumper_factory_base::notifier::~notifier() throw()
109 const std::string& dumper_factory_base::id() throw()
111 return d_id;
114 dumper_factory_base::~dumper_factory_base()
116 S_dumpers().erase(d_id);
117 run_notify();
120 std::set<dumper_factory_base*> dumper_factory_base::get_dumper_set() throw(std::bad_alloc)
122 std::set<dumper_factory_base*> d;
123 for(auto i : S_dumpers())
124 d.insert(i.second);
125 return d;
128 dumper_factory_base::dumper_factory_base(const std::string& id) throw(std::bad_alloc)
130 d_id = id;
131 S_dumpers()[d_id] = this;
134 void dumper_factory_base::ctor_notify()
136 run_notify();
139 void dumper_factory_base::add_notifier(dumper_factory_base::notifier& n)
141 S_notifiers().insert(&n);
144 void dumper_factory_base::drop_notifier(dumper_factory_base::notifier& n)
146 S_notifiers().erase(&n);
149 void dumper_factory_base::run_notify()
151 for(auto i : S_notifiers())
152 i->dumpers_updated();
155 const unsigned dumper_factory_base::target_type_mask = 3;
156 const unsigned dumper_factory_base::target_type_file = 0;
157 const unsigned dumper_factory_base::target_type_prefix = 1;
158 const unsigned dumper_factory_base::target_type_special = 2;
160 dumper_base::dumper_base()
162 mdumper = NULL;
163 fbase = NULL;
164 samples_killed = 0;
167 dumper_base::dumper_base(master_dumper& _mdumper, dumper_factory_base& _fbase)
168 : mdumper(&_mdumper), fbase(&_fbase)
170 threads::arlock h(mdumper->lock);
171 mdumper->dumpers[fbase] = this;
172 samples_killed = 0;
175 dumper_base::~dumper_base() throw()
177 if(!mdumper) return;
178 threads::arlock h(mdumper->lock);
179 mdumper->dumpers.erase(fbase);
180 mdumper->statuschange();
183 master_dumper::notifier::~notifier() throw()
187 master_dumper::master_dumper(lua_state& _lua2)
188 : lua2(_lua2)
190 current_rate_n = 48000;
191 current_rate_d = 1;
192 output = &std::cerr;
195 dumper_base* master_dumper::get_instance(dumper_factory_base* f) throw()
197 threads::arlock h(lock);
198 return dumpers.count(f) ? dumpers[f] : NULL;
201 dumper_base* master_dumper::start(dumper_factory_base& factory, const std::string& mode,
202 const std::string& targetname) throw(std::bad_alloc, std::runtime_error)
204 threads::arlock h(lock);
205 auto f = factory.start(*this, mode, targetname);
206 statuschange();
207 return f;
210 void master_dumper::add_notifier(master_dumper::notifier& n)
212 threads::arlock h(lock);
213 notifications.insert(&n);
216 void master_dumper::drop_notifier(master_dumper::notifier& n)
218 threads::arlock h(lock);
219 notifications.erase(&n);
222 void master_dumper::add_dumper(dumper_base& n)
224 threads::arlock h(lock);
225 sdumpers.insert(&n);
228 void master_dumper::drop_dumper(dumper_base& n)
230 threads::arlock h(lock);
231 sdumpers.erase(&n);
234 void master_dumper::statuschange()
236 for(auto i : notifications)
237 i->dump_status_change();
240 std::pair<uint32_t, uint32_t> master_dumper::get_rate()
242 threads::arlock h(lock);
243 return std::make_pair(current_rate_n, current_rate_d);
246 const master_dumper::gameinfo& master_dumper::get_gameinfo()
248 return current_gi;
251 unsigned master_dumper::get_dumper_count() throw()
253 threads::arlock h(lock);
254 return sdumpers.size();
257 void master_dumper::on_frame(struct framebuffer::raw& _frame, uint32_t fps_n, uint32_t fps_d)
259 threads::arlock h(lock);
260 for(auto i : sdumpers)
261 try {
262 i->on_frame(_frame, fps_n, fps_d);
263 } catch(std::exception& e) {
264 (*output) << "Error in on_frame: " << e.what() << std::endl;
265 } catch(...) {
266 (*output) << "Error in on_frame: <unknown error>" << std::endl;
270 void master_dumper::on_sample(short l, short r)
272 threads::arlock h(lock);
273 for(auto i : sdumpers)
274 try {
275 if(__builtin_expect(i->samples_killed, 0)) {
276 i->samples_killed--;
277 continue;
279 i->on_sample(l, r);
280 } catch(std::exception& e) {
281 (*output) << "Error in on_sample: " << e.what() << std::endl;
282 } catch(...) {
283 (*output) << "Error in on_sample: <unknown error>" << std::endl;
287 void master_dumper::on_rate_change(uint32_t n, uint32_t d)
289 threads::arlock h(lock);
290 uint32_t ga = gcd(n, d);
291 n /= ga;
292 d /= ga;
293 if(n != current_rate_n || d != current_rate_d) {
294 current_rate_n = n;
295 current_rate_d = d;
296 } else
297 return;
299 for(auto i : sdumpers)
300 try {
301 i->on_rate_change(current_rate_n, current_rate_d);
302 } catch(std::exception& e) {
303 (*output) << "Error in on_rate_change: " << e.what() << std::endl;
304 } catch(...) {
305 (*output) << "Error in on_rate_change: <unknown error>" << std::endl;
309 void master_dumper::on_gameinfo_change(const gameinfo& gi)
311 threads::arlock h(lock);
312 current_gi = gi;
313 for(auto i : sdumpers)
314 try {
315 i->on_gameinfo_change(current_gi);
316 } catch(std::exception& e) {
317 (*output) << "Error in on_gameinfo_change: " << e.what() << std::endl;
318 } catch(...) {
319 (*output) << "Error in on_gameinfo_change: <unknown error>" << std::endl;
323 void master_dumper::end_dumps()
325 threads::arlock h(lock);
326 while(sdumpers.size() > 0) {
327 auto d = *sdumpers.begin();
328 try {
329 d->on_end();
330 } catch(std::exception& e) {
331 (*output) << "Error in on_end: " << e.what() << std::endl;
332 sdumpers.erase(d);
333 } catch(...) {
334 (*output) << "Error in on_end: <unknown error>" << std::endl;
335 sdumpers.erase(d);
340 void master_dumper::set_output(std::ostream* _output)
342 threads::arlock h(lock);
343 output = _output;
346 template<bool X> bool master_dumper::render_video_hud(struct framebuffer::fb<X>& target,
347 struct framebuffer::raw& source, uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap,
348 uint32_t bgap, std::function<void()> fn)
350 bool lua_kill_video = false;
351 struct lua::render_context lrc;
352 framebuffer::queue rq;
353 lrc.left_gap = lgap;
354 lrc.right_gap = rgap;
355 lrc.bottom_gap = bgap;
356 lrc.top_gap = tgap;
357 lrc.queue = &rq;
358 lrc.width = source.get_width();
359 lrc.height = source.get_height();
360 lua2.callback_do_video(&lrc, lua_kill_video, hscl, vscl);
361 if(fn)
362 fn();
363 target.reallocate(lrc.left_gap + source.get_width() * hscl + lrc.right_gap, lrc.top_gap +
364 source.get_height() * vscl + lrc.bottom_gap, false);
365 target.set_origin(lrc.left_gap, lrc.top_gap);
366 target.copy_from(source, hscl, vscl);
367 rq.run(target);
368 return !lua_kill_video;
371 uint64_t master_dumper::killed_audio_length(uint32_t fps_n, uint32_t fps_d, double& fraction)
373 auto r = get_rate();
374 double x = 1.0 * fps_d * r.first / (fps_n * r.second) + fraction;
375 uint64_t y = x;
376 fraction = x - y;
377 return y;
380 template bool master_dumper::render_video_hud(struct framebuffer::fb<false>& target, struct framebuffer::raw& source,
381 uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap, uint32_t bgap,
382 std::function<void()> fn);
383 template bool master_dumper::render_video_hud(struct framebuffer::fb<true>& target, struct framebuffer::raw& source,
384 uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap, uint32_t bgap,
385 std::function<void()> fn);