Fix most hotkeys to show up in configuration (were missing due to errant !)
[lsnes.git] / src / core / advdumper.cpp
blob00fff941672b3a9ec975432076959984dd5021f4
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;
174 dumper_base::~dumper_base() throw()
176 if(!mdumper) return;
177 threads::arlock h(mdumper->lock);
178 mdumper->dumpers.erase(fbase);
179 mdumper->statuschange();
182 master_dumper::notifier::~notifier() throw()
186 master_dumper::master_dumper(lua_state& _lua2)
187 : lua2(_lua2)
189 current_rate_n = 48000;
190 current_rate_d = 1;
191 output = &std::cerr;
194 dumper_base* master_dumper::get_instance(dumper_factory_base* f) throw()
196 threads::arlock h(lock);
197 return dumpers.count(f) ? dumpers[f] : NULL;
200 dumper_base* master_dumper::start(dumper_factory_base& factory, const std::string& mode,
201 const std::string& targetname) throw(std::bad_alloc, std::runtime_error)
203 threads::arlock h(lock);
204 auto f = factory.start(*this, mode, targetname);
205 statuschange();
206 return f;
209 void master_dumper::add_notifier(master_dumper::notifier& n)
211 threads::arlock h(lock);
212 notifications.insert(&n);
215 void master_dumper::drop_notifier(master_dumper::notifier& n)
217 threads::arlock h(lock);
218 notifications.erase(&n);
221 void master_dumper::add_dumper(dumper_base& n)
223 threads::arlock h(lock);
224 sdumpers.insert(&n);
227 void master_dumper::drop_dumper(dumper_base& n)
229 threads::arlock h(lock);
230 sdumpers.erase(&n);
233 void master_dumper::statuschange()
235 for(auto i : notifications)
236 i->dump_status_change();
239 std::pair<uint32_t, uint32_t> master_dumper::get_rate()
241 threads::arlock h(lock);
242 return std::make_pair(current_rate_n, current_rate_d);
245 const master_dumper::gameinfo& master_dumper::get_gameinfo()
247 return current_gi;
250 unsigned master_dumper::get_dumper_count() throw()
252 threads::arlock h(lock);
253 return sdumpers.size();
256 void master_dumper::on_frame(struct framebuffer::raw& _frame, uint32_t fps_n, uint32_t fps_d)
258 threads::arlock h(lock);
259 for(auto i : sdumpers)
260 try {
261 i->on_frame(_frame, fps_n, fps_d);
262 } catch(std::exception& e) {
263 (*output) << "Error in on_frame: " << e.what() << std::endl;
264 } catch(...) {
265 (*output) << "Error in on_frame: <unknown error>" << std::endl;
269 void master_dumper::on_sample(short l, short r)
271 threads::arlock h(lock);
272 for(auto i : sdumpers)
273 try {
274 if(__builtin_expect(i->samples_killed, 0)) {
275 i->samples_killed--;
276 continue;
278 i->on_sample(l, r);
279 } catch(std::exception& e) {
280 (*output) << "Error in on_sample: " << e.what() << std::endl;
281 } catch(...) {
282 (*output) << "Error in on_sample: <unknown error>" << std::endl;
286 void master_dumper::on_rate_change(uint32_t n, uint32_t d)
288 threads::arlock h(lock);
289 uint32_t ga = gcd(n, d);
290 n /= ga;
291 d /= ga;
292 if(n != current_rate_n || d != current_rate_d) {
293 current_rate_n = n;
294 current_rate_d = d;
295 } else
296 return;
298 for(auto i : sdumpers)
299 try {
300 i->on_rate_change(current_rate_n, current_rate_d);
301 } catch(std::exception& e) {
302 (*output) << "Error in on_rate_change: " << e.what() << std::endl;
303 } catch(...) {
304 (*output) << "Error in on_rate_change: <unknown error>" << std::endl;
308 void master_dumper::on_gameinfo_change(const gameinfo& gi)
310 threads::arlock h(lock);
311 current_gi = gi;
312 for(auto i : sdumpers)
313 try {
314 i->on_gameinfo_change(current_gi);
315 } catch(std::exception& e) {
316 (*output) << "Error in on_gameinfo_change: " << e.what() << std::endl;
317 } catch(...) {
318 (*output) << "Error in on_gameinfo_change: <unknown error>" << std::endl;
322 void master_dumper::end_dumps()
324 threads::arlock h(lock);
325 while(sdumpers.size() > 0) {
326 auto d = *sdumpers.begin();
327 try {
328 d->on_end();
329 } catch(std::exception& e) {
330 (*output) << "Error in on_end: " << e.what() << std::endl;
331 sdumpers.erase(d);
332 } catch(...) {
333 (*output) << "Error in on_end: <unknown error>" << std::endl;
334 sdumpers.erase(d);
339 void master_dumper::set_output(std::ostream* _output)
341 threads::arlock h(lock);
342 output = _output;
345 template<bool X> bool master_dumper::render_video_hud(struct framebuffer::fb<X>& target,
346 struct framebuffer::raw& source, uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap,
347 uint32_t bgap, std::function<void()> fn)
349 bool lua_kill_video = false;
350 struct lua::render_context lrc;
351 framebuffer::queue rq;
352 lrc.left_gap = lgap;
353 lrc.right_gap = rgap;
354 lrc.bottom_gap = bgap;
355 lrc.top_gap = tgap;
356 lrc.queue = &rq;
357 lrc.width = source.get_width();
358 lrc.height = source.get_height();
359 lua2.callback_do_video(&lrc, lua_kill_video, hscl, vscl);
360 if(fn)
361 fn();
362 target.reallocate(lrc.left_gap + source.get_width() * hscl + lrc.right_gap, lrc.top_gap +
363 source.get_height() * vscl + lrc.bottom_gap, false);
364 target.set_origin(lrc.left_gap, lrc.top_gap);
365 target.copy_from(source, hscl, vscl);
366 rq.run(target);
367 return !lua_kill_video;
370 uint64_t master_dumper::killed_audio_length(uint32_t fps_n, uint32_t fps_d, double& fraction)
372 auto r = get_rate();
373 double x = 1.0 * fps_d * r.first / (fps_n * r.second) + fraction;
374 uint64_t y = x;
375 fraction = x - y;
376 return y;
379 template bool master_dumper::render_video_hud(struct framebuffer::fb<false>& target, struct framebuffer::raw& source,
380 uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap, uint32_t bgap,
381 std::function<void()> fn);
382 template bool master_dumper::render_video_hud(struct framebuffer::fb<true>& target, struct framebuffer::raw& source,
383 uint32_t hscl, uint32_t vscl, uint32_t lgap, uint32_t tgap, uint32_t rgap, uint32_t bgap,
384 std::function<void()> fn);