Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/ImDebugger/ImMemView.cpp
3185 views
1
#include <cctype>
2
#include <cmath>
3
#include <iomanip>
4
#include <cstdio>
5
#include <sstream>
6
7
#include "ext/imgui/imgui.h"
8
#include "ext/imgui/imgui_internal.h"
9
#include "ext/imgui/imgui_impl_thin3d.h"
10
11
#include "ext/xxhash.h"
12
#include "Common/StringUtils.h"
13
#include "Common/File/FileUtil.h"
14
#include "Core/Config.h"
15
#include "Core/MemMap.h"
16
#include "Core/Reporting.h"
17
#include "Core/RetroAchievements.h"
18
#include "Common/System/Display.h"
19
20
#include "Core/System.h"
21
#include "UI/ImDebugger/ImDebugger.h"
22
#include "UI/ImDebugger/ImMemView.h"
23
24
static const char* searchtypes[] = {"u8", "u16", "u32", "u64", "float", "string", "string16","bytesequence"};
25
26
ImMemView::ImMemView() {
27
windowStart_ = curAddress_;
28
selectRangeStart_ = curAddress_;
29
selectRangeEnd_ = curAddress_ + 1;
30
lastSelectReset_ = curAddress_;
31
memSearch_.matchAddress = 0xFFFFFFFF;
32
memSearch_.searching = false;
33
memSearch_.status = SEARCH_INITIAL;
34
}
35
36
ImMemView::~ImMemView() {}
37
38
static uint32_t pickTagColor(std::string_view tag) {
39
uint32_t colors[6] = { 0xFF301010, 0xFF103030, 0xFF403010, 0xFF103000, 0xFF301030, 0xFF101030 };
40
int which = XXH3_64bits(tag.data(), tag.length()) % ARRAY_SIZE(colors);
41
return colors[which];
42
}
43
44
void ImMemView::Draw(ImDrawList *drawList) {
45
if (!debugger_->isAlive()) {
46
return;
47
}
48
49
ImGui_PushFixedFont();
50
51
rowHeight_ = ImGui::GetTextLineHeightWithSpacing();
52
charWidth_ = ImGui::CalcTextSize("W", nullptr, false, -1.0f).x;
53
charHeight_ = ImGui::GetTextLineHeight();
54
55
offsetPositionY_ = offsetLine * rowHeight_;
56
57
addressStartX_ = charWidth_;
58
hexStartX_ = addressStartX_ + 9 * charWidth_;
59
asciiStartX_ = hexStartX_ + (rowSize_ * 3 + 1) * charWidth_;
60
61
const ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
62
const ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
63
const ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
64
65
// This will catch our interactions
66
bool pressed = ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
67
const bool is_hovered = ImGui::IsItemHovered(); // Hovered
68
const bool is_active = ImGui::IsItemActive(); // Held
69
70
if (pressed) {
71
// INFO_LOG(Log::System, "Pressed");
72
}
73
ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY);
74
75
visibleRows_ = canvas_sz.y / rowHeight_;
76
77
if (displayOffsetScale_) {
78
// visibleRows_ is calculated based on the size of the control, but X rows have already been used for the offsets and are no longer usable
79
visibleRows_ -= offsetSpace;
80
}
81
82
Bounds bounds;
83
bounds.x = canvas_p0.x;
84
bounds.y = canvas_p0.y;
85
bounds.w = canvas_p1.x - canvas_p0.x;
86
bounds.h = canvas_p1.y - canvas_p0.y;
87
88
drawList->PushClipRect(canvas_p0, canvas_p1, true);
89
drawList->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(25, 25, 25, 255));
90
91
if (displayOffsetScale_)
92
drawOffsetScale(drawList);
93
94
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, windowStart_, (visibleRows_ + 1) * rowSize_);
95
96
_assert_msg_(((windowStart_ | rowSize_) & 3) == 0, "readMemory() can't handle unaligned reads");
97
98
// draw one extra row that may be partially visible
99
for (int i = 0; i < visibleRows_ + 1; i++) {
100
int rowY = rowHeight_ * i;
101
// Skip the first X rows to make space for the offsets.
102
if (displayOffsetScale_)
103
rowY += rowHeight_ * offsetSpace;
104
105
char temp[32];
106
uint32_t address = windowStart_ + i * rowSize_;
107
snprintf(temp, sizeof(temp), "%08x", address);
108
drawList->AddText(ImVec2(canvas_p0.x + addressStartX_, canvas_p0.y + rowY), IM_COL32(0xE0, 0xE0, 0xE0, 0xFF), temp);
109
110
union {
111
uint32_t words[4];
112
uint8_t bytes[16];
113
} memory;
114
int valid = debugger_ != nullptr && debugger_->isAlive() ? Memory::ValidSize(address, 16) / 4 : 0;
115
for (int i = 0; i < valid; ++i) {
116
memory.words[i] = debugger_->readMemory(address + i * 4);
117
}
118
119
for (int j = 0; j < rowSize_; j++) {
120
const uint32_t byteAddress = (address + j) & ~0xC0000000;
121
std::string_view tag;
122
bool tagContinues = false;
123
for (const auto &info : memRangeInfo) {
124
if (info.start <= byteAddress && info.start + info.size > byteAddress) {
125
tag = info.tag; // doesn't take ownership!
126
tagContinues = byteAddress + 1 < info.start + info.size;
127
}
128
}
129
130
int hexX = hexStartX_ + j * 3 * charWidth_;
131
int hexLen = 2;
132
int asciiX = asciiStartX_ + j * (charWidth_ + 2);
133
134
char c;
135
if (valid) {
136
snprintf(temp, sizeof(temp), "%02x ", memory.bytes[j]);
137
c = (char)memory.bytes[j];
138
if (memory.bytes[j] < 32 || memory.bytes[j] >= 128)
139
c = '.';
140
} else {
141
truncate_cpy(temp, "??");
142
c = '.';
143
}
144
145
ImColor standardBG = 0xFF000000;
146
bool continueRect = false;
147
148
ImColor hexBGCol = 0;
149
ImColor hexTextCol = 0xFFFFFFFF;
150
ImColor asciiBGCol = 0;
151
ImColor asciiTextCol = 0xFFFFFFFF;
152
int underline = -1;
153
154
const ImColor primarySelFg = 0xFFFFFFFF;
155
const ImColor primarySelBg = 0xFFFF9933;
156
const ImColor secondarySelFg = 0xFFFFFFFF;
157
const ImColor secondarySelBg = 0xFF808080;
158
159
const ImColor zeroColor = 0x888888ff;
160
const ImColor searchResBg = 0xFF108010;
161
162
if (address + j >= selectRangeStart_ && address + j < selectRangeEnd_ && !memSearch_.searching) {
163
if (asciiSelected_) {
164
hexBGCol = secondarySelBg;
165
hexTextCol = secondarySelFg;
166
asciiBGCol = primarySelBg;
167
asciiTextCol = primarySelFg;
168
} else {
169
hexBGCol = primarySelBg;
170
hexTextCol = primarySelFg;
171
asciiBGCol = secondarySelBg;
172
asciiTextCol = secondarySelFg;
173
if (address + j == curAddress_)
174
underline = selectedNibble_;
175
}
176
if (!tag.empty() && tagContinues) {
177
continueRect = true;
178
}
179
} else if (!tag.empty()) {
180
if (memSearch_.status == SEARCH_OK && address+j>= memSearch_.matchAddress && address +j < memSearch_.matchAddress + memSearch_.fast_size && !memSearch_.searching) {
181
hexBGCol = searchResBg;
182
} else {
183
hexBGCol = pickTagColor(tag);
184
}
185
continueRect = tagContinues;
186
asciiBGCol = hexBGCol;
187
}
188
189
ImColor fg = hexTextCol;
190
ImColor bg = hexBGCol;
191
// SelectObject(hdc, underline == 0 ? (HGDIOBJ)underlineFont : (HGDIOBJ)font);
192
if (bg != 0) {
193
int bgWidth = 2; // continueRect ? 3 : 2;
194
drawList->AddRectFilled(ImVec2(canvas_p0.x + hexX - 1, canvas_p0.y + rowY), ImVec2(canvas_p0.x + hexX + charWidth_ * bgWidth, canvas_p0.y + rowY + charHeight_), bg);
195
}
196
if (drawZeroDark_ && temp[0] == '0' && temp[1] == '0') {
197
// if the byte is all zero make it darker
198
fg = zeroColor;
199
}
200
drawList->AddText(ImVec2(canvas_p0.x + hexX, canvas_p0.y + rowY), fg, &temp[0], &temp[2]);
201
if (underline >= 0) {
202
float x = canvas_p0.x + hexX + underline * charWidth_;
203
drawList->AddRectFilled(ImVec2(x, canvas_p0.y + rowY + charHeight_ - 2), ImVec2(x + charWidth_, canvas_p0.y + rowY + charHeight_), IM_COL32(0xFF, 0xFF, 0xFF, 0xFF));
204
}
205
206
fg = asciiTextCol;
207
bg = asciiBGCol;
208
if (bg) {
209
drawList->AddRectFilled(ImVec2(canvas_p0.x + asciiX, canvas_p0.y + rowY), ImVec2(canvas_p0.x + asciiX + charWidth_, canvas_p0.y + rowY + charHeight_), bg);
210
}
211
drawList->AddText(ImVec2(canvas_p0.x + asciiX, canvas_p0.y + rowY), fg, &c, &c + 1);
212
}
213
}
214
215
ImGuiIO& io = ImGui::GetIO();
216
ImVec2 mousePos = ImVec2(io.MousePos.x - canvas_p0.x, io.MousePos.y - canvas_p0.y);
217
if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
218
// INFO_LOG(Log::System, "Mousedown %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered);
219
onMouseDown(mousePos.x, mousePos.y, 1);
220
}
221
if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
222
// INFO_LOG(Log::CPU, "Mousedown %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered);
223
onMouseDown(mousePos.x, mousePos.y, 2);
224
}
225
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
226
// INFO_LOG(Log::System, "Mouseup %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered);
227
if (is_hovered) {
228
onMouseUp(mousePos.x, mousePos.y, 1);
229
}
230
}
231
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
232
// INFO_LOG(Log::System, "Mousedrag %f,%f active:%d hover:%d", mousePos.x, mousePos.y, is_active, is_hovered);
233
if (is_hovered) {
234
onMouseMove(mousePos.x, mousePos.y, 1);
235
}
236
}
237
238
if (is_hovered) {
239
if (io.MouseWheel > 0.0f) { // TODO: Scale steps by the value.
240
windowStart_ -= rowSize_ * 4;
241
} else if (io.MouseWheel < 0.0f) {
242
windowStart_ += rowSize_ * 4;
243
}
244
}
245
246
ProcessKeyboardShortcuts(ImGui::IsItemFocused());
247
248
ImGui_PopFont();
249
250
ImGui::OpenPopupOnItemClick("memcontext", ImGuiPopupFlags_MouseButtonRight);
251
PopupMenu();
252
253
drawList->PopClipRect();
254
}
255
256
// We don't have a scroll bar - though maybe we should build one.
257
/*
258
void ImMemView::onVScroll(WPARAM wParam, LPARAM lParam) {
259
switch (wParam & 0xFFFF) {
260
case SB_LINEDOWN:
261
ScrollWindow(1, GotoModeFromModifiers(false));
262
break;
263
case SB_LINEUP:
264
ScrollWindow(-1, GotoModeFromModifiers(false));
265
break;
266
case SB_PAGEDOWN:
267
ScrollWindow(visibleRows_, GotoModeFromModifiers(false));
268
break;
269
case SB_PAGEUP:
270
ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));
271
break;
272
default:
273
return;
274
}
275
}
276
*/
277
278
void ImMemView::ProcessKeyboardShortcuts(bool focused) {
279
if (!focused) {
280
return;
281
}
282
283
ImGuiIO& io = ImGui::GetIO();
284
285
if (io.KeyMods & ImGuiMod_Ctrl) {
286
if (ImGui::IsKeyPressed(ImGuiKey_G)) {
287
/*
288
u32 addr;
289
if (executeExpressionWindow(wnd, debugger_, addr) == false)
290
return;
291
gotoAddr(addr);
292
return;
293
*/
294
}
295
296
if (ImGui::IsKeyPressed(ImGuiKey_P)) {
297
PasteFromByteClipboard();
298
return;
299
}
300
if (ImGui::IsKeyPressed(ImGuiKey_C)) {
301
CopyToByteClipboard();
302
return;
303
}
304
} else {
305
if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) {
306
ScrollCursor(rowSize_, GotoModeFromModifiers(false));
307
return;
308
}
309
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) {
310
ScrollCursor(-rowSize_, GotoModeFromModifiers(false));
311
return;
312
}
313
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) {
314
ScrollCursor(-1, GotoModeFromModifiers(false));
315
return;
316
}
317
if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) {
318
ScrollCursor(1, GotoModeFromModifiers(false));
319
return;
320
}
321
if (ImGui::IsKeyPressed(ImGuiKey_PageDown)) {
322
ScrollWindow(visibleRows_, GotoModeFromModifiers(false));
323
return;
324
}
325
if (ImGui::IsKeyPressed(ImGuiKey_PageUp)) {
326
ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));
327
return;
328
}
329
330
if (editableMemory_) {
331
if (!asciiSelected_) {
332
for (int i = ImGuiKey_0; i != ImGuiKey_G; i++) {
333
if (ImGui::IsKeyPressed(static_cast<ImGuiKey>(i))) {
334
EditMemory(i - ImGuiKey_0);
335
return;
336
}
337
}
338
for (int i = ImGuiKey_Keypad0; i != ImGuiKey_KeypadDecimal; i++) {
339
if (ImGui::IsKeyPressed(static_cast<ImGuiKey>(i))) {
340
EditMemory(i - ImGuiKey_Keypad0);
341
return;
342
}
343
}
344
}
345
}
346
}
347
}
348
349
void ImMemView::EditMemory(int newNibble) {
350
uint8_t og = Memory::ReadUnchecked_U8(curAddress_);
351
u8 out;
352
if (selectedNibble_) {
353
out = (og & 0xF0) | newNibble;
354
} else {
355
out = (og & 0x0F) | (newNibble << 4);
356
}
357
Memory::WriteUnchecked_U8((u8)out, curAddress_);
358
ScrollCursor(1, GotoMode::RESET);
359
}
360
361
void ImMemView::CopyToByteClipboard() {
362
byteClipboard_.clear();
363
uint32_t size = selectRangeEnd_ - selectRangeStart_;
364
byteClipboard_.resize(size);
365
Memory::MemcpyUnchecked(byteClipboard_.data(), curAddress_, size);
366
}
367
368
void ImMemView::PasteFromByteClipboard() {
369
if (editableMemory_)
370
Memory::MemcpyUnchecked(curAddress_, byteClipboard_.data(), (u32)byteClipboard_.size());
371
}
372
373
void ImMemView::toggleEditableMemory(bool toggle) {
374
editableMemory_ = toggle;
375
}
376
377
378
void ImMemView::onChar(int c) {
379
ImGuiIO& io = ImGui::GetIO();
380
381
if (io.KeyMods & ImGuiMod_Ctrl) {
382
return;
383
}
384
385
if (!Memory::IsValidAddress(curAddress_)) {
386
ScrollCursor(1, GotoMode::RESET);
387
return;
388
}
389
390
if (asciiSelected_) {
391
Memory::WriteUnchecked_U8((u8)c, curAddress_);
392
ScrollCursor(1, GotoMode::RESET);
393
} else {
394
c = tolower(c);
395
int inputValue = -1;
396
397
if (c >= '0' && c <= '9') inputValue = c - '0';
398
if (c >= 'a' && c <= 'f') inputValue = c - 'a' + 10;
399
400
if (inputValue >= 0) {
401
int shiftAmount = (1 - selectedNibble_) * 4;
402
403
u8 oldValue = Memory::ReadUnchecked_U8(curAddress_);
404
oldValue &= ~(0xF << shiftAmount);
405
u8 newValue = oldValue | (inputValue << shiftAmount);
406
Memory::WriteUnchecked_U8(newValue, curAddress_);
407
ScrollCursor(1, GotoMode::RESET);
408
}
409
}
410
411
Reporting::NotifyDebugger();
412
}
413
414
ImMemView::GotoMode ImMemView::GotoModeFromModifiers(bool isRightClick) {
415
GotoMode mode = GotoMode::RESET;
416
auto &io = ImGui::GetIO();
417
if (isRightClick) {
418
mode = GotoMode::RESET_IF_OUTSIDE;
419
} else if (io.KeyMods & ImGuiMod_Shift) {
420
if (io.KeyMods & ImGuiMod_Ctrl)
421
mode = GotoMode::EXTEND;
422
else
423
mode = GotoMode::FROM_CUR;
424
}
425
return mode;
426
}
427
428
void ImMemView::onMouseDown(float x, float y, int button) {
429
if (Achievements::HardcoreModeActive())
430
return;
431
432
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
433
}
434
435
void ImMemView::PopupMenu() {
436
if (ImGui::BeginPopup("memcontext")) {
437
438
int32_t selectedSize = selectRangeEnd_ - selectRangeStart_;
439
bool enable16 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 1) == 0);
440
bool enable32 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 3) == 0);
441
/*
442
if (ImGui::MenuItem("Dump memory")) {
443
DumpMemoryWindow dump(wnd, debugger_);
444
dump.exec();
445
break;
446
}
447
*/
448
ImGui::SeparatorText("Internal clipboard");
449
if (ImGui::MenuItem("Copy bytes")) {
450
CopyToByteClipboard();
451
}
452
if (ImGui::MenuItem("Paste bytes")) {
453
PasteFromByteClipboard();
454
}
455
ImGui::SeparatorText("System clipboard");
456
if (ImGui::MenuItem("Copy value (8-bit)")) {
457
size_t tempSize = 3 * selectedSize + 1;
458
char *temp = new char[tempSize];
459
memset(temp, 0, tempSize);
460
461
// it's admittedly not really useful like this
462
if (asciiSelected_) {
463
for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {
464
uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : '.';
465
if (c < 32 || c >= 128)
466
c = '.';
467
temp[p - selectRangeStart_] = c;
468
}
469
} else {
470
char *pos = temp;
471
for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {
472
uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : 0xFF;
473
pos += snprintf(pos, tempSize - (pos - temp + 1), "%02X ", c);
474
}
475
// Clear the last space.
476
if (pos > temp)
477
*(pos - 1) = '\0';
478
}
479
480
System_CopyStringToClipboard(temp);
481
delete[] temp;
482
}
483
484
if (ImGui::MenuItem("Copy value (16-bit)")) {
485
size_t tempSize = 5 * ((selectedSize + 1) / 2) + 1;
486
char *temp = new char[tempSize];
487
memset(temp, 0, tempSize);
488
489
char *pos = temp;
490
for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 2) {
491
uint16_t c = Memory::IsValidRange(p, 2) ? Memory::ReadUnchecked_U16(p) : 0xFFFF;
492
pos += snprintf(pos, tempSize - (pos - temp + 1), "%04X ", c);
493
}
494
// Clear the last space.
495
if (pos > temp)
496
*(pos - 1) = '\0';
497
498
System_CopyStringToClipboard(temp);
499
delete[] temp;
500
}
501
502
if (ImGui::MenuItem("Copy value (32-bit)")) {
503
size_t tempSize = 9 * ((selectedSize + 3) / 4) + 1;
504
char *temp = new char[tempSize];
505
memset(temp, 0, tempSize);
506
507
char *pos = temp;
508
for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 4) {
509
uint32_t c = Memory::IsValidRange(p, 4) ? Memory::ReadUnchecked_U32(p) : 0xFFFFFFFF;
510
pos += snprintf(pos, tempSize - (pos - temp + 1), "%08X ", c);
511
}
512
// Clear the last space.
513
if (pos > temp)
514
*(pos - 1) = '\0';
515
516
System_CopyStringToClipboard(temp);
517
delete[] temp;
518
}
519
520
if (ImGui::MenuItem("Copy value (float32)")) {
521
char temp[64];
522
snprintf(temp, sizeof(temp), "%f", Memory::IsValidAddress(curAddress_) ? Memory::Read_Float(curAddress_) : NAN);
523
System_CopyStringToClipboard(temp);
524
}
525
/*
526
case ID_MEMVIEW_EXTENTBEGIN:
527
{
528
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
529
uint32_t addr = curAddress_;
530
for (MemBlockInfo info : memRangeInfo) {
531
addr = info.start;
532
}
533
gotoAddr(addr);
534
break;
535
}
536
537
case ID_MEMVIEW_EXTENTEND:
538
{
539
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
540
uint32_t addr = curAddress_;
541
for (MemBlockInfo info : memRangeInfo) {
542
addr = info.start + info.size - 1;
543
}
544
gotoAddr(addr);
545
break;
546
}
547
*/
548
if (ImGui::MenuItem("Copy address")) {
549
char temp[24];
550
snprintf(temp, sizeof(temp), "0x%08X", curAddress_);
551
System_CopyStringToClipboard(temp);
552
}
553
ImGui::SeparatorText("Misc");
554
if (ImGui::MenuItem("Goto in disasm")) {
555
/*
556
if (disasmWindow) {
557
disasmWindow->Goto(curAddress_);
558
disasmWindow->Show(true);
559
}
560
*/
561
}
562
ImGui::EndPopup();
563
}
564
}
565
566
void ImMemView::onMouseUp(float x, float y, int button) {
567
if (button == 2) {
568
569
// HMENU menu = GetContextMenu(ContextMenuID::MEMVIEW);
570
// EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_16, enable16 ? MF_ENABLED : MF_GRAYED);
571
// EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_32, enable32 ? MF_ENABLED : MF_GRAYED);
572
// EnableMenuItem(menu, ID_MEMVIEW_COPYFLOAT_32, enable32 ? MF_ENABLED : MF_GRAYED);
573
574
}
575
576
/*
577
int x = LOWORD(lParam);
578
int y = HIWORD(lParam);
579
ReleaseCapture();
580
*/
581
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
582
}
583
584
void ImMemView::onMouseMove(float x, float y, int button) {
585
if (Achievements::HardcoreModeActive())
586
return;
587
588
if (button & 1) {
589
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
590
}
591
}
592
593
void ImMemView::updateStatusBarText() {
594
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
595
char text[512];
596
snprintf(text, sizeof(text), "%08x", curAddress_);
597
// There should only be one.
598
for (MemBlockInfo info : memRangeInfo) {
599
snprintf(text, sizeof(text), "%08x - %s %08x-%08x (PC %08x / %lld ticks)", curAddress_, info.tag.c_str(), info.start, info.start + info.size, info.pc, (long long)info.ticks);
600
}
601
statusMessage_ = text;
602
}
603
604
void ImMemView::UpdateSelectRange(uint32_t target, GotoMode mode) {
605
if (mode == GotoMode::FROM_CUR && lastSelectReset_ == 0) {
606
lastSelectReset_ = curAddress_;
607
}
608
609
switch (mode) {
610
case GotoMode::RESET:
611
selectRangeStart_ = target;
612
selectRangeEnd_ = target + 1;
613
lastSelectReset_ = target;
614
break;
615
616
case GotoMode::RESET_IF_OUTSIDE:
617
if (target < selectRangeStart_ || target >= selectRangeEnd_) {
618
selectRangeStart_ = target;
619
selectRangeEnd_ = target + 1;
620
lastSelectReset_ = target;
621
}
622
break;
623
624
case GotoMode::FROM_CUR:
625
selectRangeStart_ = lastSelectReset_ > target ? target : lastSelectReset_;
626
selectRangeEnd_ = selectRangeStart_ == lastSelectReset_ ? target + 1 : lastSelectReset_ + 1;
627
break;
628
629
case GotoMode::EXTEND:
630
if (target < selectRangeStart_)
631
selectRangeStart_ = target;
632
if (target > selectRangeEnd_)
633
selectRangeEnd_ = target;
634
break;
635
}
636
curAddress_ = target;
637
}
638
639
void ImMemView::GotoPoint(int x, int y, GotoMode mode) {
640
int line = y / rowHeight_;
641
int lineAddress = windowStart_ + line * rowSize_;
642
643
if (displayOffsetScale_) {
644
// ignore clicks on the offset space
645
if (line < offsetSpace) {
646
updateStatusBarText();
647
return;
648
}
649
// since each row has been written X rows down from where the window expected it to be written the target of the clicks must be adjusted
650
lineAddress -= rowSize_ * offsetSpace;
651
}
652
653
uint32_t target = curAddress_;
654
uint32_t targetNibble = selectedNibble_;
655
bool targetAscii = asciiSelected_;
656
if (x >= asciiStartX_) {
657
int col = (x - asciiStartX_) / (charWidth_ + 2);
658
if (col >= rowSize_)
659
return;
660
661
targetAscii = true;
662
target = lineAddress + col;
663
targetNibble = 0;
664
} else if (x >= hexStartX_) {
665
int col = (x - hexStartX_) / charWidth_;
666
if ((col / 3) >= rowSize_)
667
return;
668
669
switch (col % 3) {
670
case 0: targetNibble = 0; break;
671
case 1: targetNibble = 1; break;
672
// case 2: return; // don't change position when clicking on the space
673
case 2: targetNibble = 0; break; // TODO: split the difference? Anyway, this feels better.
674
}
675
676
targetAscii = false;
677
target = lineAddress + col / 3;
678
}
679
680
if (target != curAddress_ || targetNibble != selectedNibble_ || targetAscii != asciiSelected_) {
681
selectedNibble_ = targetNibble;
682
asciiSelected_ = targetAscii;
683
UpdateSelectRange(target, mode);
684
updateStatusBarText();
685
}
686
}
687
688
void ImMemView::gotoAddr(unsigned int addr) {
689
int lines = visibleRows_;
690
u32 windowEnd = windowStart_ + lines * rowSize_;
691
692
curAddress_ = addr;
693
lastSelectReset_ = curAddress_;
694
selectRangeStart_ = curAddress_;
695
selectRangeEnd_ = curAddress_ + 1;
696
selectedNibble_ = 0;
697
698
if (curAddress_ < windowStart_ || curAddress_ >= windowEnd) {
699
windowStart_ = curAddress_ & ~15;
700
}
701
702
updateStatusBarText();
703
}
704
705
void ImMemView::ScrollWindow(int lines, GotoMode mode) {
706
windowStart_ += lines * rowSize_;
707
708
UpdateSelectRange(curAddress_ + lines * rowSize_, mode);
709
710
updateStatusBarText();
711
}
712
713
void ImMemView::ScrollCursor(int bytes, GotoMode mode) {
714
if (!asciiSelected_ && bytes == 1) {
715
if (selectedNibble_ == 0) {
716
selectedNibble_ = 1;
717
bytes = 0;
718
} else {
719
selectedNibble_ = 0;
720
}
721
} else if (!asciiSelected_ && bytes == -1) {
722
if (selectedNibble_ == 0) {
723
selectedNibble_ = 1;
724
} else {
725
selectedNibble_ = 0;
726
bytes = 0;
727
}
728
}
729
730
UpdateSelectRange(curAddress_ + bytes, mode);
731
732
u32 windowEnd = windowStart_ + visibleRows_ * rowSize_;
733
if (curAddress_ < windowStart_) {
734
windowStart_ = curAddress_ & ~15;
735
} else if (curAddress_ >= windowEnd) {
736
windowStart_ = (curAddress_ - (visibleRows_ - 1) * rowSize_) & ~15;
737
}
738
739
updateStatusBarText();
740
}
741
742
743
bool ImMemView::ParseSearchString(const char* query, MemorySearchType mode) {
744
memSearch_.data.clear();
745
switch (mode) {
746
case FLOAT_32:{
747
float flt = std::strtof(query, nullptr);
748
uint32_t* tmp = (uint32_t*) &flt;
749
for (int i = 0; i < 4; i++) {
750
memSearch_.data.push_back((uint8_t)(*tmp & 0xff));
751
*tmp >>= 8;
752
}
753
}
754
break;
755
case BITS_8:
756
case BITS_16:
757
case BITS_32:
758
case BITS_64:{
759
long long ll = std::strtoll(query, nullptr, 0);
760
int bytes = 1 << mode;
761
for (int i = 0; i < bytes; i++) {
762
memSearch_.data.push_back((uint8_t)(ll & 0xff));
763
ll >>= 8;
764
}
765
}
766
break;
767
case STRING:{
768
while(*query != 0) {
769
memSearch_.data.push_back(*query);
770
query++;
771
}
772
}
773
break;
774
case STRING_16:{
775
// for now limited to ascii.
776
while(*query != 0) {
777
char c = *query;
778
if (c > 0x1f && c < 0x7f) {
779
memSearch_.data.push_back(*query);
780
memSearch_.data.push_back(0);
781
}
782
query++;
783
}
784
}break;
785
case BYTE_SEQ:{
786
char* s = strdup(query);
787
size_t len = strlen(s);
788
for (size_t index = 0; index < len; ) {
789
if (isspace(s[index])) {
790
index++;
791
continue;
792
}
793
u8 value = 0;
794
for (int i = 0; i < 2 && index < len; i++) {
795
char c = tolower(s[index++]);
796
if (c >= 'a' && c <= 'f') {
797
value |= (c - 'a' + 10) << (1 - i) * 4;
798
} else if (c >= '0' && c <= '9') {
799
value |= (c - '0') << (1 - i) * 4;
800
} else {
801
return false;
802
}
803
}
804
memSearch_.data.push_back(value);
805
}
806
free(s);
807
}
808
break;
809
default:
810
break;
811
}
812
813
memSearch_.fast_data = memSearch_.data.data();
814
memSearch_.fast_size = memSearch_.data.size();
815
816
return true;
817
}
818
/*
819
std::vector<u32> ImMemView::searchString(const std::string &searchQuery) {
820
std::vector<u32> searchResAddrs;
821
822
std::vector<u8> searchData;
823
if (!ParseSearchString(searchQuery, false, searchData))
824
return searchResAddrs;
825
826
if (searchData.empty())
827
return searchResAddrs;
828
829
std::vector<std::pair<u32, u32>> memoryAreas;
830
memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());
831
// Ignore the video memory mirrors.
832
memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);
833
memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());
834
835
for (const auto &area : memoryAreas) {
836
const u32 segmentStart = area.first;
837
const u32 segmentEnd = area.second - (u32)searchData.size();
838
839
for (u32 pos = segmentStart; pos < segmentEnd; pos++) {
840
if ((pos % 256) == 0 && ImGui::IsKeyDown(ImGuiKey_Escape)) {
841
return searchResAddrs;
842
}
843
844
const u8 *ptr = Memory::GetPointerUnchecked(pos);
845
if (memcmp(ptr, searchData.data(), searchData.size()) == 0) {
846
searchResAddrs.push_back(pos);
847
}
848
}
849
}
850
851
return searchResAddrs;
852
};
853
*/
854
MemorySearchStatus ImMemView::search(bool continueSearch) {
855
if (!PSP_IsInited())
856
return SEARCH_PSP_NOT_INIT;
857
if (continueSearch == false /*|| searchQuery_.empty()*/) {
858
memSearch_.searchAddress = curAddress_ + 1;
859
} else {
860
memSearch_.searchAddress = memSearch_.matchAddress + 1;
861
}
862
863
std::vector<std::pair<u32, u32>> memoryAreas;
864
// Ignore the video memory mirrors.
865
memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);
866
memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());
867
memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());
868
869
memSearch_.searching = true;
870
// NOTE:
871
// this currently stops at the first found value
872
// we could look for all matches
873
// and put them in a matches list
874
// and display as;
875
// <match idx><memory region><addr><what matched>
876
for (size_t i = 0; i < memoryAreas.size(); i++) {
877
memSearch_.segmentStart = memoryAreas[i].first;
878
memSearch_.segmentEnd = memoryAreas[i].second;
879
880
// better safe than sorry, I guess
881
if (!Memory::IsValidAddress(memSearch_.segmentStart))
882
continue;
883
const u8 *dataPointer = Memory::GetPointerUnchecked(memSearch_.segmentStart);
884
885
if (memSearch_.searchAddress < memSearch_.segmentStart)
886
memSearch_.searchAddress = memSearch_.segmentStart;
887
if (memSearch_.searchAddress >= memSearch_.segmentEnd)
888
continue;
889
890
int index = memSearch_.searchAddress - memSearch_.segmentStart;
891
int endIndex = memSearch_.segmentEnd - memSearch_.segmentStart - (int)memSearch_.fast_size;
892
while (index < endIndex) {
893
// cancel search
894
if ((index % 256) == 0 && ImGui::IsKeyDown(ImGuiKey_Escape)) {
895
memSearch_.searching = false;
896
return SEARCH_CANCEL;
897
}
898
if (memcmp(&dataPointer[index], memSearch_.fast_data, memSearch_.fast_size) == 0) {
899
memSearch_.matchAddress = index + memSearch_.segmentStart;
900
memSearch_.searching = false;
901
gotoAddr(memSearch_.matchAddress);
902
return SEARCH_OK;
903
}
904
index++;
905
}
906
}
907
908
statusMessage_ = "Not found";
909
memSearch_.searching = false;
910
return SEARCH_NOTFOUND;
911
}
912
913
// for now type as int
914
void ImMemView::initSearch(const char* str, MemorySearchType type) {
915
memSearch_.status = SEARCH_INITIAL;
916
ParseSearchString(str, type);
917
memSearch_.status = search(false);
918
}
919
void ImMemView::continueSearch() {
920
memSearch_.status = SEARCH_INITIAL;
921
memSearch_.status = search(true);
922
923
}
924
void ImMemView::drawOffsetScale(ImDrawList *drawList) {
925
int currentX = addressStartX_;
926
927
drawList->AddText(ImVec2(currentX, offsetPositionY_), 0xFF600000, "Offset");
928
// SetTextColor(hdc, 0x600000);
929
// TextOutA(hdc, currentX, offsetPositionY_, "Offset", 6);
930
931
// the start offset, the size of the hex addresses and one space
932
currentX = addressStartX_ + ((8 + 1) * charWidth_);
933
934
char temp[64];
935
for (int i = 0; i < 16; i++) {
936
snprintf(temp, sizeof(temp), "%02X", i);
937
drawList->AddText(ImVec2(currentX, offsetPositionY_), 0xFF600000, temp);
938
currentX += 3 * charWidth_; // hex and space
939
}
940
}
941
942
void ImMemView::toggleOffsetScale(CommonToggles toggle) {
943
if (toggle == On)
944
displayOffsetScale_ = true;
945
else if (toggle == Off)
946
displayOffsetScale_ = false;
947
948
updateStatusBarText();
949
}
950
951
void ImMemView::setHighlightType(MemBlockFlags flags) {
952
if (highlightFlags_ != flags) {
953
highlightFlags_ = flags;
954
updateStatusBarText();
955
}
956
}
957
958
void ImMemView::toggleDrawZeroDark(bool toggle) {
959
drawZeroDark_ = toggle;
960
}
961
962
MemorySearchStatus ImMemView::SearchStatus() {
963
return memSearch_.status;
964
}
965
uint32_t ImMemView::SearchMatchAddress() {
966
return memSearch_.matchAddress;
967
}
968
bool ImMemView::SearchEmpty() {
969
return memSearch_.data.empty();
970
}
971
972
void ImMemDumpWindow::Draw(ImConfig &cfg, MIPSDebugInterface *debug) {
973
ImGui::SetNextWindowSize(ImVec2(200, 300), ImGuiCond_FirstUseEver);
974
975
if (!ImGui::Begin(Title(), &cfg.memDumpOpen)) {
976
ImGui::End();
977
return;
978
}
979
980
if (ImGui::Button("User RAM (0x08800000)")) {
981
address_ = 0x08800000;
982
size_ = 0x01800000; // 24MB
983
}
984
985
ImGui::InputScalar("Starting address", ImGuiDataType_U32, &address_, NULL, NULL, "%08X");
986
ImGui::InputScalar("Size", ImGuiDataType_U32, &size_, NULL, NULL, "%08X");
987
988
ImGui::InputText("Filename", filename_, ARRAY_SIZE(filename_));
989
990
const char* modes[] = { "Raw", "Disassembly" };
991
int modeIndex = static_cast<int>(mode_);
992
if (ImGui::Combo("Memory Dump Mode", &modeIndex, modes, IM_ARRAYSIZE(modes))) {
993
// Update the current mode if the user selects a new one
994
mode_ = static_cast<MemDumpMode>(modeIndex);
995
}
996
997
if (ImGui::Button(mode_ == MemDumpMode::Raw ? "Dump to file" : "Disassemble to file")) {
998
uint32_t validSize = Memory::ValidSize(address_, size_);
999
if (validSize != size_) {
1000
errorMsg_ = "Address range out of bounds";
1001
if (Memory::IsValidAddress(address_)) {
1002
size_ = validSize;
1003
}
1004
} else if (strlen(filename_) == 0) {
1005
errorMsg_ = "Please specify a valid filename";
1006
} else {
1007
FILE *file = File::OpenCFile(Path(filename_), "wb");
1008
if (!file) {
1009
errorMsg_ = "Couldn't open file for writing";
1010
} else {
1011
if (mode_ == MemDumpMode::Raw) {
1012
const uint8_t *ptr = Memory::GetPointer(address_);
1013
fwrite(ptr, 1, size_, file);
1014
} else {
1015
std::string disassembly = DisassembleRange(address_, size_, true, debug);
1016
fprintf(file, "%s", disassembly.c_str());
1017
}
1018
errorMsg_.clear();
1019
fclose(file);
1020
}
1021
}
1022
}
1023
1024
if (!errorMsg_.empty()) {
1025
ImGui::TextUnformatted(errorMsg_.data(), errorMsg_.data() + errorMsg_.size());
1026
}
1027
1028
ImGui::End();
1029
}
1030
1031
#define OPEN_SEARCH_FORM() \
1032
if (!(searchFormFlags_ & ImGuiTreeNodeFlags_DefaultOpen)) \
1033
searchFormFlags_ ^= ImGuiTreeNodeFlags_DefaultOpen; \
1034
focusSearchValueInput_ = true
1035
1036
void ImMemWindow::ProcessKeyboardShortcuts() {
1037
if (ImGui::IsKeyPressed(ImGuiKey_F)) {
1038
OPEN_SEARCH_FORM();
1039
selectedSearchType_ = STRING;
1040
return;
1041
}
1042
1043
if (ImGui::IsKeyPressed(ImGuiKey_B)) {
1044
OPEN_SEARCH_FORM();
1045
selectedSearchType_ = BITS_8;
1046
// XXX: ctrl+b already restarts the emulation
1047
// SDL/SDLMain.cpp
1048
return;
1049
}
1050
1051
if (ImGui::IsKeyPressed(ImGuiKey_H)) {
1052
OPEN_SEARCH_FORM();
1053
selectedSearchType_ = BITS_16;
1054
return;
1055
}
1056
if (ImGui::IsKeyPressed(ImGuiKey_U)) {
1057
OPEN_SEARCH_FORM();
1058
selectedSearchType_ = BITS_32;
1059
return;
1060
}
1061
if (ImGui::IsKeyPressed(ImGuiKey_X)) {
1062
OPEN_SEARCH_FORM();
1063
selectedSearchType_ = BYTE_SEQ;
1064
return;
1065
}
1066
if (ImGui::IsKeyPressed(ImGuiKey_N)) {
1067
memView_.continueSearch();
1068
return;
1069
}
1070
}
1071
1072
void ImMemWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImControl &control, int index) {
1073
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
1074
if (!ImGui::Begin(Title(index), &cfg.memViewOpen[index])) {
1075
ImGui::End();
1076
return;
1077
}
1078
1079
// Toolbars
1080
1081
ImGui::InputScalar("Go to addr: ", ImGuiDataType_U32, &gotoAddr_, NULL, NULL, "%08X");
1082
if (ImGui::IsItemDeactivatedAfterEdit()) {
1083
memView_.gotoAddr(gotoAddr_);
1084
}
1085
ImGui::SameLine();
1086
if (ImGui::SmallButton("Go")) {
1087
memView_.gotoAddr(gotoAddr_);
1088
}
1089
ImGui::SameLine();
1090
if (ImGui::SmallButton("Settings")) {
1091
ImGui::OpenPopup("disSettings");
1092
}
1093
if (ImGui::BeginPopup("disSettings")) {
1094
if (ImGui::Checkbox("Darken Zeros", &drawZeroDark_)) {
1095
memView_.toggleDrawZeroDark(drawZeroDark_);
1096
}
1097
if (ImGui::Checkbox("Edit Memory", &editableMemory_)) {
1098
memView_.toggleEditableMemory(editableMemory_);
1099
}
1100
ImGui::EndPopup();
1101
}
1102
ImGui::Separator();
1103
if (ImGui::CollapsingHeader("Memory Search", searchFormFlags_)) {
1104
ImGui::Combo("type", reinterpret_cast<int*>(&selectedSearchType_), searchtypes, IM_ARRAYSIZE(searchtypes));
1105
1106
if (focusSearchValueInput_) {
1107
ImGui::SetKeyboardFocusHere(0);
1108
focusSearchValueInput_ = false;
1109
}
1110
if (ImGui::InputText("data", searchStr_, IM_ARRAYSIZE(searchStr_), ImGuiInputTextFlags_EnterReturnsTrue) || ImGui::Button("Search")) {
1111
memView_.initSearch(searchStr_, selectedSearchType_);
1112
// TODO: transfer focus on the memview.
1113
}
1114
ImGui::SameLine();
1115
1116
bool isEmpty = memView_.SearchEmpty();
1117
if (isEmpty)
1118
ImGui::BeginDisabled(true);
1119
if (ImGui::Button("Next")) {
1120
memView_.continueSearch();
1121
}
1122
if (isEmpty)
1123
ImGui::EndDisabled();
1124
int searchStatus = memView_.SearchStatus();
1125
if (searchStatus != 0)
1126
ImGui::SameLine();
1127
switch (searchStatus) {
1128
case SEARCH_OK:
1129
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "match found 0x%x", memView_.SearchMatchAddress());
1130
break;
1131
case SEARCH_NOTFOUND:
1132
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "no match found");
1133
break;
1134
default:
1135
break;
1136
}
1137
}
1138
ImGui::Separator();
1139
1140
ImVec2 size(0, -ImGui::GetFrameHeightWithSpacing());
1141
1142
auto node = [&](const char *title, uint32_t start, uint32_t len) {
1143
if (ImGui::TreeNode(title)) {
1144
if (ImGui::Selectable("(start)", cfg.selectedMemoryBlock == start)) {
1145
cfg.selectedMemoryBlock = start;
1146
GotoAddr(start);
1147
}
1148
const std::vector<MemBlockInfo> info = FindMemInfo(start, len);
1149
for (auto &iter : info) {
1150
ImGui::PushID(iter.start);
1151
if (ImGui::Selectable(iter.tag.c_str(), cfg.selectedMemoryBlock == iter.start)) {
1152
cfg.selectedMemoryBlock = iter.start;
1153
GotoAddr(iter.start);
1154
}
1155
ImGui::PopID();
1156
}
1157
const u32 end = start + len;
1158
if (ImGui::Selectable("(end)", cfg.selectedMemoryBlock == end)) {
1159
cfg.selectedMemoryBlock = end;
1160
GotoAddr(end);
1161
}
1162
ImGui::TreePop();
1163
}
1164
};
1165
1166
// Main views - list of interesting addresses to the left, memory view to the right.
1167
if (ImGui::BeginChild("addr_list", ImVec2(200.0f, size.y), ImGuiChildFlags_ResizeX)) {
1168
node("Scratch", 0x00010000, 0x00004000);
1169
node("Kernel RAM", 0x08000000, 0x00800000);
1170
node("User RAM", 0x08800000, 0x01800000);
1171
node("VRAM", 0x04000000, 0x00200000);
1172
}
1173
1174
ImGui::EndChild();
1175
1176
ImGui::SameLine();
1177
1178
if (ImGui::BeginChild("memview", size)) {
1179
memView_.Draw(ImGui::GetWindowDrawList());
1180
}
1181
ImGui::EndChild();
1182
1183
StatusBar(memView_.StatusMessage());
1184
1185
ImGui::End();
1186
ProcessKeyboardShortcuts();
1187
}
1188
1189
const char *ImMemWindow::Title(int index) {
1190
static const char *const titles[4] = { "Memory 1", "Memory 2", "Memory 3", "Memory 4" };
1191
return titles[index];
1192
}
1193
1194