Allow memory watch to read registers
[lsnes.git] / src / platform / wxwidgets / editor-hexedit.cpp
blob91897d36576ef654f1349934c14e5cf371df6f36
1 #include "core/moviedata.hpp"
2 #include "core/memorywatch.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/instance.hpp"
5 #include "core/instance-map.hpp"
6 #include "core/project.hpp"
7 #include "core/memorymanip.hpp"
8 #include "core/ui-services.hpp"
9 #include "library/memorysearch.hpp"
10 #include "library/hex.hpp"
12 #include "platform/wxwidgets/platform.hpp"
13 #include "platform/wxwidgets/textrender.hpp"
14 #include "platform/wxwidgets/loadsave.hpp"
15 #include "platform/wxwidgets/scrollbar.hpp"
17 #include <wx/wx.h>
18 #include <wx/event.h>
19 #include <wx/control.h>
20 #include <wx/combobox.h>
21 #include <wx/radiobut.h>
23 #include "library/string.hpp"
24 #include "library/json.hpp"
25 #include "library/zip.hpp"
26 #include "interface/romtype.hpp"
28 #include <iomanip>
30 class wxeditor_hexedit;
32 namespace
34 const size_t maxvaluelen = 8; //The length of longest value type.
35 instance_map<wxeditor_hexedit> editor;
37 struct val_type
39 const char* name;
40 unsigned len;
41 bool hard_bigendian;
42 const char* format;
43 int type; //0 => Unsigned, 1 => Signed, 2 => Float
44 int scale;
45 std::string (*read)(const uint8_t* x);
48 val_type datatypes[] = {
49 {"1 byte (signed)", 1, false, "", 1, 0, [](const uint8_t* x) -> std::string {
50 return (stringfmt() << (int)(char)x[0]).str();
51 }},
52 {"1 byte (unsigned)", 1, false, "", 0, 0, [](const uint8_t* x) -> std::string {
53 return (stringfmt() << (int)x[0]).str();
54 }},
55 {"1 byte (hex)", 1, false, "%02x", 0, 0, [](const uint8_t* x) -> std::string {
56 return hex::to(x[0]);
57 }},
58 {"2 bytes (signed)", 2, false, "", 1, 0, [](const uint8_t* x) -> std::string {
59 return (stringfmt() << *(int16_t*)x).str();
60 }},
61 {"2 bytes (unsigned)", 2, false, "", 0, 0, [](const uint8_t* x) -> std::string {
62 return (stringfmt() << *(uint16_t*)x).str();
63 }},
64 {"2 bytes (hex)", 2, false, "%04x", 0, 0, [](const uint8_t* x) -> std::string {
65 return hex::to(*(uint16_t*)x);
66 }},
67 {"3 bytes (signed)", 3, true, "", 1, 0, [](const uint8_t* x) -> std::string {
68 int32_t a = 0;
69 a |= (uint32_t)x[0] << 16;
70 a |= (uint32_t)x[1] << 8;
71 a |= (uint32_t)x[2];
72 if(a & 0x800000)
73 a -= 0x1000000;
74 return (stringfmt() << a).str();
75 }},
76 {"3 bytes (unsigned)", 3, true, "", 0, 0, [](const uint8_t* x) -> std::string {
77 int32_t a = 0;
78 a |= (uint32_t)x[0] << 16;
79 a |= (uint32_t)x[1] << 8;
80 a |= (uint32_t)x[2];
81 return (stringfmt() << a).str();
82 }},
83 {"3 bytes (hex)", 3, true, "%06x", 0, 0, [](const uint8_t* x) -> std::string {
84 int32_t a = 0;
85 a |= (uint32_t)x[0] << 16;
86 a |= (uint32_t)x[1] << 8;
87 a |= (uint32_t)x[2];
88 return hex::to24(a);
89 }},
90 {"4 bytes (signed)", 4, false, "", 1, 0, [](const uint8_t* x) -> std::string {
91 return (stringfmt() << *(int32_t*)x).str();
92 }},
93 {"4 bytes (unsigned)", 4, false, "", 0, 0, [](const uint8_t* x) -> std::string {
94 return (stringfmt() << *(uint32_t*)x).str();
95 }},
96 {"4 bytes (hex)", 4, false, "%08x", 0, 0, [](const uint8_t* x) -> std::string {
97 return hex::to(*(uint32_t*)x);
98 }},
99 {"4 bytes (float)", 4, false, "", 2, 0, [](const uint8_t* x) -> std::string {
100 return (stringfmt() << *(float*)x).str();
102 {"8 bytes (signed)", 8, false, "", 1, 0, [](const uint8_t* x) -> std::string {
103 return (stringfmt() << *(int64_t*)x).str();
105 {"8 bytes (unsigned)", 8, false, "", 0, 0, [](const uint8_t* x) -> std::string {
106 return (stringfmt() << *(uint64_t*)x).str();
108 {"8 bytes (hex)", 8, false, "%016x", 0, 0, [](const uint8_t* x) -> std::string {
109 return hex::to(*(uint64_t*)x);
111 {"8 bytes (float)", 8, false, "", 2, 0, [](const uint8_t* x) -> std::string {
112 return (stringfmt() << *(double*)x).str();
114 {"Q8.8 (signed)", 2, false, "", 1, 8, [](const uint8_t* x) -> std::string {
115 return (stringfmt() << *(int16_t*)x / 256.0).str();
117 {"Q8.8 (unsigned)", 2, false, "", 0, 8, [](const uint8_t* x) -> std::string {
118 return (stringfmt() << *(uint16_t*)x / 256.0).str();
120 {"Q12.4 (signed)", 2, false, "", 1, 4, [](const uint8_t* x) -> std::string {
121 return (stringfmt() << *(int16_t*)x / 16.0).str();
123 {"Q12.4 (unsigned)", 2, false, "", 0, 4, [](const uint8_t* x) -> std::string {
124 return (stringfmt() << *(uint16_t*)x / 16.0).str();
126 {"Q16.8 (signed)", 3, true, "", 1, 8, [](const uint8_t* x) -> std::string {
127 int32_t a = 0;
128 a |= (uint32_t)x[0] << 16;
129 a |= (uint32_t)x[1] << 8;
130 a |= (uint32_t)x[2];
131 if(a & 0x800000)
132 a -= 0x1000000;
133 return (stringfmt() << a / 256.0).str();
135 {"Q16.8 (unsigned)", 3, true, "", 0, 8, [](const uint8_t* x) -> std::string {
136 int32_t a = 0;
137 a |= (uint32_t)x[0] << 16;
138 a |= (uint32_t)x[1] << 8;
139 a |= (uint32_t)x[2];
140 return (stringfmt() << a / 256.0).str();
142 {"Q24.8 (signed)", 4, false, "", 1, 8, [](const uint8_t* x) -> std::string {
143 return (stringfmt() << *(int32_t*)x / 256.0).str();
145 {"Q24.8 (unsigned)", 4, false, "", 0, 8, [](const uint8_t* x) -> std::string {
146 return (stringfmt() << *(uint32_t*)x / 256.0).str();
148 {"Q20.12 (signed)", 4, false, "", 1, 12, [](const uint8_t* x) -> std::string {
149 return (stringfmt() << *(int32_t*)x / 4096.0).str();
151 {"Q20.12 (unsigned)", 4, false, "", 0, 12, [](const uint8_t* x) -> std::string {
152 return (stringfmt() << *(uint32_t*)x / 4096.0).str();
154 {"Q16.16 (signed)", 4, false, "", 1, 16, [](const uint8_t* x) -> std::string {
155 return (stringfmt() << *(int32_t*)x / 65536.0).str();
157 {"Q16.16 (unsigned)", 4, false, "", 0, 16, [](const uint8_t* x) -> std::string {
158 return (stringfmt() << *(uint32_t*)x / 65536.0).str();
162 unsigned hexaddr = 6;
163 int separators[5] = {6, 15, 24, 28, 42};
164 const char32_t* sepchars[5] = {U"\u2502", U" ", U".", U" ", U"\u2502"};
165 int hexcol[16] = {7, 9, 11, 13, 16, 18, 20, 22, 25, 27, 29, 31, 34, 36, 38, 40};
166 int charcol[16] = {43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58};
167 const char32_t* hexes[16] = {U"0", U"1", U"2", U"3", U"4", U"5", U"6", U"7", U"8", U"9", U"A", U"B", U"C",
168 U"D", U"E", U"F"};
170 enum {
171 wxID_OPPOSITE_ENDIAN = wxID_HIGHEST + 1,
172 wxID_DATATYPES_FIRST,
173 wxID_DATATYPES_LAST = wxID_DATATYPES_FIRST + 255,
174 wxID_REGIONS_FIRST,
175 wxID_REGIONS_LAST = wxID_REGIONS_FIRST + 255,
176 wxID_ADD_BOOKMARK,
177 wxID_DELETE_BOOKMARK,
178 wxID_LOAD_BOOKMARKS,
179 wxID_SAVE_BOOKMARKS,
180 wxID_BOOKMARKS_FIRST,
181 wxID_BOOKMARKS_LAST = wxID_BOOKMARKS_FIRST + 255,
182 wxID_SEARCH_DISQUALIFY,
183 wxID_SEARCH_PREV,
184 wxID_SEARCH_NEXT,
185 wxID_SEARCH_WATCH,
189 class wxeditor_hexedit : public wxFrame
191 public:
192 wxeditor_hexedit(emulator_instance& _inst, wxWindow* parent)
193 : wxFrame(parent, wxID_ANY, wxT("lsnes: Memory editor"), wxDefaultPosition, wxSize(-1, -1),
194 wxCAPTION | wxMINIMIZE_BOX | wxCLOSE_BOX | wxSYSTEM_MENU), inst(_inst)
196 Centre();
197 wxBoxSizer* top = new wxBoxSizer(wxVERTICAL);
198 SetSizer(top);
200 destructing = false;
201 hex_input_state = -1;
202 current_vma = 0;
204 Connect(wxEVT_CHAR, wxKeyEventHandler(wxeditor_hexedit::on_keyboard), NULL, this);
206 wxBoxSizer* parea = new wxBoxSizer(wxHORIZONTAL);
207 parea->Add(hpanel = new _panel(this, inst), 1, wxGROW);
208 hpanel->SetFocus();
209 parea->Add(scroll = new scroll_bar(this, wxID_ANY, true), 0, wxGROW);
210 top->Add(parea, 1, wxGROW);
211 scroll->Connect(wxEVT_CHAR, wxKeyEventHandler(wxeditor_hexedit::on_keyboard), NULL, this);
213 SetStatusBar(statusbar = new wxStatusBar(this));
214 SetMenuBar(menubar = new wxMenuBar);
216 valuemenu = new wxMenu();
217 menubar->Append(valuemenu, wxT("Value"));
218 regionmenu = new wxMenu();
219 menubar->Append(regionmenu, wxT("Region"));
220 typemenu = new wxMenu();
221 bookmarkmenu = new wxMenu();
222 bookmarkmenu->Append(wxID_ADD_BOOKMARK, wxT("Add bookmark..."));
223 bookmarkmenu->Append(wxID_DELETE_BOOKMARK, wxT("Delete bookmark..."));
224 bookmarkmenu->AppendSeparator();
225 bookmarkmenu->Append(wxID_LOAD_BOOKMARKS, wxT("Load bookmarks..."));
226 bookmarkmenu->Append(wxID_SAVE_BOOKMARKS, wxT("Save bookmarks..."));
227 bookmarkmenu->AppendSeparator();
228 menubar->Append(bookmarkmenu, wxT("Bookmarks"));
229 valuemenu->AppendSubMenu(typemenu, wxT("Type"));
230 oendian = valuemenu->AppendCheckItem(wxID_OPPOSITE_ENDIAN, wxT("Little endian"));
231 for(size_t i = 0; i < sizeof(datatypes) / sizeof(datatypes[0]); i++)
232 typemenu->AppendRadioItem(wxID_DATATYPES_FIRST + i, towxstring(datatypes[i].name));
233 typemenu->FindItem(wxID_DATATYPES_FIRST)->Check();
234 searchmenu = new wxMenu();
235 menubar->Append(searchmenu, wxT("Search"));
236 searchmenu->Append(wxID_SEARCH_PREV, wxT("Previous...\tCtrl+P"));
237 searchmenu->Append(wxID_SEARCH_NEXT, wxT("Next...\tCtrl+N"));
238 searchmenu->Append(wxID_SEARCH_WATCH, wxT("Add watch...\tCtrl+W"));
239 searchmenu->AppendSeparator();
240 searchmenu->Append(wxID_SEARCH_DISQUALIFY, wxT("Disqualify...\tCtrl+D"));
241 set_search_status();
243 littleendian = true;
244 valuemenu->FindItem(wxID_OPPOSITE_ENDIAN)->Check(littleendian);
245 curtype = 0;
246 Connect(wxID_ADD_BOOKMARK, wxEVT_COMMAND_MENU_SELECTED,
247 wxCommandEventHandler(wxeditor_hexedit::on_addbookmark));
248 Connect(wxID_DELETE_BOOKMARK, wxEVT_COMMAND_MENU_SELECTED,
249 wxCommandEventHandler(wxeditor_hexedit::on_deletebookmark));
250 Connect(wxID_LOAD_BOOKMARKS, wxEVT_COMMAND_MENU_SELECTED,
251 wxCommandEventHandler(wxeditor_hexedit::on_loadbookmarks));
252 Connect(wxID_SAVE_BOOKMARKS, wxEVT_COMMAND_MENU_SELECTED,
253 wxCommandEventHandler(wxeditor_hexedit::on_savebookmarks));
254 Connect(wxID_OPPOSITE_ENDIAN, wxEVT_COMMAND_MENU_SELECTED,
255 wxCommandEventHandler(wxeditor_hexedit::on_changeendian));
256 Connect(wxID_DATATYPES_FIRST, wxID_DATATYPES_LAST, wxEVT_COMMAND_MENU_SELECTED,
257 wxCommandEventHandler(wxeditor_hexedit::on_typechange));
258 Connect(wxID_REGIONS_FIRST, wxID_REGIONS_LAST, wxEVT_COMMAND_MENU_SELECTED,
259 wxCommandEventHandler(wxeditor_hexedit::on_vmasel));
260 Connect(wxID_BOOKMARKS_FIRST, wxID_BOOKMARKS_LAST, wxEVT_COMMAND_MENU_SELECTED,
261 wxCommandEventHandler(wxeditor_hexedit::on_bookmark));
262 Connect(wxID_SEARCH_DISQUALIFY, wxEVT_COMMAND_MENU_SELECTED,
263 wxCommandEventHandler(wxeditor_hexedit::on_search_discard));
264 Connect(wxID_SEARCH_PREV, wxEVT_COMMAND_MENU_SELECTED,
265 wxCommandEventHandler(wxeditor_hexedit::on_search_prevnext));
266 Connect(wxID_SEARCH_NEXT, wxEVT_COMMAND_MENU_SELECTED,
267 wxCommandEventHandler(wxeditor_hexedit::on_search_prevnext));
268 Connect(wxID_SEARCH_WATCH, wxEVT_COMMAND_MENU_SELECTED,
269 wxCommandEventHandler(wxeditor_hexedit::on_search_watch));
271 scroll->set_page_size(hpanel->lines);
272 scroll->set_handler([this](scroll_bar& s) {
273 this->hpanel->offset = s.get_position();
274 this->hpanel->request_paint();
277 corechange.set(inst.dispatch->core_changed, [this](bool hard) {
278 this->on_core_changed(hard); });
279 on_core_changed(true);
280 top->SetSizeHints(this);
281 Fit();
283 ~wxeditor_hexedit()
285 destructing = true;
286 editor.remove(inst);
288 bool ShouldPreventAppExit() const
290 return false;
292 void set_search_status()
294 bool e = wxwindow_memorysearch_active(inst);
295 searchmenu->FindItem(wxID_SEARCH_DISQUALIFY)->Enable(e);
296 searchmenu->FindItem(wxID_SEARCH_PREV)->Enable(e);
297 searchmenu->FindItem(wxID_SEARCH_NEXT)->Enable(e);
299 void on_keyboard(wxKeyEvent& e)
301 int c = e.GetKeyCode();
302 if(c == WXK_ESCAPE) {
303 hex_input_state = -1;
304 hpanel->request_paint();
305 return;
307 if(c == WXK_LEFT && hex_input_state < 0) {
308 if(hpanel->seloff > 0) hpanel->seloff--;
309 hpanel->request_paint();
310 return;
312 if(c == WXK_RIGHT && hex_input_state < 0) {
313 if(hpanel->seloff + 1 < hpanel->vmasize) hpanel->seloff++;
314 hpanel->request_paint();
315 return;
317 if(c == WXK_UP && hex_input_state < 0) {
318 if(hpanel->seloff >= 16) hpanel->seloff -= 16;
319 hpanel->request_paint();
320 return;
322 if(c == WXK_DOWN && hex_input_state < 0) {
323 if(hpanel->seloff + 16 < hpanel->vmasize) hpanel->seloff += 16;
324 hpanel->request_paint();
325 return;
327 if(c == WXK_PAGEUP && hex_input_state < 0) {
328 scroll->apply_delta(-static_cast<int>(hpanel->lines));
329 hpanel->offset = scroll->get_position();
330 hpanel->request_paint();
331 return;
333 if(c == WXK_PAGEDOWN && hex_input_state < 0) {
334 scroll->apply_delta(static_cast<int>(hpanel->lines));
335 hpanel->offset = scroll->get_position();
336 hpanel->request_paint();
337 return;
339 if(c >= '0' && c <= '9') {
340 do_hex(c - '0');
341 return;
343 if(c >= 'A' && c <= 'F') {
344 do_hex(c - 'A' + 10);
345 return;
347 if(c >= 'a' && c <= 'f') {
348 do_hex(c - 'a' + 10);
349 return;
351 e.Skip();
353 void on_mouse(wxMouseEvent& e)
355 auto cell = hpanel->get_cell();
356 if(e.LeftDown())
357 hpanel->on_mouse0(e.GetX() / cell.first, e.GetY() / cell.second, true);
358 if(e.LeftUp())
359 hpanel->on_mouse0(e.GetX() / cell.first, e.GetY() / cell.second, false);
360 unsigned speed = 1;
361 if(e.ShiftDown())
362 speed = 10;
363 if(e.ShiftDown() && e.ControlDown())
364 speed = 50;
365 scroll->apply_wheel(e.GetWheelRotation(), e.GetWheelDelta(), speed);
366 hpanel->offset = scroll->get_position();
368 void on_loadbookmarks(wxCommandEvent& e)
370 try {
371 std::string filename = choose_file_load(this, "Load bookmarks from file",
372 UI_get_project_otherpath(inst), filetype_hexbookmarks);
373 auto _in = zip::readrel(filename, "");
374 std::string in(_in.begin(), _in.end());
375 JSON::node root(in);
376 std::vector<bookmark_entry> newbookmarks;
377 for(auto i : root) {
378 bookmark_entry e;
379 e.name = i["name"].as_string8();
380 e.vma = i["vma"].as_string8();
381 e.scroll = i["offset"].as_int();
382 e.sel = i["selected"].as_uint();
383 newbookmarks.push_back(e);
385 std::swap(bookmarks, newbookmarks);
386 for(unsigned i = wxID_BOOKMARKS_FIRST; i <= wxID_BOOKMARKS_LAST; i++) {
387 auto p = bookmarkmenu->FindItem(i);
388 if(p)
389 bookmarkmenu->Delete(p);
391 int idx = 0;
392 for(auto i : bookmarks) {
393 if(wxID_BOOKMARKS_FIRST + idx > wxID_BOOKMARKS_LAST)
394 break;
395 bookmarkmenu->Append(wxID_BOOKMARKS_FIRST + idx, towxstring(i.name));
396 idx++;
398 } catch(canceled_exception& e) {
399 } catch(std::exception& e) {
400 show_message_ok(this, "Error", std::string("Can't load bookmarks: ") + e.what(),
401 wxICON_EXCLAMATION);
402 return;
405 void on_savebookmarks(wxCommandEvent& e)
407 JSON::node root(JSON::array);
408 for(auto i : bookmarks) {
409 JSON::node n(JSON::object);
410 n["name"] = JSON::string(i.name);
411 n["vma"] = JSON::string(i.vma);
412 n["offset"] = JSON::number((int64_t)i.scroll);
413 n["selected"] = JSON::number(i.sel);
414 root.append(n);
416 std::string doc = root.serialize();
417 try {
418 std::string filename = choose_file_save(this, "Save bookmarks to file",
419 UI_get_project_otherpath(inst), filetype_hexbookmarks);
420 std::ofstream out(filename.c_str());
421 out << doc << std::endl;
422 out.close();
423 } catch(canceled_exception& e) {
424 } catch(std::exception& e) {
425 show_message_ok(this, "Error", std::string("Can't save bookmarks: ") + e.what(),
426 wxICON_EXCLAMATION);
429 void on_addbookmark(wxCommandEvent& e)
431 if(bookmarks.size() <= wxID_BOOKMARKS_LAST - wxID_BOOKMARKS_FIRST) {
432 std::string name = pick_text(this, "Add bookmark", "Enter name for bookmark", "", false);
433 bookmark_entry ent;
434 ent.name = name;
435 ent.vma = get_current_vma_name();
436 ent.scroll = hpanel->offset;
437 ent.sel = hpanel->seloff;
438 int idx = bookmarks.size();
439 bookmarks.push_back(ent);
440 bookmarkmenu->Append(wxID_BOOKMARKS_FIRST + idx, towxstring(name));
441 } else {
442 show_message_ok(this, "Error adding bookmark", "Too many bookmarks", wxICON_EXCLAMATION);
445 void on_deletebookmark(wxCommandEvent& e)
447 if(bookmarks.size() > 0) {
448 std::vector<wxString> _choices;
449 for(auto i : bookmarks)
450 _choices.push_back(towxstring(i.name));
451 wxSingleChoiceDialog* d2 = new wxSingleChoiceDialog(this, towxstring("Select bookmark "
452 "to delete"), towxstring("Delete bookmark"), _choices.size(), &_choices[0]);
453 d2->SetSelection(0);
454 if(d2->ShowModal() == wxID_CANCEL) {
455 d2->Destroy();
456 return;
458 int sel = d2->GetSelection();
459 d2->Destroy();
460 if(sel >= 0)
461 bookmarks.erase(bookmarks.begin() + sel);
462 for(unsigned i = wxID_BOOKMARKS_FIRST; i <= wxID_BOOKMARKS_LAST; i++) {
463 auto p = bookmarkmenu->FindItem(i);
464 if(p)
465 bookmarkmenu->Delete(p);
467 int idx = 0;
468 for(auto i : bookmarks) {
469 bookmarkmenu->Append(wxID_BOOKMARKS_FIRST + idx, towxstring(i.name));
470 idx++;
474 void rescroll_panel()
476 uint64_t vfirst = static_cast<uint64_t>(hpanel->offset) * 16;
477 uint64_t vlast = static_cast<uint64_t>(hpanel->offset + hpanel->lines) * 16;
478 if(hpanel->seloff < vfirst || hpanel->seloff >= vlast) {
479 int l = hpanel->seloff / 16;
480 int r = hpanel->lines / 4;
481 hpanel->offset = (l > r) ? (l - r) : 0;
482 scroll->set_position(hpanel->offset);
485 void on_search_discard(wxCommandEvent& e)
487 auto p = wxwindow_memorysearch_active(inst);
488 if(!p)
489 return;
490 if(hpanel->seloff < hpanel->vmasize) {
491 p->dq_range(hpanel->vmabase + hpanel->seloff, hpanel->vmabase + hpanel->seloff);
492 wxwindow_memorysearch_update(inst);
493 hpanel->seloff = p->cycle_candidate_vma(hpanel->vmabase + hpanel->seloff, true) -
494 hpanel->vmabase;
495 rescroll_panel();
496 hpanel->request_paint();
499 void on_search_watch(wxCommandEvent& e)
501 try {
502 if(!hpanel->vmasize)
503 return;
504 uint64_t addr = hpanel->vmabase + hpanel->seloff;
505 std::string n = pick_text(this, "Name for watch", (stringfmt()
506 << "Enter name for watch at 0x" << std::hex << addr << ":").str());
507 if(n == "")
508 return;
509 memwatch_item e;
510 e.expr = (stringfmt() << addr).str();
511 e.format = datatypes[curtype].format;
512 e.bytes = datatypes[curtype].len;
513 e.signed_flag = (datatypes[curtype].type == 1);
514 e.float_flag = (datatypes[curtype].type == 2);
515 //Handle hostendian VMAs.
516 auto i = inst.memory->get_regions();
517 bool hostendian = false;
518 for(auto& j : i) {
519 if(addr >= j->base && addr < j->base + j->size && !j->endian)
520 hostendian = true;
522 e.endianess = hostendian ? 0 : (littleendian ? -1 : 1);
523 e.scale_div = 1ULL << datatypes[curtype].scale;
524 inst.iqueue->run([n, &e]() { CORE().mwatch->set(n, e); });
525 } catch(canceled_exception& e) {
528 void on_search_prevnext(wxCommandEvent& e)
530 auto p = wxwindow_memorysearch_active(inst);
531 if(!p)
532 return;
533 if(hpanel->seloff < hpanel->vmasize) {
534 hpanel->seloff = p->cycle_candidate_vma(hpanel->vmabase + hpanel->seloff, e.GetId() ==
535 wxID_SEARCH_NEXT) - hpanel->vmabase;
536 rescroll_panel();
537 hpanel->request_paint();
540 void on_bookmark(wxCommandEvent& e)
542 int id = e.GetId();
543 if(id < wxID_BOOKMARKS_FIRST || id > wxID_BOOKMARKS_LAST)
544 return;
545 bookmark_entry ent = bookmarks[id - wxID_BOOKMARKS_FIRST];
546 int r = vma_index_for_name(ent.vma);
547 uint64_t base = 0, size = 0;
548 auto i = inst.memory->get_regions();
549 for(auto j : i) {
550 if(j->readonly || j->special)
551 continue;
552 if(j->name == ent.vma) {
553 base = j->base;
554 size = j->size;
557 if(ent.sel >= size || ent.scroll >= (ssize_t)((size + 15) / 16))
558 goto invalid_bookmark;
559 current_vma = r;
560 regionmenu->FindItem(wxID_REGIONS_FIRST + current_vma)->Check();
561 update_vma(base, size);
562 hpanel->offset = ent.scroll;
563 hpanel->seloff = ent.sel;
564 scroll->set_position(hpanel->offset);
565 hpanel->request_paint();
566 return;
567 invalid_bookmark:
568 show_message_ok(this, "Error jumping to bookmark", "Bookmark refers to nonexistent location",
569 wxICON_EXCLAMATION);
570 return;
572 void on_vmasel(wxCommandEvent& e)
574 if(destructing)
575 return;
576 int selected = e.GetId();
577 if(selected < wxID_REGIONS_FIRST || selected > wxID_REGIONS_LAST)
578 return;
579 selected -= wxID_REGIONS_FIRST;
580 auto i = inst.memory->get_regions();
581 int index = 0;
582 for(auto j : i) {
583 if(j->readonly || j->special)
584 continue;
585 if(index == selected) {
586 if(j->base != hpanel->vmabase || j->size != hpanel->vmasize)
587 update_vma(j->base, j->size);
588 current_vma = index;
589 if(vma_endians.count(index)) {
590 littleendian = vma_endians[index];
591 valuemenu->FindItem(wxID_OPPOSITE_ENDIAN)->Check(littleendian);
593 return;
595 index++;
597 current_vma = index;
598 update_vma(0, 0);
600 bool is_endian_little(int endian)
602 if(endian < 0) return true;
603 if(endian > 0) return false;
604 uint16_t magic = 1;
605 return (*reinterpret_cast<uint8_t*>(&magic) == 1);
607 void update_vma(uint64_t base, uint64_t size)
609 hpanel->vmabase = base;
610 hpanel->vmasize = size;
611 hpanel->offset = 0;
612 hpanel->seloff = 0;
613 scroll->set_range((size + 15) / 16);
614 scroll->set_position(0);
615 hpanel->request_paint();
617 void on_typechange(wxCommandEvent& e)
619 if(destructing)
620 return;
621 int id = e.GetId();
622 if(id < wxID_DATATYPES_FIRST || id > wxID_DATATYPES_LAST)
623 return;
624 curtype = id - wxID_DATATYPES_FIRST;
625 hpanel->request_paint();
627 void on_changeendian(wxCommandEvent& e)
629 if(destructing)
630 return;
631 littleendian = valuemenu->FindItem(wxID_OPPOSITE_ENDIAN)->IsChecked();
632 if(vma_endians.count(current_vma))
633 vma_endians[current_vma] = littleendian;
634 hpanel->request_paint();
636 void updated()
638 if(destructing)
639 return;
640 hpanel->request_paint();
642 void jumpto(uint64_t addr)
644 if(destructing)
645 return;
646 //Switch to correct VMA.
647 auto i = inst.memory->get_regions();
648 int index = 0;
649 for(auto j : i) {
650 if(j->readonly || j->special)
651 continue;
652 if(addr >= j->base && addr < j->base + j->size) {
653 if(j->base != hpanel->vmabase || j->size != hpanel->vmasize)
654 update_vma(j->base, j->size);
655 current_vma = index;
656 if(vma_endians.count(index)) {
657 littleendian = vma_endians[index];
658 valuemenu->FindItem(wxID_OPPOSITE_ENDIAN)->Check(littleendian);
660 break;
662 index++;
664 if(addr < hpanel->vmabase || addr >= hpanel->vmabase + hpanel->vmasize)
665 return;
666 hpanel->seloff = addr - hpanel->vmabase;
667 rescroll_panel();
668 hpanel->request_paint();
670 void refresh_curvalue()
672 uint8_t buf[maxvaluelen];
673 memcpy(buf, hpanel->value, maxvaluelen);
674 val_type vt = datatypes[curtype];
675 if(littleendian != is_endian_little(vt.hard_bigendian ? 1 : 0))
676 for(unsigned i = 0; i < vt.len / 2; i++)
677 std::swap(buf[i], buf[vt.len - i - 1]);
678 wxMenuItem* it = regionmenu->FindItem(wxID_REGIONS_FIRST + current_vma);
679 std::string vma = "(none)";
680 if(it) vma = tostdstring(it->GetItemLabelText());
681 unsigned addrlen = 1;
682 while(hpanel->vmasize > (1ULL << (4 * addrlen)))
683 addrlen++;
684 std::string addr = (stringfmt() << std::hex << std::setw(addrlen) << std::setfill('0') <<
685 hpanel->seloff).str();
686 std::string vtext = vt.read(buf);
687 statusbar->SetStatusText(towxstring("Region: " + vma + " Address: " + addr + " Value: " + vtext));
689 int vma_index_for_name(const std::string& x)
691 for(size_t i = 0; i < vma_names.size(); i++)
692 if(vma_names[i] == x)
693 return i;
694 return -1;
696 std::string get_current_vma_name()
698 if(current_vma >= vma_names.size())
699 return "";
700 return vma_names[current_vma];
702 void on_core_changed(bool _hard)
704 if(destructing)
705 return;
706 bool hard = _hard;
707 runuifun([this, hard]() {
708 for(unsigned i = wxID_REGIONS_FIRST; i <= wxID_REGIONS_LAST; i++) {
709 auto p = regionmenu->FindItem(i);
710 if(p)
711 regionmenu->Delete(p);
713 std::string current_reg = get_current_vma_name();
714 uint64_t nsbase = 0, nssize = 0;
715 auto i = inst.memory->get_regions();
716 vma_names.clear();
717 if(hard)
718 vma_endians.clear();
719 int index = 0;
720 int curreg_index = 0;
721 for(auto j : i) {
722 if(j->readonly || j->special)
723 continue;
724 regionmenu->AppendRadioItem(wxID_REGIONS_FIRST + index, towxstring(j->name));
725 vma_names.push_back(j->name);
726 if(j->name == current_reg || index == 0) {
727 curreg_index = index;
728 nsbase = j->base;
729 nssize = j->size;
731 if(!vma_endians.count(index))
732 vma_endians[index] = is_endian_little(j->endian);
733 index++;
735 if(!index) {
736 update_vma(0, 0);
737 return;
739 regionmenu->FindItem(wxID_REGIONS_FIRST + curreg_index)->Check();
740 current_vma = curreg_index;
741 if(vma_endians.count(current_vma)) {
742 littleendian = vma_endians[current_vma];
743 typemenu->FindItem(wxID_DATATYPES_FIRST)->Check(littleendian);
745 if(nsbase != hpanel->vmabase || nssize != hpanel->vmasize)
746 update_vma(nsbase, nssize);
748 hpanel->request_paint();
750 void do_hex(int hex)
752 if(hpanel->seloff > hpanel->vmasize)
753 return;
754 if(hex_input_state < 0)
755 hex_input_state = hex;
756 else {
757 uint8_t byte = hex_input_state * 16 + hex;
758 uint64_t addr = hpanel->vmabase + hpanel->seloff;
759 hex_input_state = -1;
760 if(hpanel->seloff + 1 < hpanel->vmasize)
761 hpanel->seloff++;
762 inst.iqueue->run([addr, byte]() {
763 CORE().memory->write<uint8_t>(addr, byte);
766 hpanel->request_paint();
768 class _panel : public text_framebuffer_panel
770 public:
771 _panel(wxeditor_hexedit* parent, emulator_instance& _inst)
772 : text_framebuffer_panel(parent, 59, lines = 28, wxID_ANY, NULL), inst(_inst)
774 rparent = parent;
775 vmabase = 0;
776 vmasize = 0;
777 offset = 0;
778 seloff = 0;
779 memset(value, 0, maxvaluelen);
780 clear();
781 Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(wxeditor_hexedit::on_mouse), NULL, parent);
782 Connect(wxEVT_LEFT_UP, wxMouseEventHandler(wxeditor_hexedit::on_mouse), NULL, parent);
783 Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(wxeditor_hexedit::on_mouse), NULL, parent);
784 Connect(wxEVT_CHAR, wxKeyEventHandler(wxeditor_hexedit::on_keyboard), NULL, parent);
785 request_paint();
787 void prepare_paint()
789 uint64_t paint_offset = static_cast<uint64_t>(offset) * 16;
790 uint64_t _vmabase = vmabase;
791 uint64_t _vmasize = vmasize;
792 uint64_t _seloff = seloff;
793 int _lines = lines;
794 uint8_t* _value = value;
795 inst.iqueue->run([_vmabase, _vmasize, paint_offset, _seloff, _value, _lines,
796 this]() {
797 memory_search* memsearch = wxwindow_memorysearch_active(inst);
798 //Paint the stuff
799 for(ssize_t j = 0; j < _lines; j++) {
800 uint64_t addr = paint_offset + j * 16;
801 if(addr >= _vmasize) {
802 //Past-the-end.
803 for(size_t i = 0; i < get_characters().first; i++)
804 write(" ", 1, i, j, 0, 0xFFFFFF);
805 continue;
807 for(size_t i = 0; i < sizeof(separators)/sizeof(separators[0]); i++) {
808 write(sepchars[i], 1, separators[i], j, 0, 0xFFFFFF);
810 for(size_t i = 0; i < hexaddr; i++) {
811 write(hexes[(addr >> 4 * (hexaddr - i - 1)) & 15], 1, i, j, 0,
812 0xFFFFFF);
814 size_t bytes = 16;
815 if(_vmasize - addr < 16)
816 bytes = _vmasize - addr;
817 uint64_t laddr = addr + _vmabase;
818 for(size_t i = 0; i < bytes; i++) {
819 uint32_t fg = 0;
820 uint32_t bg = 0xFFFFFF;
821 bool candidate = (memsearch && memsearch->is_candidate(laddr + i));
822 if(candidate) bg = (bg & 0xC0C0C0) | 0x3F0000;
823 if(addr + i == _seloff)
824 std::swap(fg, bg);
825 uint8_t b = inst.memory->read<uint8_t>(laddr + i);
826 if(rparent->hex_input_state < 0 || addr + i != seloff
828 write(hexes[(b >> 4) & 15], 1, hexcol[i], j, fg, bg);
829 else
830 write(hexes[rparent->hex_input_state], 1, hexcol[i], j, 0xFF,
832 write(hexes[b & 15], 1, hexcol[i] + 1, j, fg, bg);
833 char32_t buf[2] = {0, 0};
834 buf[0] = byte_to_char(b);
835 write(buf, 1, charcol[i], j, fg, bg);
837 for(size_t i = bytes; i < 16; i++) {
838 write(" ", 2, hexcol[i], j, 0, 0xFFFFFF);
839 write(" ", 1, charcol[i] + 1, j, 0, 0xFFFFFF);
842 memset(_value, 0, maxvaluelen);
843 inst.memory->read_range(_vmabase + _seloff, _value, maxvaluelen);
845 rparent->refresh_curvalue();
846 rparent->set_search_status();
848 char32_t byte_to_char(uint8_t ch)
850 if(ch == 160)
851 return U' ';
852 if((ch & 0x60) == 0 || ch == 127 || ch == 0xad)
853 return U'.';
854 return ch;
856 void on_mouse0(int x, int y, bool polarity)
858 if(!polarity)
859 return;
860 uint64_t rowaddr = 16 * (static_cast<uint64_t>(offset) + y);
861 int coladdr = 16;
862 for(unsigned i = 0; i < 16; i++)
863 if(x == hexcol[i] || x == hexcol[i] + 1 || x == charcol[i])
864 coladdr = i;
865 if(rowaddr + coladdr >= vmasize || coladdr > 15)
866 return;
867 seloff = rowaddr + coladdr;
868 request_paint();
870 emulator_instance& inst;
871 wxeditor_hexedit* rparent;
872 int offset;
873 uint64_t vmabase;
874 uint64_t vmasize;
875 uint64_t seloff;
876 uint8_t value[maxvaluelen];
877 int lines;
879 private:
880 struct bookmark_entry
882 std::string name;
883 std::string vma;
884 int scroll;
885 uint64_t sel;
887 emulator_instance& inst;
888 wxMenu* regionmenu;
889 wxMenu* bookmarkmenu;
890 wxMenu* searchmenu;
891 wxComboBox* datatype;
892 wxMenuItem* oendian;
893 wxStatusBar* statusbar;
894 wxMenuBar* menubar;
895 scroll_bar* scroll;
896 _panel* hpanel;
897 wxMenu* valuemenu;
898 wxMenu* typemenu;
899 struct dispatch::target<bool> corechange;
900 unsigned current_vma;
901 std::vector<std::string> vma_names;
902 std::map<unsigned, bool> vma_endians;
903 std::vector<bookmark_entry> bookmarks;
904 bool destructing;
905 unsigned curtype;
906 bool littleendian;
907 int hex_input_state;
910 void wxeditor_hexedit_display(wxWindow* parent, emulator_instance& inst)
912 auto e = editor.lookup(inst);
913 if(e) {
914 e->Raise();
915 return;
917 try {
918 editor.create(inst, parent)->Show();
919 } catch(...) {
923 void wxeditor_hexeditor_update(emulator_instance& inst)
925 auto e = editor.lookup(inst);
926 if(e) e->updated();
929 bool wxeditor_hexeditor_available(emulator_instance& inst)
931 return editor.exists(inst);
934 bool wxeditor_hexeditor_jumpto(emulator_instance& inst, uint64_t addr)
936 auto e = editor.lookup(inst);
937 if(e) {
938 e->jumpto(addr);
939 return true;
940 } else
941 return false;