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"
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"
30 class wxeditor_hexedit
;
34 const size_t maxvaluelen
= 8; //The length of longest value type.
35 instance_map
<wxeditor_hexedit
> editor
;
43 int type
; //0 => Unsigned, 1 => Signed, 2 => Float
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();
52 {"1 byte (unsigned)", 1, false, "", 0, 0, [](const uint8_t* x
) -> std::string
{
53 return (stringfmt() << (int)x
[0]).str();
55 {"1 byte (hex)", 1, false, "%02x", 0, 0, [](const uint8_t* x
) -> std::string
{
58 {"2 bytes (signed)", 2, false, "", 1, 0, [](const uint8_t* x
) -> std::string
{
59 return (stringfmt() << *(int16_t*)x
).str();
61 {"2 bytes (unsigned)", 2, false, "", 0, 0, [](const uint8_t* x
) -> std::string
{
62 return (stringfmt() << *(uint16_t*)x
).str();
64 {"2 bytes (hex)", 2, false, "%04x", 0, 0, [](const uint8_t* x
) -> std::string
{
65 return hex::to(*(uint16_t*)x
);
67 {"3 bytes (signed)", 3, true, "", 1, 0, [](const uint8_t* x
) -> std::string
{
69 a
|= (uint32_t)x
[0] << 16;
70 a
|= (uint32_t)x
[1] << 8;
74 return (stringfmt() << a
).str();
76 {"3 bytes (unsigned)", 3, true, "", 0, 0, [](const uint8_t* x
) -> std::string
{
78 a
|= (uint32_t)x
[0] << 16;
79 a
|= (uint32_t)x
[1] << 8;
81 return (stringfmt() << a
).str();
83 {"3 bytes (hex)", 3, true, "%06x", 0, 0, [](const uint8_t* x
) -> std::string
{
85 a
|= (uint32_t)x
[0] << 16;
86 a
|= (uint32_t)x
[1] << 8;
90 {"4 bytes (signed)", 4, false, "", 1, 0, [](const uint8_t* x
) -> std::string
{
91 return (stringfmt() << *(int32_t*)x
).str();
93 {"4 bytes (unsigned)", 4, false, "", 0, 0, [](const uint8_t* x
) -> std::string
{
94 return (stringfmt() << *(uint32_t*)x
).str();
96 {"4 bytes (hex)", 4, false, "%08x", 0, 0, [](const uint8_t* x
) -> std::string
{
97 return hex::to(*(uint32_t*)x
);
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
{
128 a
|= (uint32_t)x
[0] << 16;
129 a
|= (uint32_t)x
[1] << 8;
133 return (stringfmt() << a
/ 256.0).str();
135 {"Q16.8 (unsigned)", 3, true, "", 0, 8, [](const uint8_t* x
) -> std::string
{
137 a
|= (uint32_t)x
[0] << 16;
138 a
|= (uint32_t)x
[1] << 8;
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",
171 wxID_OPPOSITE_ENDIAN
= wxID_HIGHEST
+ 1,
172 wxID_DATATYPES_FIRST
,
173 wxID_DATATYPES_LAST
= wxID_DATATYPES_FIRST
+ 255,
175 wxID_REGIONS_LAST
= wxID_REGIONS_FIRST
+ 255,
177 wxID_DELETE_BOOKMARK
,
180 wxID_BOOKMARKS_FIRST
,
181 wxID_BOOKMARKS_LAST
= wxID_BOOKMARKS_FIRST
+ 255,
182 wxID_SEARCH_DISQUALIFY
,
189 class wxeditor_hexedit
: public wxFrame
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
)
197 wxBoxSizer
* top
= new wxBoxSizer(wxVERTICAL
);
201 hex_input_state
= -1;
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
);
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"));
244 valuemenu
->FindItem(wxID_OPPOSITE_ENDIAN
)->Check(littleendian
);
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);
288 bool ShouldPreventAppExit() const
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();
307 if(c
== WXK_LEFT
&& hex_input_state
< 0) {
308 if(hpanel
->seloff
> 0) hpanel
->seloff
--;
309 hpanel
->request_paint();
312 if(c
== WXK_RIGHT
&& hex_input_state
< 0) {
313 if(hpanel
->seloff
+ 1 < hpanel
->vmasize
) hpanel
->seloff
++;
314 hpanel
->request_paint();
317 if(c
== WXK_UP
&& hex_input_state
< 0) {
318 if(hpanel
->seloff
>= 16) hpanel
->seloff
-= 16;
319 hpanel
->request_paint();
322 if(c
== WXK_DOWN
&& hex_input_state
< 0) {
323 if(hpanel
->seloff
+ 16 < hpanel
->vmasize
) hpanel
->seloff
+= 16;
324 hpanel
->request_paint();
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();
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();
339 if(c
>= '0' && c
<= '9') {
343 if(c
>= 'A' && c
<= 'F') {
344 do_hex(c
- 'A' + 10);
347 if(c
>= 'a' && c
<= 'f') {
348 do_hex(c
- 'a' + 10);
353 void on_mouse(wxMouseEvent
& e
)
355 auto cell
= hpanel
->get_cell();
357 hpanel
->on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
359 hpanel
->on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
363 if(e
.ShiftDown() && e
.ControlDown())
365 scroll
->apply_wheel(e
.GetWheelRotation(), e
.GetWheelDelta(), speed
);
366 hpanel
->offset
= scroll
->get_position();
368 void on_loadbookmarks(wxCommandEvent
& e
)
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());
376 std::vector
<bookmark_entry
> newbookmarks
;
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
);
389 bookmarkmenu
->Delete(p
);
392 for(auto i
: bookmarks
) {
393 if(wxID_BOOKMARKS_FIRST
+ idx
> wxID_BOOKMARKS_LAST
)
395 bookmarkmenu
->Append(wxID_BOOKMARKS_FIRST
+ idx
, towxstring(i
.name
));
398 } catch(canceled_exception
& e
) {
399 } catch(std::exception
& e
) {
400 show_message_ok(this, "Error", std::string("Can't load bookmarks: ") + e
.what(),
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
);
416 std::string doc
= root
.serialize();
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
;
423 } catch(canceled_exception
& e
) {
424 } catch(std::exception
& e
) {
425 show_message_ok(this, "Error", std::string("Can't save bookmarks: ") + e
.what(),
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);
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
));
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]);
454 if(d2
->ShowModal() == wxID_CANCEL
) {
458 int sel
= d2
->GetSelection();
461 bookmarks
.erase(bookmarks
.begin() + sel
);
462 for(unsigned i
= wxID_BOOKMARKS_FIRST
; i
<= wxID_BOOKMARKS_LAST
; i
++) {
463 auto p
= bookmarkmenu
->FindItem(i
);
465 bookmarkmenu
->Delete(p
);
468 for(auto i
: bookmarks
) {
469 bookmarkmenu
->Append(wxID_BOOKMARKS_FIRST
+ idx
, towxstring(i
.name
));
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
);
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) -
496 hpanel
->request_paint();
499 void on_search_watch(wxCommandEvent
& e
)
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());
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;
519 if(addr
>= j
->base
&& addr
< j
->base
+ j
->size
&& !j
->endian
)
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
);
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
;
537 hpanel
->request_paint();
540 void on_bookmark(wxCommandEvent
& e
)
543 if(id
< wxID_BOOKMARKS_FIRST
|| id
> wxID_BOOKMARKS_LAST
)
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();
550 if(j
->readonly
|| j
->special
)
552 if(j
->name
== ent
.vma
) {
557 if(ent
.sel
>= size
|| ent
.scroll
>= (ssize_t
)((size
+ 15) / 16))
558 goto invalid_bookmark
;
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();
568 show_message_ok(this, "Error jumping to bookmark", "Bookmark refers to nonexistent location",
572 void on_vmasel(wxCommandEvent
& e
)
576 int selected
= e
.GetId();
577 if(selected
< wxID_REGIONS_FIRST
|| selected
> wxID_REGIONS_LAST
)
579 selected
-= wxID_REGIONS_FIRST
;
580 auto i
= inst
.memory
->get_regions();
583 if(j
->readonly
|| j
->special
)
585 if(index
== selected
) {
586 if(j
->base
!= hpanel
->vmabase
|| j
->size
!= hpanel
->vmasize
)
587 update_vma(j
->base
, j
->size
);
589 if(vma_endians
.count(index
)) {
590 littleendian
= vma_endians
[index
];
591 valuemenu
->FindItem(wxID_OPPOSITE_ENDIAN
)->Check(littleendian
);
600 bool is_endian_little(int endian
)
602 if(endian
< 0) return true;
603 if(endian
> 0) return false;
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
;
613 scroll
->set_range((size
+ 15) / 16);
614 scroll
->set_position(0);
615 hpanel
->request_paint();
617 void on_typechange(wxCommandEvent
& e
)
622 if(id
< wxID_DATATYPES_FIRST
|| id
> wxID_DATATYPES_LAST
)
624 curtype
= id
- wxID_DATATYPES_FIRST
;
625 hpanel
->request_paint();
627 void on_changeendian(wxCommandEvent
& e
)
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();
640 hpanel
->request_paint();
642 void jumpto(uint64_t addr
)
646 //Switch to correct VMA.
647 auto i
= inst
.memory
->get_regions();
650 if(j
->readonly
|| j
->special
)
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
);
656 if(vma_endians
.count(index
)) {
657 littleendian
= vma_endians
[index
];
658 valuemenu
->FindItem(wxID_OPPOSITE_ENDIAN
)->Check(littleendian
);
664 if(addr
< hpanel
->vmabase
|| addr
>= hpanel
->vmabase
+ hpanel
->vmasize
)
666 hpanel
->seloff
= addr
- hpanel
->vmabase
;
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
)))
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
)
696 std::string
get_current_vma_name()
698 if(current_vma
>= vma_names
.size())
700 return vma_names
[current_vma
];
702 void on_core_changed(bool _hard
)
707 runuifun([this, hard
]() {
708 for(unsigned i
= wxID_REGIONS_FIRST
; i
<= wxID_REGIONS_LAST
; i
++) {
709 auto p
= regionmenu
->FindItem(i
);
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();
720 int curreg_index
= 0;
722 if(j
->readonly
|| j
->special
)
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
;
731 if(!vma_endians
.count(index
))
732 vma_endians
[index
] = is_endian_little(j
->endian
);
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();
752 if(hpanel
->seloff
> hpanel
->vmasize
)
754 if(hex_input_state
< 0)
755 hex_input_state
= hex
;
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
)
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
771 _panel(wxeditor_hexedit
* parent
, emulator_instance
& _inst
)
772 : text_framebuffer_panel(parent
, 59, lines
= 28, wxID_ANY
, NULL
), inst(_inst
)
779 memset(value
, 0, maxvaluelen
);
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
);
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
;
794 uint8_t* _value
= value
;
795 inst
.iqueue
->run([_vmabase
, _vmasize
, paint_offset
, _seloff
, _value
, _lines
,
797 memory_search
* memsearch
= wxwindow_memorysearch_active(inst
);
799 for(ssize_t j
= 0; j
< _lines
; j
++) {
800 uint64_t addr
= paint_offset
+ j
* 16;
801 if(addr
>= _vmasize
) {
803 for(size_t i
= 0; i
< get_characters().first
; i
++)
804 write(" ", 1, i
, j
, 0, 0xFFFFFF);
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,
815 if(_vmasize
- addr
< 16)
816 bytes
= _vmasize
- addr
;
817 uint64_t laddr
= addr
+ _vmabase
;
818 for(size_t i
= 0; i
< bytes
; i
++) {
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
)
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
);
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
)
852 if((ch
& 0x60) == 0 || ch
== 127 || ch
== 0xad)
856 void on_mouse0(int x
, int y
, bool polarity
)
860 uint64_t rowaddr
= 16 * (static_cast<uint64_t>(offset
) + y
);
862 for(unsigned i
= 0; i
< 16; i
++)
863 if(x
== hexcol
[i
] || x
== hexcol
[i
] + 1 || x
== charcol
[i
])
865 if(rowaddr
+ coladdr
>= vmasize
|| coladdr
> 15)
867 seloff
= rowaddr
+ coladdr
;
870 emulator_instance
& inst
;
871 wxeditor_hexedit
* rparent
;
876 uint8_t value
[maxvaluelen
];
880 struct bookmark_entry
887 emulator_instance
& inst
;
889 wxMenu
* bookmarkmenu
;
891 wxComboBox
* datatype
;
893 wxStatusBar
* statusbar
;
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
;
910 void wxeditor_hexedit_display(wxWindow
* parent
, emulator_instance
& inst
)
912 auto e
= editor
.lookup(inst
);
918 editor
.create(inst
, parent
)->Show();
923 void wxeditor_hexeditor_update(emulator_instance
& inst
)
925 auto e
= editor
.lookup(inst
);
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
);