Make various instance stuff to take references to other instance objs
[lsnes.git] / src / core / memorywatch.cpp
blob4d6d115570cd3711c46f0804fa2d61e944c57ae0
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/framebuffer.hpp"
4 #include "core/instance.hpp"
5 #include "core/memorymanip.hpp"
6 #include "core/memorywatch.hpp"
7 #include "core/project.hpp"
8 #include "core/window.hpp"
9 #include "fonts/wrapper.hpp"
10 #include "library/directory.hpp"
11 #include "library/framebuffer-font2.hpp"
12 #include "library/string.hpp"
13 #include "library/int24.hpp"
14 #include "library/mathexpr-ntype.hpp"
15 #include "library/memorywatch.hpp"
16 #include "library/memorywatch-list.hpp"
17 #include "library/memorywatch-fb.hpp"
18 #include "library/memorywatch-null.hpp"
20 #include <cstdio>
21 #include <cstdlib>
22 #include <list>
23 #include <iomanip>
24 #include <stack>
25 #include <cmath>
26 #include <sstream>
27 #include <map>
29 namespace
31 std::map<std::string, std::pair<framebuffer::font2*, size_t>> fonts_in_use;
33 framebuffer::font2& get_builtin_font2()
35 static framebuffer::font2 f(main_font);
36 return f;
39 framebuffer::font2* get_font(const std::string filename)
41 //Handle NULL font.
42 if(filename == "")
43 return &get_builtin_font2();
44 std::string abs_filename = directory::absolute_path(filename);
45 if(fonts_in_use.count(abs_filename)) {
46 fonts_in_use[abs_filename].second++;
47 return fonts_in_use[abs_filename].first;
49 framebuffer::font2* f = new framebuffer::font2(abs_filename);
50 try {
51 fonts_in_use[abs_filename] = std::make_pair(f, 1);
52 } catch(...) {
53 delete f;
54 throw;
56 return f;
58 void put_font(framebuffer::font2* font)
60 //Handle NULL font (always there).
61 if(!font)
62 return;
63 //Find font using this.
64 std::string filename;
65 for(auto& i : fonts_in_use)
66 if(i.second.first == font)
67 filename = i.first;
68 if(filename == "")
69 return;
70 fonts_in_use[filename].second--;
71 if(!fonts_in_use[filename].second) {
72 delete fonts_in_use[filename].first;
73 fonts_in_use.erase(filename);
77 std::string json_string_default(const JSON::node& node, const std::string& pointer, const std::string& dflt)
79 return (node.type_of(pointer) == JSON::string) ? node[pointer].as_string8() : dflt;
82 uint64_t json_unsigned_default(const JSON::node& node, const std::string& pointer, uint64_t dflt)
84 return (node.type_of(pointer) == JSON::number) ? node[pointer].as_uint() : dflt;
87 int64_t json_signed_default(const JSON::node& node, const std::string& pointer, int64_t dflt)
89 return (node.type_of(pointer) == JSON::number) ? node[pointer].as_int() : dflt;
92 bool json_boolean_default(const JSON::node& node, const std::string& pointer, bool dflt)
94 return (node.type_of(pointer) == JSON::boolean) ? node[pointer].as_bool() : dflt;
97 void dummy_target_fn(const std::string& n, const std::string& v) {}
100 memwatch_printer::memwatch_printer()
102 position = PC_MEMORYWATCH;
103 cond_enable = false;
104 onscreen_alt_origin_x = false;
105 onscreen_alt_origin_y = false;
106 onscreen_cliprange_x = false;
107 onscreen_cliprange_y = false;
108 onscreen_fg_color = 0xFFFFFF;
109 onscreen_bg_color = -1;
110 onscreen_halo_color = 0;
113 JSON::node memwatch_printer::serialize()
115 JSON::node ndata(JSON::object);
116 switch(position) {
117 case PC_DISABLED: ndata["position"] = JSON::s("disabled"); break;
118 case PC_MEMORYWATCH: ndata["position"] = JSON::s("memorywatch"); break;
119 case PC_ONSCREEN: ndata["position"] = JSON::s("onscreen"); break;
121 ndata["cond_enable"] = JSON::b(cond_enable);
122 ndata["enabled"] = JSON::s(enabled);
123 ndata["onscreen_xpos"] = JSON::s(onscreen_xpos);
124 ndata["onscreen_ypos"] = JSON::s(onscreen_ypos);
125 ndata["onscreen_alt_origin_x"] = JSON::b(onscreen_alt_origin_x);
126 ndata["onscreen_alt_origin_y"] = JSON::b(onscreen_alt_origin_y);
127 ndata["onscreen_cliprange_x"] = JSON::b(onscreen_cliprange_x);
128 ndata["onscreen_cliprange_y"] = JSON::b(onscreen_cliprange_y);
129 ndata["onscreen_font"] = JSON::s(onscreen_font);
130 ndata["onscreen_fg_color"] = JSON::i(onscreen_fg_color);
131 ndata["onscreen_bg_color"] = JSON::i(onscreen_bg_color);
132 ndata["onscreen_halo_color"] = JSON::i(onscreen_halo_color);
133 return ndata;
136 void memwatch_printer::unserialize(const JSON::node& node)
138 std::string _position = json_string_default(node, "position", "");
139 if(_position == "disabled") position = PC_DISABLED;
140 else if(_position == "memorywatch") position = PC_MEMORYWATCH;
141 else if(_position == "onscreen") position = PC_ONSCREEN;
142 else position = PC_MEMORYWATCH;
143 cond_enable = json_boolean_default(node, "cond_enable", false);
144 enabled = json_string_default(node, "enabled", "");
145 onscreen_xpos = json_string_default(node, "onscreen_xpos", "");
146 onscreen_ypos = json_string_default(node, "onscreen_ypos", "");
147 onscreen_alt_origin_x = json_boolean_default(node, "onscreen_alt_origin_x", false);
148 onscreen_alt_origin_y = json_boolean_default(node, "onscreen_alt_origin_y", false);
149 onscreen_cliprange_x = json_boolean_default(node, "onscreen_cliprange_x", false);
150 onscreen_cliprange_y = json_boolean_default(node, "onscreen_cliprange_y", false);
151 onscreen_font = json_string_default(node, "onscreen_font", "");
152 onscreen_fg_color = json_signed_default(node, "onscreen_fg_color", false);
153 onscreen_bg_color = json_signed_default(node, "onscreen_bg_color", false);
154 onscreen_halo_color = json_signed_default(node, "onscreen_halo_color", false);
157 gcroot_pointer<memorywatch::item_printer> memwatch_printer::get_printer_obj(
158 std::function<gcroot_pointer<mathexpr::mathexpr>(const std::string& n)> vars)
160 gcroot_pointer<memorywatch::item_printer> ptr;
161 memorywatch::output_list* l;
162 memorywatch::output_fb* f;
164 std::string _enabled = (enabled != "") ? enabled : "true";
166 switch(position) {
167 case PC_DISABLED:
168 ptr = gcroot_pointer<memorywatch::item_printer>(new memorywatch::output_null);
169 break;
170 case PC_MEMORYWATCH:
171 ptr = gcroot_pointer<memorywatch::item_printer>(new memorywatch::output_list);
172 l = dynamic_cast<memorywatch::output_list*>(ptr.as_pointer());
173 l->cond_enable = cond_enable;
174 try {
175 if(l->cond_enable)
176 l->enabled = mathexpr::mathexpr::parse(*mathexpr::expression_value(), _enabled, vars);
177 else
178 l->enabled = mathexpr::mathexpr::parse(*mathexpr::expression_value(), "true", vars);
179 } catch(std::exception& e) {
180 (stringfmt() << "Error while parsing conditional: " << e.what()).throwex();
182 l->set_output(dummy_target_fn);
183 break;
184 case PC_ONSCREEN:
185 ptr = gcroot_pointer<memorywatch::item_printer>(new memorywatch::output_fb);
186 f = dynamic_cast<memorywatch::output_fb*>(ptr.as_pointer());
187 f->font = NULL;
188 f->set_dtor_cb([](memorywatch::output_fb& obj) { put_font(obj.font); });
189 f->cond_enable = cond_enable;
190 std::string while_parsing = "(unknown)";
191 try {
192 while_parsing = "conditional";
193 if(f->cond_enable)
194 f->enabled = mathexpr::mathexpr::parse(*mathexpr::expression_value(), _enabled, vars);
195 else
196 f->enabled = mathexpr::mathexpr::parse(*mathexpr::expression_value(), "true", vars);
197 while_parsing = "X position";
198 f->pos_x = mathexpr::mathexpr::parse(*mathexpr::expression_value(), onscreen_xpos, vars);
199 while_parsing = "Y position";
200 f->pos_y = mathexpr::mathexpr::parse(*mathexpr::expression_value(), onscreen_ypos, vars);
201 } catch(std::exception& e) {
202 (stringfmt() << "Error while parsing " << while_parsing << ": " << e.what()).throwex();
204 f->alt_origin_x = onscreen_alt_origin_x;
205 f->alt_origin_y = onscreen_alt_origin_y;
206 f->cliprange_x = onscreen_cliprange_x;
207 f->cliprange_y = onscreen_cliprange_y;
208 f->fg = onscreen_fg_color;
209 f->bg = onscreen_bg_color;
210 f->halo = onscreen_halo_color;
211 try {
212 f->font = get_font(onscreen_font);
213 } catch(std::exception& e) {
214 messages << "Bad font '" << onscreen_font << "': " << e.what() << std::endl;
215 f->font = &get_builtin_font2();
217 break;
219 return ptr;
222 memwatch_item::memwatch_item(memory_space& memory)
224 bytes = 0;
225 signed_flag = false;
226 float_flag = false;
227 endianess = 0;
228 scale_div = 1;
229 addr_base = 0;
230 addr_size = 0;
231 mspace = &memory;
234 JSON::node memwatch_item::serialize()
236 JSON::node ndata(JSON::object);
237 ndata["printer"] = printer.serialize();
238 ndata["expr"] = JSON::s(expr);
239 ndata["format"] = JSON::s(format);
240 ndata["bytes"] = JSON::u(bytes);
241 ndata["signed"] = JSON::b(signed_flag);
242 ndata["float"] = JSON::b(float_flag);
243 ndata["endianess"] = JSON::i(endianess);
244 ndata["scale_div"] = JSON::u(scale_div);
245 ndata["addr_base"] = JSON::u(addr_base);
246 ndata["addr_size"] = JSON::u(addr_size);
247 return ndata;
250 void memwatch_item::unserialize(const JSON::node& node)
252 if(node.type_of("printer") == JSON::object)
253 printer.unserialize(node["printer"]);
254 else
255 printer = memwatch_printer();
256 expr = json_string_default(node, "expr", "0");
257 format = json_string_default(node, "format", "");
258 bytes = json_unsigned_default(node, "bytes", 0);
259 signed_flag = json_boolean_default(node, "signed", false);
260 float_flag = json_boolean_default(node, "float", false);
261 endianess = json_signed_default(node, "endianess", false);
262 scale_div = json_unsigned_default(node, "scale_div", 1);
263 addr_base = json_unsigned_default(node, "addr_base", 0);
264 addr_size = json_unsigned_default(node, "addr_size", 0);
267 memorywatch::memread_oper* memwatch_item::get_memread_oper()
269 if(!bytes)
270 return NULL;
271 memorywatch::memread_oper* o = new memorywatch::memread_oper;
272 o->bytes = bytes;
273 o->signed_flag = signed_flag;
274 o->float_flag = float_flag;
275 o->endianess = endianess;
276 o->scale_div = scale_div;
277 o->addr_base = addr_base;
278 o->addr_size = addr_size;
279 o->mspace = mspace;
280 return o;
283 void memwatch_item::compatiblity_unserialize(memory_space& memory, const std::string& item)
285 regex_results r;
286 if(!(r = regex("C0x([0-9A-Fa-f]{1,16})z([bBwWoOdDqQfF])(H([0-9A-Ga-g]))?", item)))
287 throw std::runtime_error("Unknown compatiblity memory watch");
288 std::string _addr = r[1];
289 std::string _type = r[2];
290 std::string _hext = r[4];
291 uint64_t addr = strtoull(_addr.c_str(), NULL, 16);
292 char type = _type[0];
293 char hext = (_hext != "") ? _hext[0] : 0;
294 switch(type) {
295 case 'b': bytes = 1; signed_flag = true; float_flag = false; break;
296 case 'B': bytes = 1; signed_flag = false; float_flag = false; break;
297 case 'w': bytes = 2; signed_flag = true; float_flag = false; break;
298 case 'W': bytes = 2; signed_flag = false; float_flag = false; break;
299 case 'o': bytes = 3; signed_flag = true; float_flag = false; break;
300 case 'O': bytes = 3; signed_flag = false; float_flag = false; break;
301 case 'd': bytes = 4; signed_flag = true; float_flag = false; break;
302 case 'D': bytes = 4; signed_flag = false; float_flag = false; break;
303 case 'q': bytes = 8; signed_flag = true; float_flag = false; break;
304 case 'Q': bytes = 8; signed_flag = false; float_flag = false; break;
305 case 'f': bytes = 4; signed_flag = true; float_flag = true; break;
306 case 'F': bytes = 8; signed_flag = true; float_flag = true; break;
307 default: bytes = 0; break;
309 auto mdata = memory.lookup(addr);
310 if(mdata.first) {
311 addr = mdata.second;
312 addr_base = mdata.first->base;
313 addr_size = mdata.first->size;
314 endianess = mdata.first->endian;
315 } else {
316 addr_base = 0;
317 addr_size = 0;
318 endianess = -1;
320 if(hext) {
321 unsigned width;
322 if(hext >= '0' && hext <= '9')
323 width = hext - '0';
324 else
325 width = (hext & 0x1F) + 9;
326 format = (stringfmt() << "%0" << width << "x").str();
327 } else
328 format = "";
329 expr = (stringfmt() << "0x" << std::hex << addr).str();
330 scale_div = 1;
331 mspace = &memory;
332 printer.position = memwatch_printer::PC_MEMORYWATCH;
333 printer.cond_enable = false;
334 printer.enabled = "true";
335 printer.onscreen_xpos = "0";
336 printer.onscreen_ypos = "0";
337 printer.onscreen_alt_origin_x = false;
338 printer.onscreen_alt_origin_y = false;
339 printer.onscreen_cliprange_x = false;
340 printer.onscreen_cliprange_y = false;
341 printer.onscreen_font = "";
342 printer.onscreen_fg_color = 0xFFFFFF;
343 printer.onscreen_bg_color = -1;
344 printer.onscreen_halo_color = 0;
347 memwatch_set::memwatch_set(memory_space& _memory, project_state& _project, emu_framebuffer& _fbuf)
348 : memory(_memory), project(_project), fbuf(_fbuf)
352 std::set<std::string> memwatch_set::enumerate()
354 std::set<std::string> r;
355 for(auto& i : items)
356 r.insert(i.first);
357 return r;
360 void memwatch_set::clear(const std::string& name)
362 std::map<std::string, memwatch_item> nitems = items;
363 nitems.erase(name);
364 rebuild(nitems);
365 std::swap(items, nitems);
366 auto pr = project.get();
367 if(pr) {
368 pr->watches.erase(name);
369 pr->flush();
371 fbuf.redraw_framebuffer();
374 void memwatch_set::set(const std::string& name, const std::string& item)
376 memwatch_item _item(memory);
377 if(item != "" && item[0] != '{') {
378 //Compatiblity.
379 try {
380 _item.compatiblity_unserialize(memory, item);
381 } catch(std::exception& e) {
382 messages << "Can't handle old memory watch '" << name << "'" << std::endl;
383 return;
385 } else
386 _item.unserialize(JSON::node(item));
387 set(name, _item);
390 memwatch_item& memwatch_set::get(const std::string& name)
392 if(!items.count(name))
393 throw std::runtime_error("No such memory watch named '" + name + "'");
394 return items.find(name)->second;
397 std::string memwatch_set::get_string(const std::string& name, JSON::printer* printer)
399 auto& x = get(name);
400 auto y = x.serialize();
401 auto z = y.serialize(printer);
402 return z;
405 void memwatch_set::watch(struct framebuffer::queue& rq)
407 //Set framebuffer for all FB watches.
408 watch_set.foreach([&rq](memorywatch::item& i) {
409 memorywatch::output_fb* fb = dynamic_cast<memorywatch::output_fb*>(i.printer.as_pointer());
410 if(fb)
411 fb->set_rqueue(rq);
413 watch_set.refresh();
414 erase_unused_watches();
417 bool memwatch_set::rename(const std::string& oldname, const std::string& newname)
419 std::map<std::string, memwatch_item> nitems = items;
420 if(nitems.count(newname))
421 return false;
422 if(!nitems.count(oldname))
423 return false;
424 nitems.insert(std::make_pair(newname, nitems.find(oldname)->second));
425 nitems.erase(oldname);
426 rebuild(nitems);
427 std::swap(items, nitems);
428 fbuf.redraw_framebuffer();
429 return true;
432 void memwatch_set::set(const std::string& name, memwatch_item& item)
434 std::map<std::string, memwatch_item> nitems = items;
435 nitems.insert(std::make_pair(name, item));
436 rebuild(nitems);
437 std::swap(items, nitems);
438 auto pr = project.get();
439 if(pr) {
440 pr->watches[name] = get_string(name);
441 pr->flush();
443 fbuf.redraw_framebuffer();
446 std::string memwatch_set::get_value(const std::string& name)
448 return watch_set.get(name).get_value();
451 void memwatch_set::set_multi(std::list<std::pair<std::string, memwatch_item>>& list)
453 std::map<std::string, memwatch_item> nitems = items;
454 for(auto& i : list)
455 nitems.insert(i);
456 rebuild(nitems);
457 std::swap(items, nitems);
458 auto pr = project.get();
459 if(pr) {
460 for(auto& i : list)
461 pr->watches[i.first] = get_string(i.first);
462 pr->flush();
464 fbuf.redraw_framebuffer();
467 void memwatch_set::set_multi(std::list<std::pair<std::string, std::string>>& list)
469 std::list<std::pair<std::string, memwatch_item>> _list;
470 for(auto& i: list) {
471 memwatch_item it(memory);
472 it.unserialize(JSON::node(i.second));
473 _list.push_back(std::make_pair(i.first, it));
475 set_multi(_list);
478 void memwatch_set::clear_multi(const std::set<std::string>& names)
480 std::map<std::string, memwatch_item> nitems = items;
481 for(auto& i : names)
482 nitems.erase(i);
483 rebuild(nitems);
484 std::swap(items, nitems);
485 auto pr = project.get();
486 if(pr) {
487 for(auto& i : names)
488 pr->watches.erase(i);
489 pr->flush();
491 fbuf.redraw_framebuffer();
494 void memwatch_set::rebuild(std::map<std::string, memwatch_item>& nitems)
497 memorywatch::set new_set;
498 std::map<std::string, gcroot_pointer<mathexpr::mathexpr>> vars;
499 auto vars_fn = [&vars](const std::string& n) -> gcroot_pointer<mathexpr::mathexpr> {
500 if(!vars.count(n))
501 vars[n] = gcroot_pointer<mathexpr::mathexpr>(gcroot_pointer_object_tag(),
502 mathexpr::expression_value());
503 return vars[n];
505 for(auto& i : nitems) {
506 mathexpr::operinfo* memread_oper = i.second.get_memread_oper();
507 try {
508 gcroot_pointer<mathexpr::mathexpr> rt_expr;
509 gcroot_pointer<memorywatch::item_printer> rt_printer;
510 std::vector<gcroot_pointer<mathexpr::mathexpr>> v;
511 try {
512 rt_expr = mathexpr::mathexpr::parse(*mathexpr::expression_value(),
513 i.second.expr, vars_fn);
514 } catch(std::exception& e) {
515 (stringfmt() << "Error while parsing address/expression: "
516 << e.what()).throwex();
518 v.push_back(rt_expr);
519 if(memread_oper) {
520 rt_expr = gcroot_pointer<mathexpr::mathexpr>(gcroot_pointer_object_tag(),
521 mathexpr::expression_value(), memread_oper, v, true);
522 memread_oper = NULL;
524 rt_printer = i.second.printer.get_printer_obj(vars_fn);
526 //Set final callback for list objects (since it wasn't known on creation).
527 auto list_obj = dynamic_cast<memorywatch::output_list*>(rt_printer.as_pointer());
528 if(list_obj)
529 list_obj->set_output([this](const std::string& n, const std::string& v) {
530 this->watch_output(n, v);
533 memorywatch::item it(*mathexpr::expression_value());
534 *vars_fn(i.first) = *rt_expr;
535 it.expr = vars_fn(i.first);
536 it.printer = rt_printer;
537 it.format = i.second.format;
538 new_set.create(i.first, it);
539 } catch(...) {
540 delete memread_oper;
541 throw;
544 watch_set.swap(new_set);
546 garbage_collectable::do_gc();
549 void memwatch_set::watch_output(const std::string& name, const std::string& value)
551 used_memorywatches[name] = true;
552 window_vars[name] = utf8::to32(value);
555 void memwatch_set::erase_unused_watches()
557 for(auto& i : used_memorywatches) {
558 if(!i.second)
559 window_vars.erase(i.first);
560 i.second = false;