3 #include <wx/control.h>
4 #include <wx/combobox.h>
5 #include <wx/radiobut.h>
7 #include "core/moviedata.hpp"
8 #include "core/memorywatch.hpp"
9 #include "core/dispatch.hpp"
10 #include "core/instance.hpp"
11 #include "core/instance-map.hpp"
12 #include "core/project.hpp"
13 #include "core/memorymanip.hpp"
14 #include "core/ui-services.hpp"
15 #include "library/memorysearch.hpp"
16 #include "library/hex.hpp"
18 #include "platform/wxwidgets/platform.hpp"
19 #include "platform/wxwidgets/textrender.hpp"
20 #include "platform/wxwidgets/loadsave.hpp"
21 #include "platform/wxwidgets/scrollbar.hpp"
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
)
198 wxBoxSizer
* top
= new wxBoxSizer(wxVERTICAL
);
202 hex_input_state
= -1;
205 Connect(wxEVT_CHAR
, wxKeyEventHandler(wxeditor_hexedit::on_keyboard
), NULL
, this);
207 wxBoxSizer
* parea
= new wxBoxSizer(wxHORIZONTAL
);
208 parea
->Add(hpanel
= new _panel(this, inst
), 1, wxGROW
);
210 parea
->Add(scroll
= new scroll_bar(this, wxID_ANY
, true), 0, wxGROW
);
211 top
->Add(parea
, 1, wxGROW
);
212 scroll
->Connect(wxEVT_CHAR
, wxKeyEventHandler(wxeditor_hexedit::on_keyboard
), NULL
, this);
214 SetStatusBar(statusbar
= new wxStatusBar(this));
215 SetMenuBar(menubar
= new wxMenuBar
);
217 valuemenu
= new wxMenu();
218 menubar
->Append(valuemenu
, wxT("Value"));
219 regionmenu
= new wxMenu();
220 menubar
->Append(regionmenu
, wxT("Region"));
221 typemenu
= new wxMenu();
222 bookmarkmenu
= new wxMenu();
223 bookmarkmenu
->Append(wxID_ADD_BOOKMARK
, wxT("Add bookmark..."));
224 bookmarkmenu
->Append(wxID_DELETE_BOOKMARK
, wxT("Delete bookmark..."));
225 bookmarkmenu
->AppendSeparator();
226 bookmarkmenu
->Append(wxID_LOAD_BOOKMARKS
, wxT("Load bookmarks..."));
227 bookmarkmenu
->Append(wxID_SAVE_BOOKMARKS
, wxT("Save bookmarks..."));
228 bookmarkmenu
->AppendSeparator();
229 menubar
->Append(bookmarkmenu
, wxT("Bookmarks"));
230 valuemenu
->AppendSubMenu(typemenu
, wxT("Type"));
231 oendian
= valuemenu
->AppendCheckItem(wxID_OPPOSITE_ENDIAN
, wxT("Little endian"));
232 for(size_t i
= 0; i
< sizeof(datatypes
) / sizeof(datatypes
[0]); i
++)
233 typemenu
->AppendRadioItem(wxID_DATATYPES_FIRST
+ i
, towxstring(datatypes
[i
].name
));
234 typemenu
->FindItem(wxID_DATATYPES_FIRST
)->Check();
235 searchmenu
= new wxMenu();
236 menubar
->Append(searchmenu
, wxT("Search"));
237 searchmenu
->Append(wxID_SEARCH_PREV
, wxT("Previous...\tCtrl+P"));
238 searchmenu
->Append(wxID_SEARCH_NEXT
, wxT("Next...\tCtrl+N"));
239 searchmenu
->Append(wxID_SEARCH_WATCH
, wxT("Add watch...\tCtrl+W"));
240 searchmenu
->AppendSeparator();
241 searchmenu
->Append(wxID_SEARCH_DISQUALIFY
, wxT("Disqualify...\tCtrl+D"));
245 valuemenu
->FindItem(wxID_OPPOSITE_ENDIAN
)->Check(littleendian
);
247 Connect(wxID_ADD_BOOKMARK
, wxEVT_COMMAND_MENU_SELECTED
,
248 wxCommandEventHandler(wxeditor_hexedit::on_addbookmark
));
249 Connect(wxID_DELETE_BOOKMARK
, wxEVT_COMMAND_MENU_SELECTED
,
250 wxCommandEventHandler(wxeditor_hexedit::on_deletebookmark
));
251 Connect(wxID_LOAD_BOOKMARKS
, wxEVT_COMMAND_MENU_SELECTED
,
252 wxCommandEventHandler(wxeditor_hexedit::on_loadbookmarks
));
253 Connect(wxID_SAVE_BOOKMARKS
, wxEVT_COMMAND_MENU_SELECTED
,
254 wxCommandEventHandler(wxeditor_hexedit::on_savebookmarks
));
255 Connect(wxID_OPPOSITE_ENDIAN
, wxEVT_COMMAND_MENU_SELECTED
,
256 wxCommandEventHandler(wxeditor_hexedit::on_changeendian
));
257 Connect(wxID_DATATYPES_FIRST
, wxID_DATATYPES_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
258 wxCommandEventHandler(wxeditor_hexedit::on_typechange
));
259 Connect(wxID_REGIONS_FIRST
, wxID_REGIONS_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
260 wxCommandEventHandler(wxeditor_hexedit::on_vmasel
));
261 Connect(wxID_BOOKMARKS_FIRST
, wxID_BOOKMARKS_LAST
, wxEVT_COMMAND_MENU_SELECTED
,
262 wxCommandEventHandler(wxeditor_hexedit::on_bookmark
));
263 Connect(wxID_SEARCH_DISQUALIFY
, wxEVT_COMMAND_MENU_SELECTED
,
264 wxCommandEventHandler(wxeditor_hexedit::on_search_discard
));
265 Connect(wxID_SEARCH_PREV
, wxEVT_COMMAND_MENU_SELECTED
,
266 wxCommandEventHandler(wxeditor_hexedit::on_search_prevnext
));
267 Connect(wxID_SEARCH_NEXT
, wxEVT_COMMAND_MENU_SELECTED
,
268 wxCommandEventHandler(wxeditor_hexedit::on_search_prevnext
));
269 Connect(wxID_SEARCH_WATCH
, wxEVT_COMMAND_MENU_SELECTED
,
270 wxCommandEventHandler(wxeditor_hexedit::on_search_watch
));
272 scroll
->set_page_size(hpanel
->lines
);
273 scroll
->set_handler([this](scroll_bar
& s
) {
274 this->hpanel
->offset
= s
.get_position();
275 this->hpanel
->request_paint();
278 corechange
.set(inst
.dispatch
->core_changed
, [this](bool hard
) {
279 this->on_core_changed(hard
); });
280 on_core_changed(true);
281 top
->SetSizeHints(this);
289 bool ShouldPreventAppExit() const
293 void set_search_status()
296 bool e
= wxwindow_memorysearch_active(inst
);
297 searchmenu
->FindItem(wxID_SEARCH_DISQUALIFY
)->Enable(e
);
298 searchmenu
->FindItem(wxID_SEARCH_PREV
)->Enable(e
);
299 searchmenu
->FindItem(wxID_SEARCH_NEXT
)->Enable(e
);
301 void on_keyboard(wxKeyEvent
& e
)
304 int c
= e
.GetKeyCode();
305 if(c
== WXK_ESCAPE
) {
306 hex_input_state
= -1;
307 hpanel
->request_paint();
310 if(c
== WXK_LEFT
&& hex_input_state
< 0) {
311 if(hpanel
->seloff
> 0) hpanel
->seloff
--;
312 hpanel
->request_paint();
315 if(c
== WXK_RIGHT
&& hex_input_state
< 0) {
316 if(hpanel
->seloff
+ 1 < hpanel
->vmasize
) hpanel
->seloff
++;
317 hpanel
->request_paint();
320 if(c
== WXK_UP
&& hex_input_state
< 0) {
321 if(hpanel
->seloff
>= 16) hpanel
->seloff
-= 16;
322 hpanel
->request_paint();
325 if(c
== WXK_DOWN
&& hex_input_state
< 0) {
326 if(hpanel
->seloff
+ 16 < hpanel
->vmasize
) hpanel
->seloff
+= 16;
327 hpanel
->request_paint();
330 if(c
== WXK_PAGEUP
&& hex_input_state
< 0) {
331 scroll
->apply_delta(-static_cast<int>(hpanel
->lines
));
332 hpanel
->offset
= scroll
->get_position();
333 hpanel
->request_paint();
336 if(c
== WXK_PAGEDOWN
&& hex_input_state
< 0) {
337 scroll
->apply_delta(static_cast<int>(hpanel
->lines
));
338 hpanel
->offset
= scroll
->get_position();
339 hpanel
->request_paint();
342 if(c
>= '0' && c
<= '9') {
346 if(c
>= 'A' && c
<= 'F') {
347 do_hex(c
- 'A' + 10);
350 if(c
>= 'a' && c
<= 'f') {
351 do_hex(c
- 'a' + 10);
356 void on_mouse(wxMouseEvent
& e
)
359 auto cell
= hpanel
->get_cell();
361 hpanel
->on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, true);
363 hpanel
->on_mouse0(e
.GetX() / cell
.first
, e
.GetY() / cell
.second
, false);
367 if(e
.ShiftDown() && e
.ControlDown())
369 scroll
->apply_wheel(e
.GetWheelRotation(), e
.GetWheelDelta(), speed
);
370 hpanel
->offset
= scroll
->get_position();
372 void on_loadbookmarks(wxCommandEvent
& e
)
376 std::string filename
= choose_file_load(this, "Load bookmarks from file",
377 UI_get_project_otherpath(inst
), filetype_hexbookmarks
);
378 auto _in
= zip::readrel(filename
, "");
379 std::string
in(_in
.begin(), _in
.end());
381 std::vector
<bookmark_entry
> newbookmarks
;
384 e
.name
= i
["name"].as_string8();
385 e
.vma
= i
["vma"].as_string8();
386 e
.scroll
= i
["offset"].as_int();
387 e
.sel
= i
["selected"].as_uint();
388 newbookmarks
.push_back(e
);
390 std::swap(bookmarks
, newbookmarks
);
391 for(unsigned i
= wxID_BOOKMARKS_FIRST
; i
<= wxID_BOOKMARKS_LAST
; i
++) {
392 auto p
= bookmarkmenu
->FindItem(i
);
394 bookmarkmenu
->Delete(p
);
397 for(auto i
: bookmarks
) {
398 if(wxID_BOOKMARKS_FIRST
+ idx
> wxID_BOOKMARKS_LAST
)
400 bookmarkmenu
->Append(wxID_BOOKMARKS_FIRST
+ idx
, towxstring(i
.name
));
403 } catch(canceled_exception
& e
) {
404 } catch(std::exception
& e
) {
405 show_message_ok(this, "Error", std::string("Can't load bookmarks: ") + e
.what(),
410 void on_savebookmarks(wxCommandEvent
& e
)
413 JSON::node
root(JSON::array
);
414 for(auto i
: bookmarks
) {
415 JSON::node
n(JSON::object
);
416 n
["name"] = JSON::string(i
.name
);
417 n
["vma"] = JSON::string(i
.vma
);
418 n
["offset"] = JSON::number((int64_t)i
.scroll
);
419 n
["selected"] = JSON::number(i
.sel
);
422 std::string doc
= root
.serialize();
424 std::string filename
= choose_file_save(this, "Save bookmarks to file",
425 UI_get_project_otherpath(inst
), filetype_hexbookmarks
);
426 std::ofstream
out(filename
.c_str());
427 out
<< doc
<< std::endl
;
429 } catch(canceled_exception
& e
) {
430 } catch(std::exception
& e
) {
431 show_message_ok(this, "Error", std::string("Can't save bookmarks: ") + e
.what(),
435 void on_addbookmark(wxCommandEvent
& e
)
438 if(bookmarks
.size() <= wxID_BOOKMARKS_LAST
- wxID_BOOKMARKS_FIRST
) {
439 std::string name
= pick_text(this, "Add bookmark", "Enter name for bookmark", "", false);
442 ent
.vma
= get_current_vma_name();
443 ent
.scroll
= hpanel
->offset
;
444 ent
.sel
= hpanel
->seloff
;
445 int idx
= bookmarks
.size();
446 bookmarks
.push_back(ent
);
447 bookmarkmenu
->Append(wxID_BOOKMARKS_FIRST
+ idx
, towxstring(name
));
449 show_message_ok(this, "Error adding bookmark", "Too many bookmarks", wxICON_EXCLAMATION
);
452 void on_deletebookmark(wxCommandEvent
& e
)
455 if(bookmarks
.size() > 0) {
456 std::vector
<wxString
> _choices
;
457 for(auto i
: bookmarks
)
458 _choices
.push_back(towxstring(i
.name
));
459 wxSingleChoiceDialog
* d2
= new wxSingleChoiceDialog(this, towxstring("Select bookmark "
460 "to delete"), towxstring("Delete bookmark"), _choices
.size(), &_choices
[0]);
462 if(d2
->ShowModal() == wxID_CANCEL
) {
466 int sel
= d2
->GetSelection();
469 bookmarks
.erase(bookmarks
.begin() + sel
);
470 for(unsigned i
= wxID_BOOKMARKS_FIRST
; i
<= wxID_BOOKMARKS_LAST
; i
++) {
471 auto p
= bookmarkmenu
->FindItem(i
);
473 bookmarkmenu
->Delete(p
);
476 for(auto i
: bookmarks
) {
477 bookmarkmenu
->Append(wxID_BOOKMARKS_FIRST
+ idx
, towxstring(i
.name
));
482 void rescroll_panel()
485 uint64_t vfirst
= static_cast<uint64_t>(hpanel
->offset
) * 16;
486 uint64_t vlast
= static_cast<uint64_t>(hpanel
->offset
+ hpanel
->lines
) * 16;
487 if(hpanel
->seloff
< vfirst
|| hpanel
->seloff
>= vlast
) {
488 int l
= hpanel
->seloff
/ 16;
489 int r
= hpanel
->lines
/ 4;
490 hpanel
->offset
= (l
> r
) ? (l
- r
) : 0;
491 scroll
->set_position(hpanel
->offset
);
494 void on_search_discard(wxCommandEvent
& e
)
497 auto p
= wxwindow_memorysearch_active(inst
);
500 if(hpanel
->seloff
< hpanel
->vmasize
) {
501 p
->dq_range(hpanel
->vmabase
+ hpanel
->seloff
, hpanel
->vmabase
+ hpanel
->seloff
);
502 wxwindow_memorysearch_update(inst
);
503 hpanel
->seloff
= p
->cycle_candidate_vma(hpanel
->vmabase
+ hpanel
->seloff
, true) -
506 hpanel
->request_paint();
509 void on_search_watch(wxCommandEvent
& e
)
515 uint64_t addr
= hpanel
->vmabase
+ hpanel
->seloff
;
516 std::string n
= pick_text(this, "Name for watch", (stringfmt()
517 << "Enter name for watch at 0x" << std::hex
<< addr
<< ":").str());
521 e
.expr
= (stringfmt() << addr
).str();
522 e
.format
= datatypes
[curtype
].format
;
523 e
.bytes
= datatypes
[curtype
].len
;
524 e
.signed_flag
= (datatypes
[curtype
].type
== 1);
525 e
.float_flag
= (datatypes
[curtype
].type
== 2);
526 //Handle hostendian VMAs.
527 auto i
= inst
.memory
->get_regions();
528 bool hostendian
= false;
530 if(addr
>= j
->base
&& addr
< j
->base
+ j
->size
&& !j
->endian
)
533 e
.endianess
= hostendian
? 0 : (littleendian
? -1 : 1);
534 e
.scale_div
= 1ULL << datatypes
[curtype
].scale
;
535 inst
.iqueue
->run([n
, &e
]() { CORE().mwatch
->set(n
, e
); });
536 } catch(canceled_exception
& e
) {
539 void on_search_prevnext(wxCommandEvent
& e
)
542 auto p
= wxwindow_memorysearch_active(inst
);
545 if(hpanel
->seloff
< hpanel
->vmasize
) {
546 hpanel
->seloff
= p
->cycle_candidate_vma(hpanel
->vmabase
+ hpanel
->seloff
, e
.GetId() ==
547 wxID_SEARCH_NEXT
) - hpanel
->vmabase
;
549 hpanel
->request_paint();
552 void on_bookmark(wxCommandEvent
& e
)
556 if(id
< wxID_BOOKMARKS_FIRST
|| id
> wxID_BOOKMARKS_LAST
)
558 bookmark_entry ent
= bookmarks
[id
- wxID_BOOKMARKS_FIRST
];
559 int r
= vma_index_for_name(ent
.vma
);
560 uint64_t base
= 0, size
= 0;
561 auto i
= inst
.memory
->get_regions();
563 if(j
->readonly
|| j
->special
)
565 if(j
->name
== ent
.vma
) {
570 if(ent
.sel
>= size
|| ent
.scroll
>= (ssize_t
)((size
+ 15) / 16))
571 goto invalid_bookmark
;
573 regionmenu
->FindItem(wxID_REGIONS_FIRST
+ current_vma
)->Check();
574 update_vma(base
, size
);
575 hpanel
->offset
= ent
.scroll
;
576 hpanel
->seloff
= ent
.sel
;
577 scroll
->set_position(hpanel
->offset
);
578 hpanel
->request_paint();
581 show_message_ok(this, "Error jumping to bookmark", "Bookmark refers to nonexistent location",
585 void on_vmasel(wxCommandEvent
& e
)
590 int selected
= e
.GetId();
591 if(selected
< wxID_REGIONS_FIRST
|| selected
> wxID_REGIONS_LAST
)
593 selected
-= wxID_REGIONS_FIRST
;
594 auto i
= inst
.memory
->get_regions();
597 if(j
->readonly
|| j
->special
)
599 if(index
== selected
) {
600 if(j
->base
!= hpanel
->vmabase
|| j
->size
!= hpanel
->vmasize
)
601 update_vma(j
->base
, j
->size
);
603 if(vma_endians
.count(index
)) {
604 littleendian
= vma_endians
[index
];
605 valuemenu
->FindItem(wxID_OPPOSITE_ENDIAN
)->Check(littleendian
);
614 bool is_endian_little(int endian
)
616 if(endian
< 0) return true;
617 if(endian
> 0) return false;
619 return (*reinterpret_cast<uint8_t*>(&magic
) == 1);
621 void update_vma(uint64_t base
, uint64_t size
)
624 hpanel
->vmabase
= base
;
625 hpanel
->vmasize
= size
;
628 scroll
->set_range((size
+ 15) / 16);
629 scroll
->set_position(0);
630 hpanel
->request_paint();
632 void on_typechange(wxCommandEvent
& e
)
638 if(id
< wxID_DATATYPES_FIRST
|| id
> wxID_DATATYPES_LAST
)
640 curtype
= id
- wxID_DATATYPES_FIRST
;
641 hpanel
->request_paint();
643 void on_changeendian(wxCommandEvent
& e
)
648 littleendian
= valuemenu
->FindItem(wxID_OPPOSITE_ENDIAN
)->IsChecked();
649 if(vma_endians
.count(current_vma
))
650 vma_endians
[current_vma
] = littleendian
;
651 hpanel
->request_paint();
658 hpanel
->request_paint();
660 void jumpto(uint64_t addr
)
665 //Switch to correct VMA.
666 auto i
= inst
.memory
->get_regions();
669 if(j
->readonly
|| j
->special
)
671 if(addr
>= j
->base
&& addr
< j
->base
+ j
->size
) {
672 if(j
->base
!= hpanel
->vmabase
|| j
->size
!= hpanel
->vmasize
)
673 update_vma(j
->base
, j
->size
);
675 if(vma_endians
.count(index
)) {
676 littleendian
= vma_endians
[index
];
677 valuemenu
->FindItem(wxID_OPPOSITE_ENDIAN
)->Check(littleendian
);
683 if(addr
< hpanel
->vmabase
|| addr
>= hpanel
->vmabase
+ hpanel
->vmasize
)
685 hpanel
->seloff
= addr
- hpanel
->vmabase
;
687 hpanel
->request_paint();
689 void refresh_curvalue()
692 uint8_t buf
[maxvaluelen
];
693 memcpy(buf
, hpanel
->value
, maxvaluelen
);
694 val_type vt
= datatypes
[curtype
];
695 if(littleendian
!= is_endian_little(vt
.hard_bigendian
? 1 : 0))
696 for(unsigned i
= 0; i
< vt
.len
/ 2; i
++)
697 std::swap(buf
[i
], buf
[vt
.len
- i
- 1]);
698 wxMenuItem
* it
= regionmenu
->FindItem(wxID_REGIONS_FIRST
+ current_vma
);
699 std::string vma
= "(none)";
700 if(it
) vma
= tostdstring(it
->GetItemLabelText());
701 unsigned addrlen
= 1;
702 while(hpanel
->vmasize
> (1ULL << (4 * addrlen
)))
704 std::string addr
= (stringfmt() << std::hex
<< std::setw(addrlen
) << std::setfill('0') <<
705 hpanel
->seloff
).str();
706 std::string vtext
= vt
.read(buf
);
707 statusbar
->SetStatusText(towxstring("Region: " + vma
+ " Address: " + addr
+ " Value: " + vtext
));
709 int vma_index_for_name(const std::string
& x
)
711 for(size_t i
= 0; i
< vma_names
.size(); i
++)
712 if(vma_names
[i
] == x
)
716 std::string
get_current_vma_name()
718 if(current_vma
>= vma_names
.size())
720 return vma_names
[current_vma
];
722 void on_core_changed(bool _hard
)
727 runuifun([this, hard
]() {
728 for(unsigned i
= wxID_REGIONS_FIRST
; i
<= wxID_REGIONS_LAST
; i
++) {
729 auto p
= regionmenu
->FindItem(i
);
731 regionmenu
->Delete(p
);
733 std::string current_reg
= get_current_vma_name();
734 uint64_t nsbase
= 0, nssize
= 0;
735 auto i
= inst
.memory
->get_regions();
740 int curreg_index
= 0;
742 if(j
->readonly
|| j
->special
)
744 regionmenu
->AppendRadioItem(wxID_REGIONS_FIRST
+ index
, towxstring(j
->name
));
745 vma_names
.push_back(j
->name
);
746 if(j
->name
== current_reg
|| index
== 0) {
747 curreg_index
= index
;
751 if(!vma_endians
.count(index
))
752 vma_endians
[index
] = is_endian_little(j
->endian
);
759 regionmenu
->FindItem(wxID_REGIONS_FIRST
+ curreg_index
)->Check();
760 current_vma
= curreg_index
;
761 if(vma_endians
.count(current_vma
)) {
762 littleendian
= vma_endians
[current_vma
];
763 typemenu
->FindItem(wxID_DATATYPES_FIRST
)->Check(littleendian
);
765 if(nsbase
!= hpanel
->vmabase
|| nssize
!= hpanel
->vmasize
)
766 update_vma(nsbase
, nssize
);
767 hpanel
->request_paint();
772 if(hpanel
->seloff
> hpanel
->vmasize
)
774 if(hex_input_state
< 0)
775 hex_input_state
= hex
;
777 uint8_t byte
= hex_input_state
* 16 + hex
;
778 uint64_t addr
= hpanel
->vmabase
+ hpanel
->seloff
;
779 hex_input_state
= -1;
780 if(hpanel
->seloff
+ 1 < hpanel
->vmasize
)
782 inst
.iqueue
->run([addr
, byte
]() {
783 CORE().memory
->write
<uint8_t>(addr
, byte
);
786 hpanel
->request_paint();
788 class _panel
: public text_framebuffer_panel
791 _panel(wxeditor_hexedit
* parent
, emulator_instance
& _inst
)
792 : text_framebuffer_panel(parent
, 59, lines
= 28, wxID_ANY
, NULL
), inst(_inst
)
800 memset(value
, 0, maxvaluelen
);
802 Connect(wxEVT_LEFT_DOWN
, wxMouseEventHandler(wxeditor_hexedit::on_mouse
), NULL
, parent
);
803 Connect(wxEVT_LEFT_UP
, wxMouseEventHandler(wxeditor_hexedit::on_mouse
), NULL
, parent
);
804 Connect(wxEVT_MOUSEWHEEL
, wxMouseEventHandler(wxeditor_hexedit::on_mouse
), NULL
, parent
);
805 Connect(wxEVT_CHAR
, wxKeyEventHandler(wxeditor_hexedit::on_keyboard
), NULL
, parent
);
811 uint64_t paint_offset
= static_cast<uint64_t>(offset
) * 16;
812 uint64_t _vmabase
= vmabase
;
813 uint64_t _vmasize
= vmasize
;
814 uint64_t _seloff
= seloff
;
816 uint8_t* _value
= value
;
817 inst
.iqueue
->run([_vmabase
, _vmasize
, paint_offset
, _seloff
, _value
, _lines
,
819 memory_search
* memsearch
= wxwindow_memorysearch_active(inst
);
821 for(ssize_t j
= 0; j
< _lines
; j
++) {
822 uint64_t addr
= paint_offset
+ j
* 16;
823 if(addr
>= _vmasize
) {
825 for(size_t i
= 0; i
< get_characters().first
; i
++)
826 write(" ", 1, i
, j
, 0, 0xFFFFFF);
829 for(size_t i
= 0; i
< sizeof(separators
)/sizeof(separators
[0]); i
++) {
830 write(sepchars
[i
], 1, separators
[i
], j
, 0, 0xFFFFFF);
832 for(size_t i
= 0; i
< hexaddr
; i
++) {
833 write(hexes
[(addr
>> 4 * (hexaddr
- i
- 1)) & 15], 1, i
, j
, 0,
837 if(_vmasize
- addr
< 16)
838 bytes
= _vmasize
- addr
;
839 uint64_t laddr
= addr
+ _vmabase
;
840 for(size_t i
= 0; i
< bytes
; i
++) {
842 uint32_t bg
= 0xFFFFFF;
843 bool candidate
= (memsearch
&& memsearch
->is_candidate(laddr
+ i
));
844 if(candidate
) bg
= (bg
& 0xC0C0C0) | 0x3F0000;
845 if(addr
+ i
== _seloff
)
847 uint8_t b
= inst
.memory
->read
<uint8_t>(laddr
+ i
);
848 if(rparent
->hex_input_state
< 0 || addr
+ i
!= seloff
850 write(hexes
[(b
>> 4) & 15], 1, hexcol
[i
], j
, fg
, bg
);
852 write(hexes
[rparent
->hex_input_state
], 1, hexcol
[i
], j
, 0xFF,
854 write(hexes
[b
& 15], 1, hexcol
[i
] + 1, j
, fg
, bg
);
855 char32_t buf
[2] = {0, 0};
856 buf
[0] = byte_to_char(b
);
857 write(buf
, 1, charcol
[i
], j
, fg
, bg
);
859 for(size_t i
= bytes
; i
< 16; i
++) {
860 write(" ", 2, hexcol
[i
], j
, 0, 0xFFFFFF);
861 write(" ", 1, charcol
[i
] + 1, j
, 0, 0xFFFFFF);
864 memset(_value
, 0, maxvaluelen
);
865 inst
.memory
->read_range(_vmabase
+ _seloff
, _value
, maxvaluelen
);
867 rparent
->refresh_curvalue();
868 rparent
->set_search_status();
870 char32_t
byte_to_char(uint8_t ch
)
874 if((ch
& 0x60) == 0 || ch
== 127 || ch
== 0xad)
878 void on_mouse0(int x
, int y
, bool polarity
)
883 uint64_t rowaddr
= 16 * (static_cast<uint64_t>(offset
) + y
);
885 for(unsigned i
= 0; i
< 16; i
++)
886 if(x
== hexcol
[i
] || x
== hexcol
[i
] + 1 || x
== charcol
[i
])
888 if(rowaddr
+ coladdr
>= vmasize
|| coladdr
> 15)
890 seloff
= rowaddr
+ coladdr
;
893 emulator_instance
& inst
;
894 wxeditor_hexedit
* rparent
;
899 uint8_t value
[maxvaluelen
];
903 struct bookmark_entry
910 emulator_instance
& inst
;
912 wxMenu
* bookmarkmenu
;
914 wxComboBox
* datatype
;
916 wxStatusBar
* statusbar
;
922 struct dispatch::target
<bool> corechange
;
923 unsigned current_vma
;
924 std::vector
<std::string
> vma_names
;
925 std::map
<unsigned, bool> vma_endians
;
926 std::vector
<bookmark_entry
> bookmarks
;
933 void wxeditor_hexedit_display(wxWindow
* parent
, emulator_instance
& inst
)
936 auto e
= editor
.lookup(inst
);
942 editor
.create(inst
, parent
)->Show();
947 void wxeditor_hexeditor_update(emulator_instance
& inst
)
950 auto e
= editor
.lookup(inst
);
954 bool wxeditor_hexeditor_available(emulator_instance
& inst
)
956 return editor
.exists(inst
);
959 bool wxeditor_hexeditor_jumpto(emulator_instance
& inst
, uint64_t addr
)
962 auto e
= editor
.lookup(inst
);