1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/framebuffer.hpp"
4 #include "core/memorymanip.hpp"
5 #include "core/memorywatch.hpp"
6 #include "core/project.hpp"
7 #include "core/window.hpp"
8 #include "fonts/wrapper.hpp"
9 #include "library/directory.hpp"
10 #include "library/string.hpp"
11 #include "library/int24.hpp"
12 #include "library/mathexpr-ntype.hpp"
13 #include "library/memorywatch.hpp"
14 #include "library/memorywatch-list.hpp"
15 #include "library/memorywatch-fb.hpp"
16 #include "library/memorywatch-null.hpp"
29 std::map
<std::string
, std::pair
<framebuffer::font2
*, size_t>> fonts_in_use
;
30 std::map
<std::string
, std::u32string
> memorywatch_vars
;
32 framebuffer::font2
& get_builtin_font2()
34 static framebuffer::font2
f(main_font
);
38 framebuffer::font2
* get_font(const std::string filename
)
42 return &get_builtin_font2();
43 std::string abs_filename
= get_absolute_path(filename
);
44 if(fonts_in_use
.count(abs_filename
)) {
45 fonts_in_use
[abs_filename
].second
++;
46 return fonts_in_use
[abs_filename
].first
;
48 framebuffer::font2
* f
= new framebuffer::font2(abs_filename
);
50 fonts_in_use
[abs_filename
] = std::make_pair(f
, 1);
57 void put_font(framebuffer::font2
* font
)
59 //Handle NULL font (always there).
62 //Find font using this.
64 for(auto& i
: fonts_in_use
)
65 if(i
.second
.first
== font
)
69 fonts_in_use
[filename
].second
--;
70 if(!fonts_in_use
[filename
].second
) {
71 delete fonts_in_use
[filename
].first
;
72 fonts_in_use
.erase(filename
);
76 std::string
json_string_default(const JSON::node
& node
, const std::string
& pointer
, const std::string
& dflt
)
78 return (node
.type_of(pointer
) == JSON::string
) ? node
[pointer
].as_string8() : dflt
;
81 uint64_t json_unsigned_default(const JSON::node
& node
, const std::string
& pointer
, uint64_t dflt
)
83 return (node
.type_of(pointer
) == JSON::number
) ? node
[pointer
].as_uint() : dflt
;
86 int64_t json_signed_default(const JSON::node
& node
, const std::string
& pointer
, int64_t dflt
)
88 return (node
.type_of(pointer
) == JSON::number
) ? node
[pointer
].as_int() : dflt
;
91 bool json_boolean_default(const JSON::node
& node
, const std::string
& pointer
, bool dflt
)
93 return (node
.type_of(pointer
) == JSON::boolean
) ? node
[pointer
].as_bool() : dflt
;
96 void dummy_target_fn(const std::string
& n
, const std::string
& v
) {}
99 lsnes_memorywatch_printer::lsnes_memorywatch_printer()
101 position
= PC_MEMORYWATCH
;
103 onscreen_alt_origin_x
= false;
104 onscreen_alt_origin_y
= false;
105 onscreen_cliprange_x
= false;
106 onscreen_cliprange_y
= false;
107 onscreen_fg_color
= 0xFFFFFF;
108 onscreen_bg_color
= -1;
109 onscreen_halo_color
= 0;
112 JSON::node
lsnes_memorywatch_printer::serialize()
114 JSON::node
ndata(JSON::object
);
116 case PC_DISABLED
: ndata
["position"] = JSON::s("disabled"); break;
117 case PC_MEMORYWATCH
: ndata
["position"] = JSON::s("memorywatch"); break;
118 case PC_ONSCREEN
: ndata
["position"] = JSON::s("onscreen"); break;
120 ndata
["cond_enable"] = JSON::b(cond_enable
);
121 ndata
["enabled"] = JSON::s(enabled
);
122 ndata
["onscreen_xpos"] = JSON::s(onscreen_xpos
);
123 ndata
["onscreen_ypos"] = JSON::s(onscreen_ypos
);
124 ndata
["onscreen_alt_origin_x"] = JSON::b(onscreen_alt_origin_x
);
125 ndata
["onscreen_alt_origin_y"] = JSON::b(onscreen_alt_origin_y
);
126 ndata
["onscreen_cliprange_x"] = JSON::b(onscreen_cliprange_x
);
127 ndata
["onscreen_cliprange_y"] = JSON::b(onscreen_cliprange_y
);
128 ndata
["onscreen_font"] = JSON::s(onscreen_font
);
129 ndata
["onscreen_fg_color"] = JSON::i(onscreen_fg_color
);
130 ndata
["onscreen_bg_color"] = JSON::i(onscreen_bg_color
);
131 ndata
["onscreen_halo_color"] = JSON::i(onscreen_halo_color
);
135 void lsnes_memorywatch_printer::unserialize(const JSON::node
& node
)
137 std::string _position
= json_string_default(node
, "position", "");
138 if(_position
== "disabled") position
= PC_DISABLED
;
139 else if(_position
== "memorywatch") position
= PC_MEMORYWATCH
;
140 else if(_position
== "onscreen") position
= PC_ONSCREEN
;
141 else position
= PC_MEMORYWATCH
;
142 cond_enable
= json_boolean_default(node
, "cond_enable", false);
143 enabled
= json_string_default(node
, "enabled", "");
144 onscreen_xpos
= json_string_default(node
, "onscreen_xpos", "");
145 onscreen_ypos
= json_string_default(node
, "onscreen_ypos", "");
146 onscreen_alt_origin_x
= json_boolean_default(node
, "onscreen_alt_origin_x", false);
147 onscreen_alt_origin_y
= json_boolean_default(node
, "onscreen_alt_origin_y", false);
148 onscreen_cliprange_x
= json_boolean_default(node
, "onscreen_cliprange_x", false);
149 onscreen_cliprange_y
= json_boolean_default(node
, "onscreen_cliprange_y", false);
150 onscreen_font
= json_string_default(node
, "onscreen_font", "");
151 onscreen_fg_color
= json_signed_default(node
, "onscreen_fg_color", false);
152 onscreen_bg_color
= json_signed_default(node
, "onscreen_bg_color", false);
153 onscreen_halo_color
= json_signed_default(node
, "onscreen_halo_color", false);
156 gcroot_pointer
<memorywatch_item_printer
> lsnes_memorywatch_printer::get_printer_obj(
157 std::function
<gcroot_pointer
<mathexpr
>(const std::string
& n
)> vars
)
159 gcroot_pointer
<memorywatch_item_printer
> ptr
;
160 memorywatch_output_list
* l
;
161 memorywatch_output_fb
* f
;
163 std::string _enabled
= (enabled
!= "") ? enabled
: "true";
167 ptr
= gcroot_pointer
<memorywatch_item_printer
>(new memorywatch_output_null
);
170 ptr
= gcroot_pointer
<memorywatch_item_printer
>(new memorywatch_output_list
);
171 l
= dynamic_cast<memorywatch_output_list
*>(ptr
.as_pointer());
172 l
->cond_enable
= cond_enable
;
175 l
->enabled
= mathexpr::parse(*expression_value(), _enabled
, vars
);
177 l
->enabled
= mathexpr::parse(*expression_value(), "true", vars
);
178 } catch(std::exception
& e
) {
179 (stringfmt() << "Error while parsing conditional: " << e
.what()).throwex();
181 l
->set_output(dummy_target_fn
);
184 ptr
= gcroot_pointer
<memorywatch_item_printer
>(new memorywatch_output_fb
);
185 f
= dynamic_cast<memorywatch_output_fb
*>(ptr
.as_pointer());
187 f
->set_dtor_cb([](memorywatch_output_fb
& obj
) { put_font(obj
.font
); });
188 f
->cond_enable
= cond_enable
;
189 std::string while_parsing
= "(unknown)";
191 while_parsing
= "conditional";
193 f
->enabled
= mathexpr::parse(*expression_value(), _enabled
, vars
);
195 f
->enabled
= mathexpr::parse(*expression_value(), "true", vars
);
196 while_parsing
= "X position";
197 f
->pos_x
= mathexpr::parse(*expression_value(), onscreen_xpos
, vars
);
198 while_parsing
= "Y position";
199 f
->pos_y
= mathexpr::parse(*expression_value(), onscreen_ypos
, vars
);
200 } catch(std::exception
& e
) {
201 (stringfmt() << "Error while parsing " << while_parsing
<< ": " << e
.what()).throwex();
203 f
->alt_origin_x
= onscreen_alt_origin_x
;
204 f
->alt_origin_y
= onscreen_alt_origin_y
;
205 f
->cliprange_x
= onscreen_cliprange_x
;
206 f
->cliprange_y
= onscreen_cliprange_y
;
207 f
->fg
= onscreen_fg_color
;
208 f
->bg
= onscreen_bg_color
;
209 f
->halo
= onscreen_halo_color
;
211 f
->font
= get_font(onscreen_font
);
212 } catch(std::exception
& e
) {
213 messages
<< "Bad font '" << onscreen_font
<< "': " << e
.what() << std::endl
;
214 f
->font
= &get_builtin_font2();
221 lsnes_memorywatch_item::lsnes_memorywatch_item()
230 mspace
= &lsnes_memory
;
233 JSON::node
lsnes_memorywatch_item::serialize()
235 JSON::node
ndata(JSON::object
);
236 ndata
["printer"] = printer
.serialize();
237 ndata
["expr"] = JSON::s(expr
);
238 ndata
["format"] = JSON::s(format
);
239 ndata
["bytes"] = JSON::u(bytes
);
240 ndata
["signed"] = JSON::b(signed_flag
);
241 ndata
["float"] = JSON::b(float_flag
);
242 ndata
["endianess"] = JSON::i(endianess
);
243 ndata
["scale_div"] = JSON::u(scale_div
);
244 ndata
["addr_base"] = JSON::u(addr_base
);
245 ndata
["addr_size"] = JSON::u(addr_size
);
249 void lsnes_memorywatch_item::unserialize(const JSON::node
& node
)
251 if(node
.type_of("printer") == JSON::object
)
252 printer
.unserialize(node
["printer"]);
254 printer
= lsnes_memorywatch_printer();
255 expr
= json_string_default(node
, "expr", "0");
256 format
= json_string_default(node
, "format", "");
257 bytes
= json_unsigned_default(node
, "bytes", 0);
258 signed_flag
= json_boolean_default(node
, "signed", false);
259 float_flag
= json_boolean_default(node
, "float", false);
260 endianess
= json_signed_default(node
, "endianess", false);
261 scale_div
= json_unsigned_default(node
, "scale_div", 1);
262 addr_base
= json_unsigned_default(node
, "addr_base", 0);
263 addr_size
= json_unsigned_default(node
, "addr_size", 0);
266 memorywatch_memread_oper
* lsnes_memorywatch_item::get_memread_oper()
270 memorywatch_memread_oper
* o
= new memorywatch_memread_oper
;
272 o
->signed_flag
= signed_flag
;
273 o
->float_flag
= float_flag
;
274 o
->endianess
= endianess
;
275 o
->scale_div
= scale_div
;
276 o
->addr_base
= addr_base
;
277 o
->addr_size
= addr_size
;
282 void lsnes_memorywatch_item::compatiblity_unserialize(const std::string
& item
)
285 if(!(r
= regex("C0x([0-9A-Fa-f]{1,16})z([bBwWoOdDqQfF])(H([0-9A-Ga-g]))?", item
)))
286 throw std::runtime_error("Unknown compatiblity memory watch");
287 std::string _addr
= r
[1];
288 std::string _type
= r
[2];
289 std::string _hext
= r
[4];
290 uint64_t addr
= strtoull(_addr
.c_str(), NULL
, 16);
291 char type
= _type
[0];
292 char hext
= (_hext
!= "") ? _hext
[0] : 0;
294 case 'b': bytes
= 1; signed_flag
= true; float_flag
= false; break;
295 case 'B': bytes
= 1; signed_flag
= false; float_flag
= false; break;
296 case 'w': bytes
= 2; signed_flag
= true; float_flag
= false; break;
297 case 'W': bytes
= 2; signed_flag
= false; float_flag
= false; break;
298 case 'o': bytes
= 3; signed_flag
= true; float_flag
= false; break;
299 case 'O': bytes
= 3; signed_flag
= false; float_flag
= false; break;
300 case 'd': bytes
= 4; signed_flag
= true; float_flag
= false; break;
301 case 'D': bytes
= 4; signed_flag
= false; float_flag
= false; break;
302 case 'q': bytes
= 8; signed_flag
= true; float_flag
= false; break;
303 case 'Q': bytes
= 8; signed_flag
= false; float_flag
= false; break;
304 case 'f': bytes
= 4; signed_flag
= true; float_flag
= true; break;
305 case 'F': bytes
= 8; signed_flag
= true; float_flag
= true; break;
306 default: bytes
= 0; break;
308 auto mdata
= lsnes_memory
.lookup(addr
);
311 addr_base
= mdata
.first
->base
;
312 addr_size
= mdata
.first
->size
;
313 endianess
= mdata
.first
->endian
;
321 if(hext
>= '0' && hext
<= '9')
324 width
= (hext
& 0x1F) + 9;
325 format
= (stringfmt() << "%0" << width
<< "x").str();
328 expr
= (stringfmt() << "0x" << std::hex
<< addr
).str();
330 mspace
= &lsnes_memory
;
331 printer
.position
= lsnes_memorywatch_printer::PC_MEMORYWATCH
;
332 printer
.cond_enable
= false;
333 printer
.enabled
= "true";
334 printer
.onscreen_xpos
= "0";
335 printer
.onscreen_ypos
= "0";
336 printer
.onscreen_alt_origin_x
= false;
337 printer
.onscreen_alt_origin_y
= false;
338 printer
.onscreen_cliprange_x
= false;
339 printer
.onscreen_cliprange_y
= false;
340 printer
.onscreen_font
= "";
341 printer
.onscreen_fg_color
= 0xFFFFFF;
342 printer
.onscreen_bg_color
= -1;
343 printer
.onscreen_halo_color
= 0;
346 std::set
<std::string
> lsnes_memorywatch_set::enumerate()
348 std::set
<std::string
> r
;
354 void lsnes_memorywatch_set::clear(const std::string
& name
)
356 std::map
<std::string
, lsnes_memorywatch_item
> nitems
= items
;
359 std::swap(items
, nitems
);
360 auto pr
= project_get();
362 pr
->watches
.erase(name
);
365 redraw_framebuffer();
368 void lsnes_memorywatch_set::set(const std::string
& name
, const std::string
& item
)
370 lsnes_memorywatch_item _item
;
371 if(item
!= "" && item
[0] != '{') {
374 _item
.compatiblity_unserialize(item
);
375 } catch(std::exception
& e
) {
376 messages
<< "Can't handle old memory watch '" << name
<< "'" << std::endl
;
380 _item
.unserialize(JSON::node(item
));
384 lsnes_memorywatch_item
& lsnes_memorywatch_set::get(const std::string
& name
)
386 if(!items
.count(name
))
387 throw std::runtime_error("No such memory watch named '" + name
+ "'");
391 std::string
lsnes_memorywatch_set::get_string(const std::string
& name
, JSON::printer
* printer
)
394 auto y
= x
.serialize();
395 auto z
= y
.serialize(printer
);
399 void lsnes_memorywatch_set::watch(struct framebuffer::queue
& rq
)
401 //Set framebuffer for all FB watches.
402 watch_set
.foreach([&rq
](memorywatch_item
& i
) {
403 memorywatch_output_fb
* fb
= dynamic_cast<memorywatch_output_fb
*>(i
.printer
.as_pointer());
408 erase_unused_watches();
411 bool lsnes_memorywatch_set::rename(const std::string
& oldname
, const std::string
& newname
)
413 std::map
<std::string
, lsnes_memorywatch_item
> nitems
= items
;
414 if(nitems
.count(newname
))
416 if(!nitems
.count(oldname
))
418 nitems
[newname
] = nitems
[oldname
];
419 nitems
.erase(oldname
);
421 std::swap(items
, nitems
);
422 redraw_framebuffer();
426 void lsnes_memorywatch_set::set(const std::string
& name
, lsnes_memorywatch_item
& item
)
428 std::map
<std::string
, lsnes_memorywatch_item
> nitems
= items
;
431 std::swap(items
, nitems
);
432 auto pr
= project_get();
434 pr
->watches
[name
] = get_string(name
);
437 redraw_framebuffer();
440 std::string
lsnes_memorywatch_set::get_value(const std::string
& name
)
442 return watch_set
.get(name
).get_value();
445 void lsnes_memorywatch_set::set_multi(std::list
<std::pair
<std::string
, lsnes_memorywatch_item
>>& list
)
447 std::map
<std::string
, lsnes_memorywatch_item
> nitems
= items
;
449 nitems
[i
.first
] = i
.second
;
451 std::swap(items
, nitems
);
452 auto pr
= project_get();
455 pr
->watches
[i
.first
] = get_string(i
.first
);
458 redraw_framebuffer();
461 void lsnes_memorywatch_set::set_multi(std::list
<std::pair
<std::string
, std::string
>>& list
)
463 std::list
<std::pair
<std::string
, lsnes_memorywatch_item
>> _list
;
465 lsnes_memorywatch_item it
;
466 it
.unserialize(JSON::node(i
.second
));
467 _list
.push_back(std::make_pair(i
.first
, it
));
472 void lsnes_memorywatch_set::clear_multi(const std::set
<std::string
>& names
)
474 std::map
<std::string
, lsnes_memorywatch_item
> nitems
= items
;
478 std::swap(items
, nitems
);
479 auto pr
= project_get();
482 pr
->watches
.erase(i
);
485 redraw_framebuffer();
488 void lsnes_memorywatch_set::rebuild(std::map
<std::string
, lsnes_memorywatch_item
>& nitems
)
491 memorywatch_set new_set
;
492 std::map
<std::string
, gcroot_pointer
<mathexpr
>> vars
;
493 auto vars_fn
= [&vars
](const std::string
& n
) -> gcroot_pointer
<mathexpr
> {
495 vars
[n
] = gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(),
499 for(auto& i
: nitems
) {
500 mathexpr_operinfo
* memread_oper
= i
.second
.get_memread_oper();
502 gcroot_pointer
<mathexpr
> rt_expr
;
503 gcroot_pointer
<memorywatch_item_printer
> rt_printer
;
504 std::vector
<gcroot_pointer
<mathexpr
>> v
;
506 rt_expr
= mathexpr::parse(*expression_value(), i
.second
.expr
, vars_fn
);
507 } catch(std::exception
& e
) {
508 (stringfmt() << "Error while parsing address/expression: "
509 << e
.what()).throwex();
511 v
.push_back(rt_expr
);
513 rt_expr
= gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(),
514 expression_value(), memread_oper
, v
, true);
517 rt_printer
= i
.second
.printer
.get_printer_obj(vars_fn
);
519 //Set final callback for list objects (since it wasn't known on creation).
520 auto list_obj
= dynamic_cast<memorywatch_output_list
*>(rt_printer
.as_pointer());
522 list_obj
->set_output([this](const std::string
& n
, const std::string
& v
) {
523 this->memorywatch_output(n
, v
);
526 memorywatch_item
it(*expression_value());
527 *vars_fn(i
.first
) = *rt_expr
;
528 it
.expr
= vars_fn(i
.first
);
529 it
.printer
= rt_printer
;
530 it
.format
= i
.second
.format
;
531 new_set
.create(i
.first
, it
);
537 watch_set
.swap(new_set
);
539 garbage_collectable::do_gc();
542 void lsnes_memorywatch_set::memorywatch_output(const std::string
& name
, const std::string
& value
)
544 used_memorywatches
[name
] = true;
545 window_vars
[name
] = utf8::to32(value
);
548 void lsnes_memorywatch_set::erase_unused_watches()
550 for(auto& i
: used_memorywatches
) {
552 window_vars
.erase(i
.first
);
557 lsnes_memorywatch_set lsnes_memorywatch
;