CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/Debugger/CtrlMemView.cpp
Views: 1401
1
#include <cctype>
2
#include <tchar.h>
3
#include <cmath>
4
#include <iomanip>
5
#include <sstream>
6
7
#include "ext/xxhash.h"
8
#include "Common/StringUtils.h"
9
#include "Core/Config.h"
10
#include "Core/MemMap.h"
11
#include "Core/Reporting.h"
12
#include "Windows/W32Util/ContextMenu.h"
13
#include "Windows/W32Util/Misc.h"
14
#include "Windows/InputBox.h"
15
#include "Windows/main.h"
16
#include "Windows/resource.h"
17
#include "Common/System/Display.h"
18
19
#include "Debugger_Disasm.h"
20
#include "DebuggerShared.h"
21
#include "CtrlMemView.h"
22
#include "DumpMemoryWindow.h"
23
24
wchar_t CtrlMemView::szClassName[] = L"CtrlMemView";
25
26
static constexpr UINT_PTR IDT_REDRAW_DELAYED = 0xC0DE0001;
27
static constexpr UINT REDRAW_DELAY = 1000 / 60;
28
// We also redraw regularly, since data changes during runtime.
29
static constexpr UINT_PTR IDT_REDRAW_AUTO = 0xC0DE0002;
30
static constexpr UINT REDRAW_INTERVAL = 1000;
31
32
CtrlMemView::CtrlMemView(HWND _wnd) {
33
wnd=_wnd;
34
SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)this);
35
SetWindowLong(wnd, GWL_STYLE, GetWindowLong(wnd,GWL_STYLE) | WS_VSCROLL);
36
SetScrollRange(wnd, SB_VERT, -1,1,TRUE);
37
38
const float fontScale = 1.0f / g_display.dpi_scale_real_y;
39
charWidth_ = g_Config.iFontWidth * fontScale;
40
rowHeight_ = g_Config.iFontHeight * fontScale;
41
offsetPositionY_ = offsetLine * rowHeight_;
42
43
font = CreateFont(rowHeight_, charWidth_, 0, 0,
44
FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,
45
L"Lucida Console");
46
underlineFont = CreateFont(rowHeight_, charWidth_, 0, 0,
47
FW_DONTCARE, FALSE, TRUE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,
48
L"Lucida Console");
49
50
windowStart_ = curAddress_;
51
selectRangeStart_ = curAddress_;
52
selectRangeEnd_ = curAddress_ + 1;
53
lastSelectReset_ = curAddress_;
54
55
addressStartX_ = charWidth_;
56
hexStartX_ = addressStartX_ + 9 * charWidth_;
57
asciiStartX_ = hexStartX_ + (rowSize_ * 3 + 1) * charWidth_;
58
59
// set redraw timer
60
SetTimer(wnd, IDT_REDRAW_AUTO, REDRAW_INTERVAL, nullptr);
61
}
62
63
CtrlMemView::~CtrlMemView() {
64
DeleteObject(font);
65
DeleteObject(underlineFont);
66
}
67
68
void CtrlMemView::init() {
69
WNDCLASSEX wc;
70
71
wc.cbSize = sizeof(wc);
72
wc.lpszClassName = szClassName;
73
wc.hInstance = GetModuleHandle(0);
74
wc.lpfnWndProc = CtrlMemView::wndProc;
75
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
76
wc.hIcon = 0;
77
wc.lpszMenuName = 0;
78
wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);
79
wc.style = 0;
80
wc.cbClsExtra = 0;
81
wc.cbWndExtra = sizeof( CtrlMemView * );
82
wc.hIconSm = 0;
83
84
85
RegisterClassEx(&wc);
86
}
87
88
void CtrlMemView::deinit() {
89
//UnregisterClass(szClassName, hInst)
90
}
91
92
93
LRESULT CALLBACK CtrlMemView::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
94
CtrlMemView *ccp = CtrlMemView::getFrom(hwnd);
95
static bool lmbDown=false,rmbDown=false;
96
97
switch(msg) {
98
case WM_NCCREATE:
99
// Allocate a new CustCtrl structure for this window.
100
ccp = new CtrlMemView(hwnd);
101
102
// Continue with window creation.
103
return ccp != NULL;
104
105
// Clean up when the window is destroyed.
106
case WM_NCDESTROY:
107
delete ccp;
108
break;
109
case WM_SETFONT:
110
break;
111
case WM_SIZE:
112
ccp->redraw();
113
break;
114
case WM_PAINT:
115
ccp->onPaint(wParam,lParam);
116
break;
117
case WM_VSCROLL:
118
ccp->onVScroll(wParam,lParam);
119
break;
120
case WM_MOUSEWHEEL:
121
if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) {
122
ccp->ScrollWindow(-3, GotoModeFromModifiers(false));
123
} else if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) {
124
ccp->ScrollWindow(3, GotoModeFromModifiers(false));
125
}
126
break;
127
case WM_ERASEBKGND:
128
return FALSE;
129
case WM_KEYDOWN:
130
ccp->onKeyDown(wParam,lParam);
131
return 0;
132
case WM_CHAR:
133
ccp->onChar(wParam,lParam);
134
return 0;
135
case WM_KEYUP:
136
return 0;
137
case WM_LBUTTONDOWN: SetFocus(hwnd); lmbDown=true; ccp->onMouseDown(wParam,lParam,1); break;
138
case WM_RBUTTONDOWN: SetFocus(hwnd); rmbDown=true; ccp->onMouseDown(wParam,lParam,2); break;
139
case WM_MOUSEMOVE: ccp->onMouseMove(wParam,lParam,(lmbDown?1:0) | (rmbDown?2:0)); break;
140
case WM_LBUTTONUP: lmbDown=false; ccp->onMouseUp(wParam,lParam,1); break;
141
case WM_RBUTTONUP: rmbDown=false; ccp->onMouseUp(wParam,lParam,2); break;
142
case WM_SETFOCUS:
143
SetFocus(hwnd);
144
ccp->hasFocus_ = true;
145
ccp->redraw();
146
break;
147
case WM_KILLFOCUS:
148
ccp->hasFocus_ = false;
149
ccp->redraw();
150
break;
151
case WM_GETDLGCODE: // we want to process the arrow keys and all characters ourselves
152
return DLGC_WANTARROWS|DLGC_WANTCHARS|DLGC_WANTTAB;
153
break;
154
case WM_TIMER:
155
// This is actually delayed too, using another timer. That way we won't update twice.
156
if (wParam == IDT_REDRAW_AUTO && IsWindowVisible(ccp->wnd))
157
ccp->redraw();
158
159
if (wParam == IDT_REDRAW_DELAYED) {
160
InvalidateRect(hwnd, nullptr, FALSE);
161
UpdateWindow(hwnd);
162
ccp->redrawScheduled_ = false;
163
KillTimer(hwnd, wParam);
164
}
165
break;
166
default:
167
break;
168
}
169
170
return DefWindowProc(hwnd, msg, wParam, lParam);
171
}
172
173
174
CtrlMemView *CtrlMemView::getFrom(HWND hwnd) {
175
return (CtrlMemView *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
176
}
177
178
179
void CtrlMemView::onPaint(WPARAM wParam, LPARAM lParam) {
180
auto memLock = Memory::Lock();
181
182
// draw to a bitmap for double buffering
183
PAINTSTRUCT ps;
184
HDC actualHdc = BeginPaint(wnd, &ps);
185
HDC hdc = CreateCompatibleDC(actualHdc);
186
HBITMAP hBM = CreateCompatibleBitmap(actualHdc, rect_.right - rect_.left, rect_.bottom - rect_.top);
187
SelectObject(hdc, hBM);
188
189
SetBkMode(hdc,OPAQUE);
190
HPEN standardPen = CreatePen(0,0,0xFFFFFF);
191
HBRUSH standardBrush = CreateSolidBrush(0xFFFFFF);
192
COLORREF standardBG = GetBkColor(hdc);
193
194
HPEN oldPen = (HPEN) SelectObject(hdc,standardPen);
195
HBRUSH oldBrush = (HBRUSH) SelectObject(hdc,standardBrush);
196
HFONT oldFont = (HFONT) SelectObject(hdc,(HGDIOBJ)font);
197
198
// white background
199
SelectObject(hdc,standardPen);
200
SelectObject(hdc,standardBrush);
201
Rectangle(hdc, 0, 0, rect_.right, rect_.bottom);
202
203
if (displayOffsetScale_)
204
drawOffsetScale(hdc);
205
206
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, windowStart_, (visibleRows_ + 1) * rowSize_);
207
208
COLORREF lastTextCol = 0x000000;
209
COLORREF lastBGCol = standardBG;
210
auto setTextColors = [&](COLORREF fg, COLORREF bg) {
211
if (lastTextCol != fg) {
212
SetTextColor(hdc, fg);
213
lastTextCol = fg;
214
}
215
if (lastBGCol != bg) {
216
SetBkColor(hdc, bg);
217
lastBGCol = bg;
218
}
219
};
220
221
_assert_msg_(((windowStart_ | rowSize_) & 3) == 0, "readMemory() can't handle unaligned reads");
222
223
// draw one extra row that may be partially visible
224
for (int i = 0; i < visibleRows_ + 1; i++) {
225
int rowY = rowHeight_ * i;
226
// Skip the first X rows to make space for the offsets.
227
if (displayOffsetScale_)
228
rowY += rowHeight_ * offsetSpace;
229
230
char temp[32];
231
uint32_t address = windowStart_ + i * rowSize_;
232
snprintf(temp, sizeof(temp), "%08X", address);
233
234
setTextColors(0x600000, standardBG);
235
TextOutA(hdc, addressStartX_, rowY, temp, (int)strlen(temp));
236
237
union {
238
uint32_t words[4];
239
uint8_t bytes[16];
240
} memory;
241
int valid = debugger_ != nullptr && debugger_->isAlive() ? Memory::ValidSize(address, 16) / 4 : 0;
242
for (int i = 0; i < valid; ++i) {
243
memory.words[i] = debugger_->readMemory(address + i * 4);
244
}
245
246
for (int j = 0; j < rowSize_; j++) {
247
const uint32_t byteAddress = (address + j) & ~0xC0000000;
248
std::string tag;
249
bool tagContinues = false;
250
for (auto info : memRangeInfo) {
251
if (info.start <= byteAddress && info.start + info.size > byteAddress) {
252
tag = info.tag;
253
tagContinues = byteAddress + 1 < info.start + info.size;
254
}
255
}
256
257
int hexX = hexStartX_ + j * 3 * charWidth_;
258
int hexLen = 2;
259
int asciiX = asciiStartX_ + j * (charWidth_ + 2);
260
261
char c;
262
if (valid) {
263
snprintf(temp, sizeof(temp), "%02X ", memory.bytes[j]);
264
c = (char)memory.bytes[j];
265
if (memory.bytes[j] < 32 || memory.bytes[j] >= 128)
266
c = '.';
267
} else {
268
truncate_cpy(temp, "??");
269
c = '.';
270
}
271
272
COLORREF hexBGCol = standardBG;
273
COLORREF hexTextCol = 0x000000;
274
COLORREF continueBGCol = standardBG;
275
COLORREF asciiBGCol = standardBG;
276
COLORREF asciiTextCol = 0x000000;
277
int underline = -1;
278
279
if (address + j >= selectRangeStart_ && address + j < selectRangeEnd_ && !searching_) {
280
if (asciiSelected_) {
281
hexBGCol = 0xC0C0C0;
282
hexTextCol = 0x000000;
283
asciiBGCol = hasFocus_ ? 0xFF9933 : 0xC0C0C0;
284
asciiTextCol = hasFocus_ ? 0xFFFFFF : 0x000000;
285
} else {
286
hexBGCol = hasFocus_ ? 0xFF9933 : 0xC0C0C0;
287
hexTextCol = hasFocus_ ? 0xFFFFFF : 0x000000;
288
asciiBGCol = 0xC0C0C0;
289
asciiTextCol = 0x000000;
290
if (address + j == curAddress_)
291
underline = selectedNibble_;
292
}
293
if (!tag.empty() && tagContinues) {
294
continueBGCol = pickTagColor(tag);
295
}
296
} else if (!tag.empty()) {
297
hexBGCol = pickTagColor(tag);
298
continueBGCol = hexBGCol;
299
asciiBGCol = pickTagColor(tag);
300
hexLen = tagContinues ? 3 : 2;
301
}
302
303
setTextColors(hexTextCol, hexBGCol);
304
if (underline >= 0) {
305
SelectObject(hdc, underline == 0 ? (HGDIOBJ)underlineFont : (HGDIOBJ)font);
306
TextOutA(hdc, hexX, rowY, &temp[0], 1);
307
SelectObject(hdc, underline == 0 ? (HGDIOBJ)font : (HGDIOBJ)underlineFont);
308
TextOutA(hdc, hexX + charWidth_, rowY, &temp[1], 1);
309
SelectObject(hdc, (HGDIOBJ)font);
310
311
// If the tag keeps going, draw the BG too.
312
if (continueBGCol != standardBG) {
313
setTextColors(0x000000, continueBGCol);
314
TextOutA(hdc, hexX + charWidth_ * 2, rowY, &temp[2], 1);
315
}
316
} else {
317
if (continueBGCol != hexBGCol) {
318
TextOutA(hdc, hexX, rowY, temp, 2);
319
setTextColors(0x000000, continueBGCol);
320
TextOutA(hdc, hexX + charWidth_ * 2, rowY, &temp[2], 1);
321
} else {
322
TextOutA(hdc, hexX, rowY, temp, hexLen);
323
}
324
}
325
326
setTextColors(asciiTextCol, asciiBGCol);
327
TextOutA(hdc, asciiX, rowY, &c, 1);
328
}
329
}
330
331
setTextColors(0x000000, standardBG);
332
SelectObject(hdc,oldFont);
333
SelectObject(hdc,oldPen);
334
SelectObject(hdc,oldBrush);
335
336
// copy bitmap to the actual hdc
337
BitBlt(actualHdc, 0, 0, rect_.right, rect_.bottom, hdc, 0, 0, SRCCOPY);
338
DeleteObject(hBM);
339
DeleteDC(hdc);
340
341
DeleteObject(standardPen);
342
DeleteObject(standardBrush);
343
344
EndPaint(wnd, &ps);
345
}
346
347
void CtrlMemView::onVScroll(WPARAM wParam, LPARAM lParam) {
348
switch (wParam & 0xFFFF) {
349
case SB_LINEDOWN:
350
ScrollWindow(1, GotoModeFromModifiers(false));
351
break;
352
case SB_LINEUP:
353
ScrollWindow(-1, GotoModeFromModifiers(false));
354
break;
355
case SB_PAGEDOWN:
356
ScrollWindow(visibleRows_, GotoModeFromModifiers(false));
357
break;
358
case SB_PAGEUP:
359
ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));
360
break;
361
default:
362
return;
363
}
364
}
365
366
void CtrlMemView::onKeyDown(WPARAM wParam, LPARAM lParam) {
367
if (KeyDownAsync(VK_CONTROL)) {
368
switch (tolower(wParam & 0xFFFF)) {
369
case 'g':
370
{
371
u32 addr;
372
if (executeExpressionWindow(wnd, debugger_, addr) == false)
373
return;
374
gotoAddr(addr);
375
return;
376
}
377
break;
378
case 'f':
379
case 's':
380
search(false);
381
return;
382
case 'c':
383
search(true);
384
return;
385
}
386
}
387
388
switch (wParam & 0xFFFF) {
389
case VK_DOWN:
390
ScrollCursor(rowSize_, GotoModeFromModifiers(false));
391
break;
392
case VK_UP:
393
ScrollCursor(-rowSize_, GotoModeFromModifiers(false));
394
break;
395
case VK_LEFT:
396
ScrollCursor(-1, GotoModeFromModifiers(false));
397
break;
398
case VK_RIGHT:
399
ScrollCursor(1, GotoModeFromModifiers(false));
400
break;
401
case VK_NEXT:
402
ScrollWindow(visibleRows_, GotoModeFromModifiers(false));
403
break;
404
case VK_PRIOR:
405
ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));
406
break;
407
case VK_TAB:
408
SendMessage(GetParent(wnd),WM_DEB_TABPRESSED,0,0);
409
break;
410
default:
411
return;
412
}
413
}
414
415
void CtrlMemView::onChar(WPARAM wParam, LPARAM lParam) {
416
auto memLock = Memory::Lock();
417
if (!PSP_IsInited())
418
return;
419
420
if (KeyDownAsync(VK_CONTROL) || wParam == VK_TAB)
421
return;
422
423
if (!Memory::IsValidAddress(curAddress_)) {
424
ScrollCursor(1, GotoMode::RESET);
425
return;
426
}
427
428
bool active = Core_IsActive();
429
if (active)
430
Core_EnableStepping(true, "memory.access", curAddress_);
431
432
if (asciiSelected_) {
433
Memory::WriteUnchecked_U8((u8)wParam, curAddress_);
434
ScrollCursor(1, GotoMode::RESET);
435
} else {
436
wParam = tolower(wParam);
437
int inputValue = -1;
438
439
if (wParam >= '0' && wParam <= '9') inputValue = wParam - '0';
440
if (wParam >= 'a' && wParam <= 'f') inputValue = wParam -'a' + 10;
441
442
if (inputValue >= 0) {
443
int shiftAmount = (1 - selectedNibble_) * 4;
444
445
u8 oldValue = Memory::ReadUnchecked_U8(curAddress_);
446
oldValue &= ~(0xF << shiftAmount);
447
u8 newValue = oldValue | (inputValue << shiftAmount);
448
Memory::WriteUnchecked_U8(newValue, curAddress_);
449
ScrollCursor(1, GotoMode::RESET);
450
}
451
}
452
453
Reporting::NotifyDebugger();
454
if (active)
455
Core_EnableStepping(false);
456
}
457
458
void CtrlMemView::redraw() {
459
GetClientRect(wnd, &rect_);
460
visibleRows_ = rect_.bottom / rowHeight_;
461
462
if (displayOffsetScale_) {
463
// 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
464
visibleRows_ -= offsetSpace;
465
}
466
467
if (!redrawScheduled_) {
468
SetTimer(wnd, IDT_REDRAW_DELAYED, REDRAW_DELAY, nullptr);
469
redrawScheduled_ = true;
470
}
471
}
472
473
CtrlMemView::GotoMode CtrlMemView::GotoModeFromModifiers(bool isRightClick) {
474
GotoMode mode = GotoMode::RESET;
475
if (isRightClick) {
476
mode = GotoMode::RESET_IF_OUTSIDE;
477
} else if (KeyDownAsync(VK_SHIFT)) {
478
if (KeyDownAsync(VK_CONTROL))
479
mode = GotoMode::EXTEND;
480
else
481
mode = GotoMode::FROM_CUR;
482
}
483
return mode;
484
}
485
486
void CtrlMemView::onMouseDown(WPARAM wParam, LPARAM lParam, int button) {
487
int x = LOWORD(lParam);
488
int y = HIWORD(lParam);
489
490
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
491
}
492
493
void CtrlMemView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) {
494
if (button == 2) {
495
int32_t selectedSize = selectRangeEnd_ - selectRangeStart_;
496
bool enable16 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 1) == 0);
497
bool enable32 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 3) == 0);
498
499
HMENU menu = GetContextMenu(ContextMenuID::MEMVIEW);
500
EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_16, enable16 ? MF_ENABLED : MF_GRAYED);
501
EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_32, enable32 ? MF_ENABLED : MF_GRAYED);
502
EnableMenuItem(menu, ID_MEMVIEW_COPYFLOAT_32, enable32 ? MF_ENABLED : MF_GRAYED);
503
504
switch (TriggerContextMenu(ContextMenuID::MEMVIEW, wnd, ContextPoint::FromEvent(lParam))) {
505
case ID_MEMVIEW_DUMP:
506
{
507
DumpMemoryWindow dump(wnd, debugger_);
508
dump.exec();
509
break;
510
}
511
512
case ID_MEMVIEW_COPYVALUE_8:
513
{
514
auto memLock = Memory::Lock();
515
size_t tempSize = 3 * selectedSize + 1;
516
char *temp = new char[tempSize];
517
memset(temp, 0, tempSize);
518
519
// it's admittedly not really useful like this
520
if (asciiSelected_) {
521
for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {
522
uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : '.';
523
if (c < 32 || c >= 128)
524
c = '.';
525
temp[p - selectRangeStart_] = c;
526
}
527
} else {
528
char *pos = temp;
529
for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {
530
uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : 0xFF;
531
pos += snprintf(pos, tempSize - (pos - temp + 1), "%02X ", c);
532
}
533
// Clear the last space.
534
if (pos > temp)
535
*(pos - 1) = '\0';
536
}
537
W32Util::CopyTextToClipboard(wnd, temp);
538
delete[] temp;
539
}
540
break;
541
542
case ID_MEMVIEW_COPYVALUE_16:
543
{
544
auto memLock = Memory::Lock();
545
size_t tempSize = 5 * ((selectedSize + 1) / 2) + 1;
546
char *temp = new char[tempSize];
547
memset(temp, 0, tempSize);
548
549
char *pos = temp;
550
for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 2) {
551
uint16_t c = Memory::IsValidRange(p, 2) ? Memory::ReadUnchecked_U16(p) : 0xFFFF;
552
pos += snprintf(pos, tempSize - (pos - temp + 1), "%04X ", c);
553
}
554
// Clear the last space.
555
if (pos > temp)
556
*(pos - 1) = '\0';
557
558
W32Util::CopyTextToClipboard(wnd, temp);
559
delete[] temp;
560
}
561
break;
562
563
case ID_MEMVIEW_COPYVALUE_32:
564
{
565
auto memLock = Memory::Lock();
566
size_t tempSize = 9 * ((selectedSize + 3) / 4) + 1;
567
char *temp = new char[tempSize];
568
memset(temp, 0, tempSize);
569
570
char *pos = temp;
571
for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 4) {
572
uint32_t c = Memory::IsValidRange(p, 4) ? Memory::ReadUnchecked_U32(p) : 0xFFFFFFFF;
573
pos += snprintf(pos, tempSize - (pos - temp + 1), "%08X ", c);
574
}
575
// Clear the last space.
576
if (pos > temp)
577
*(pos - 1) = '\0';
578
579
W32Util::CopyTextToClipboard(wnd, temp);
580
delete[] temp;
581
}
582
break;
583
584
case ID_MEMVIEW_COPYFLOAT_32:
585
{
586
auto memLock = Memory::Lock();
587
std::ostringstream stream;
588
stream << (Memory::IsValidAddress(curAddress_) ? Memory::Read_Float(curAddress_) : NAN);
589
auto temp_string = stream.str();
590
W32Util::CopyTextToClipboard(wnd, temp_string.c_str());
591
}
592
break;
593
594
case ID_MEMVIEW_EXTENTBEGIN:
595
{
596
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
597
uint32_t addr = curAddress_;
598
for (MemBlockInfo info : memRangeInfo) {
599
addr = info.start;
600
}
601
gotoAddr(addr);
602
break;
603
}
604
605
case ID_MEMVIEW_EXTENTEND:
606
{
607
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
608
uint32_t addr = curAddress_;
609
for (MemBlockInfo info : memRangeInfo) {
610
addr = info.start + info.size - 1;
611
}
612
gotoAddr(addr);
613
break;
614
}
615
616
case ID_MEMVIEW_COPYADDRESS:
617
{
618
char temp[24];
619
snprintf(temp, sizeof(temp), "0x%08X", curAddress_);
620
W32Util::CopyTextToClipboard(wnd, temp);
621
}
622
break;
623
624
case ID_MEMVIEW_GOTOINDISASM:
625
if (disasmWindow) {
626
disasmWindow->Goto(curAddress_);
627
disasmWindow->Show(true);
628
}
629
break;
630
}
631
return;
632
}
633
634
int x = LOWORD(lParam);
635
int y = HIWORD(lParam);
636
ReleaseCapture();
637
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
638
}
639
640
void CtrlMemView::onMouseMove(WPARAM wParam, LPARAM lParam, int button) {
641
int x = LOWORD(lParam);
642
int y = HIWORD(lParam);
643
644
if (button & 1) {
645
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
646
}
647
}
648
649
void CtrlMemView::updateStatusBarText() {
650
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
651
652
char text[512];
653
snprintf(text, sizeof(text), "%08X", curAddress_);
654
// There should only be one.
655
for (MemBlockInfo info : memRangeInfo) {
656
snprintf(text, sizeof(text), "%08X - %s %08X-%08X (at PC %08X / %lld ticks)", curAddress_, info.tag.c_str(), info.start, info.start + info.size, info.pc, info.ticks);
657
}
658
659
SendMessage(GetParent(wnd), WM_DEB_SETSTATUSBARTEXT, 0, (LPARAM)text);
660
}
661
662
void CtrlMemView::UpdateSelectRange(uint32_t target, GotoMode mode) {
663
if (mode == GotoMode::FROM_CUR && lastSelectReset_ == 0) {
664
lastSelectReset_ = curAddress_;
665
}
666
667
switch (mode) {
668
case GotoMode::RESET:
669
selectRangeStart_ = target;
670
selectRangeEnd_ = target + 1;
671
lastSelectReset_ = target;
672
break;
673
674
case GotoMode::RESET_IF_OUTSIDE:
675
if (target < selectRangeStart_ || target >= selectRangeEnd_) {
676
selectRangeStart_ = target;
677
selectRangeEnd_ = target + 1;
678
lastSelectReset_ = target;
679
}
680
break;
681
682
case GotoMode::FROM_CUR:
683
selectRangeStart_ = lastSelectReset_ > target ? target : lastSelectReset_;
684
selectRangeEnd_ = selectRangeStart_ == lastSelectReset_ ? target + 1 : lastSelectReset_ + 1;
685
break;
686
687
case GotoMode::EXTEND:
688
if (target < selectRangeStart_)
689
selectRangeStart_ = target;
690
if (target > selectRangeEnd_)
691
selectRangeEnd_ = target;
692
break;
693
}
694
curAddress_ = target;
695
}
696
697
void CtrlMemView::GotoPoint(int x, int y, GotoMode mode) {
698
int line = y / rowHeight_;
699
int lineAddress = windowStart_ + line * rowSize_;
700
701
if (displayOffsetScale_) {
702
// ignore clicks on the offset space
703
if (line < offsetSpace) {
704
updateStatusBarText();
705
redraw();
706
return;
707
}
708
// 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
709
lineAddress -= rowSize_ * offsetSpace;
710
}
711
712
uint32_t target = curAddress_;
713
uint32_t targetNibble = selectedNibble_;
714
bool targetAscii = asciiSelected_;
715
if (x >= asciiStartX_) {
716
int col = (x - asciiStartX_) / (charWidth_ + 2);
717
if (col >= rowSize_)
718
return;
719
720
targetAscii = true;
721
target = lineAddress + col;
722
targetNibble = 0;
723
} else if (x >= hexStartX_) {
724
int col = (x - hexStartX_) / charWidth_;
725
if ((col/3) >= rowSize_)
726
return;
727
728
switch (col % 3) {
729
case 0: targetNibble = 0; break;
730
case 1: targetNibble = 1; break;
731
case 2: return; // don't change position when clicking on the space
732
}
733
734
targetAscii = false;
735
target = lineAddress + col / 3;
736
}
737
738
if (target != curAddress_ || targetNibble != selectedNibble_ || targetAscii != asciiSelected_) {
739
selectedNibble_ = targetNibble;
740
asciiSelected_ = targetAscii;
741
UpdateSelectRange(target, mode);
742
743
updateStatusBarText();
744
redraw();
745
}
746
}
747
748
void CtrlMemView::gotoAddr(unsigned int addr) {
749
int lines = rect_.bottom / rowHeight_;
750
u32 windowEnd = windowStart_ + lines * rowSize_;
751
752
curAddress_ = addr;
753
lastSelectReset_ = curAddress_;
754
selectRangeStart_ = curAddress_;
755
selectRangeEnd_ = curAddress_ + 1;
756
selectedNibble_ = 0;
757
758
if (curAddress_ < windowStart_ || curAddress_ >= windowEnd) {
759
windowStart_ = curAddress_ & ~15;
760
}
761
762
updateStatusBarText();
763
redraw();
764
}
765
766
void CtrlMemView::ScrollWindow(int lines, GotoMode mode) {
767
windowStart_ += lines * rowSize_;
768
769
UpdateSelectRange(curAddress_ + lines * rowSize_, mode);
770
771
updateStatusBarText();
772
redraw();
773
}
774
775
void CtrlMemView::ScrollCursor(int bytes, GotoMode mode) {
776
if (!asciiSelected_ && bytes == 1) {
777
if (selectedNibble_ == 0) {
778
selectedNibble_ = 1;
779
bytes = 0;
780
} else {
781
selectedNibble_ = 0;
782
}
783
} else if (!asciiSelected_ && bytes == -1) {
784
if (selectedNibble_ == 0) {
785
selectedNibble_ = 1;
786
} else {
787
selectedNibble_ = 0;
788
bytes = 0;
789
}
790
}
791
792
UpdateSelectRange(curAddress_ + bytes, mode);
793
794
u32 windowEnd = windowStart_ + visibleRows_ * rowSize_;
795
if (curAddress_ < windowStart_) {
796
windowStart_ = curAddress_ & ~15;
797
} else if (curAddress_ >= windowEnd) {
798
windowStart_ = (curAddress_ - (visibleRows_ - 1) * rowSize_) & ~15;
799
}
800
801
updateStatusBarText();
802
redraw();
803
}
804
805
bool CtrlMemView::ParseSearchString(const std::string &query, bool asHex, std::vector<uint8_t> &data) {
806
data.clear();
807
if (!asHex) {
808
for (size_t i = 0; i < query.length(); i++) {
809
data.push_back(query[i]);
810
}
811
return true;
812
}
813
814
for (size_t index = 0; index < query.size(); ) {
815
if (isspace(query[index])) {
816
index++;
817
continue;
818
}
819
820
u8 value = 0;
821
for (int i = 0; i < 2 && index < query.size(); i++) {
822
char c = tolower(query[index++]);
823
if (c >= 'a' && c <= 'f') {
824
value |= (c - 'a' + 10) << (1 - i) * 4;
825
} else if (c >= '0' && c <= '9') {
826
value |= (c - '0') << (1 - i) * 4;
827
} else {
828
return false;
829
}
830
}
831
832
data.push_back(value);
833
}
834
835
return true;
836
}
837
838
std::vector<u32> CtrlMemView::searchString(const std::string &searchQuery) {
839
std::vector<u32> searchResAddrs;
840
841
auto memLock = Memory::Lock();
842
if (!PSP_IsInited())
843
return searchResAddrs;
844
845
std::vector<u8> searchData;
846
if (!ParseSearchString(searchQuery, false, searchData))
847
return searchResAddrs;
848
849
if (searchData.empty())
850
return searchResAddrs;
851
852
std::vector<std::pair<u32, u32>> memoryAreas;
853
memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());
854
// Ignore the video memory mirrors.
855
memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);
856
memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());
857
858
for (const auto &area : memoryAreas) {
859
const u32 segmentStart = area.first;
860
const u32 segmentEnd = area.second - (u32)searchData.size();
861
862
for (u32 pos = segmentStart; pos < segmentEnd; pos++) {
863
if ((pos % 256) == 0 && KeyDownAsync(VK_ESCAPE)) {
864
return searchResAddrs;
865
}
866
867
const u8 *ptr = Memory::GetPointerUnchecked(pos);
868
if (memcmp(ptr, searchData.data(), searchData.size()) == 0) {
869
searchResAddrs.push_back(pos);
870
}
871
}
872
}
873
874
return searchResAddrs;
875
};
876
877
void CtrlMemView::search(bool continueSearch) {
878
auto memLock = Memory::Lock();
879
if (!PSP_IsInited())
880
return;
881
882
u32 searchAddress = 0;
883
u32 segmentStart = 0;
884
u32 segmentEnd = 0;
885
if (continueSearch == false || searchQuery_.empty()) {
886
if (InputBox_GetString(GetModuleHandle(NULL), wnd, L"Search for", searchQuery_, searchQuery_) == false) {
887
SetFocus(wnd);
888
return;
889
}
890
SetFocus(wnd);
891
searchAddress = curAddress_ + 1;
892
} else {
893
searchAddress = matchAddress_ + 1;
894
}
895
896
std::vector<u8> searchData;
897
if (!ParseSearchString(searchQuery_, !asciiSelected_, searchData)) {
898
MessageBox(wnd, L"Invalid search text.", L"Error", MB_OK);
899
return;
900
}
901
902
std::vector<std::pair<u32, u32>> memoryAreas;
903
// Ignore the video memory mirrors.
904
memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);
905
memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());
906
memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());
907
908
searching_ = true;
909
redraw(); // so the cursor is disabled
910
911
for (size_t i = 0; i < memoryAreas.size(); i++) {
912
segmentStart = memoryAreas[i].first;
913
segmentEnd = memoryAreas[i].second;
914
915
// better safe than sorry, I guess
916
if (!Memory::IsValidAddress(segmentStart))
917
continue;
918
const u8 *dataPointer = Memory::GetPointerUnchecked(segmentStart);
919
920
if (searchAddress < segmentStart)
921
searchAddress = segmentStart;
922
if (searchAddress >= segmentEnd)
923
continue;
924
925
int index = searchAddress-segmentStart;
926
int endIndex = segmentEnd-segmentStart - (int)searchData.size();
927
928
while (index < endIndex) {
929
// cancel search
930
if ((index % 256) == 0 && KeyDownAsync(VK_ESCAPE)) {
931
searching_ = false;
932
return;
933
}
934
if (memcmp(&dataPointer[index], searchData.data(), searchData.size()) == 0) {
935
matchAddress_ = index + segmentStart;
936
searching_ = false;
937
gotoAddr(matchAddress_);
938
return;
939
}
940
index++;
941
}
942
}
943
944
MessageBox(wnd, L"Not found", L"Search", MB_OK);
945
searching_ = false;
946
redraw();
947
}
948
949
void CtrlMemView::drawOffsetScale(HDC hdc) {
950
int currentX = addressStartX_;
951
952
SetTextColor(hdc, 0x600000);
953
TextOutA(hdc, currentX, offsetPositionY_, "Offset", 6);
954
955
// the start offset, the size of the hex addresses and one space
956
currentX = addressStartX_ + ((8 + 1) * charWidth_);
957
958
char temp[64];
959
for (int i = 0; i < 16; i++) {
960
snprintf(temp, sizeof(temp), "%02X", i);
961
TextOutA(hdc, currentX, offsetPositionY_, temp, 2);
962
currentX += 3 * charWidth_; // hex and space
963
}
964
}
965
966
void CtrlMemView::toggleOffsetScale(CommonToggles toggle) {
967
if (toggle == On)
968
displayOffsetScale_ = true;
969
else if (toggle == Off)
970
displayOffsetScale_ = false;
971
972
updateStatusBarText();
973
redraw();
974
}
975
976
void CtrlMemView::setHighlightType(MemBlockFlags flags) {
977
if (highlightFlags_ != flags) {
978
highlightFlags_ = flags;
979
updateStatusBarText();
980
redraw();
981
}
982
}
983
984
uint32_t CtrlMemView::pickTagColor(const std::string &tag) {
985
int colors[6] = { 0xe0FFFF, 0xFFE0E0, 0xE8E8FF, 0xFFE0FF, 0xE0FFE0, 0xFFFFE0 };
986
int which = XXH3_64bits(tag.c_str(), tag.length()) % ARRAY_SIZE(colors);
987
return colors[which];
988
}
989
990