Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/Debugger/CtrlMemView.cpp
3185 views
1
#include <cctype>
2
#include <cmath>
3
#include <sstream>
4
5
#include "ext/xxhash.h"
6
#include "Common/StringUtils.h"
7
#include "Core/Config.h"
8
#include "Core/System.h"
9
#include "Core/MemMap.h"
10
#include "Core/Reporting.h"
11
#include "Core/RetroAchievements.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
if (Achievements::HardcoreModeActive())
181
return;
182
183
auto memLock = Memory::Lock();
184
185
// draw to a bitmap for double buffering
186
PAINTSTRUCT ps;
187
HDC actualHdc = BeginPaint(wnd, &ps);
188
HDC hdc = CreateCompatibleDC(actualHdc);
189
HBITMAP hBM = CreateCompatibleBitmap(actualHdc, rect_.right - rect_.left, rect_.bottom - rect_.top);
190
SelectObject(hdc, hBM);
191
192
SetBkMode(hdc,OPAQUE);
193
HPEN standardPen = CreatePen(0,0,0xFFFFFF);
194
HBRUSH standardBrush = CreateSolidBrush(0xFFFFFF);
195
COLORREF standardBG = GetBkColor(hdc);
196
197
HPEN oldPen = (HPEN) SelectObject(hdc,standardPen);
198
HBRUSH oldBrush = (HBRUSH) SelectObject(hdc,standardBrush);
199
HFONT oldFont = (HFONT) SelectObject(hdc,(HGDIOBJ)font);
200
201
// white background
202
SelectObject(hdc,standardPen);
203
SelectObject(hdc,standardBrush);
204
Rectangle(hdc, 0, 0, rect_.right, rect_.bottom);
205
206
if (displayOffsetScale_)
207
drawOffsetScale(hdc);
208
209
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, windowStart_, (visibleRows_ + 1) * rowSize_);
210
211
COLORREF lastTextCol = 0x000000;
212
COLORREF lastBGCol = standardBG;
213
auto setTextColors = [&](COLORREF fg, COLORREF bg) {
214
if (lastTextCol != fg) {
215
SetTextColor(hdc, fg);
216
lastTextCol = fg;
217
}
218
if (lastBGCol != bg) {
219
SetBkColor(hdc, bg);
220
lastBGCol = bg;
221
}
222
};
223
224
_assert_msg_(((windowStart_ | rowSize_) & 3) == 0, "readMemory() can't handle unaligned reads");
225
226
// draw one extra row that may be partially visible
227
for (int i = 0; i < visibleRows_ + 1; i++) {
228
int rowY = rowHeight_ * i;
229
// Skip the first X rows to make space for the offsets.
230
if (displayOffsetScale_)
231
rowY += rowHeight_ * offsetSpace;
232
233
char temp[32];
234
uint32_t address = windowStart_ + i * rowSize_;
235
snprintf(temp, sizeof(temp), "%08X", address);
236
237
setTextColors(0x600000, standardBG);
238
TextOutA(hdc, addressStartX_, rowY, temp, (int)strlen(temp));
239
240
union {
241
uint32_t words[4];
242
uint8_t bytes[16];
243
} memory;
244
int valid = debugger_ != nullptr && debugger_->isAlive() ? Memory::ValidSize(address, 16) / 4 : 0;
245
for (int i = 0; i < valid; ++i) {
246
memory.words[i] = debugger_->readMemory(address + i * 4);
247
}
248
249
for (int j = 0; j < rowSize_; j++) {
250
const uint32_t byteAddress = (address + j) & ~0xC0000000;
251
std::string tag;
252
bool tagContinues = false;
253
for (auto info : memRangeInfo) {
254
if (info.start <= byteAddress && info.start + info.size > byteAddress) {
255
tag = info.tag;
256
tagContinues = byteAddress + 1 < info.start + info.size;
257
}
258
}
259
260
int hexX = hexStartX_ + j * 3 * charWidth_;
261
int hexLen = 2;
262
int asciiX = asciiStartX_ + j * (charWidth_ + 2);
263
264
char c;
265
if (valid) {
266
snprintf(temp, sizeof(temp), "%02X ", memory.bytes[j]);
267
c = (char)memory.bytes[j];
268
if (memory.bytes[j] < 32 || memory.bytes[j] >= 128)
269
c = '.';
270
} else {
271
truncate_cpy(temp, "??");
272
c = '.';
273
}
274
275
COLORREF hexBGCol = standardBG;
276
COLORREF hexTextCol = 0x000000;
277
COLORREF continueBGCol = standardBG;
278
COLORREF asciiBGCol = standardBG;
279
COLORREF asciiTextCol = 0x000000;
280
int underline = -1;
281
282
if (address + j >= selectRangeStart_ && address + j < selectRangeEnd_ && !searching_) {
283
if (asciiSelected_) {
284
hexBGCol = 0xC0C0C0;
285
hexTextCol = 0x000000;
286
asciiBGCol = hasFocus_ ? 0xFF9933 : 0xC0C0C0;
287
asciiTextCol = hasFocus_ ? 0xFFFFFF : 0x000000;
288
} else {
289
hexBGCol = hasFocus_ ? 0xFF9933 : 0xC0C0C0;
290
hexTextCol = hasFocus_ ? 0xFFFFFF : 0x000000;
291
asciiBGCol = 0xC0C0C0;
292
asciiTextCol = 0x000000;
293
if (address + j == curAddress_)
294
underline = selectedNibble_;
295
}
296
if (!tag.empty() && tagContinues) {
297
continueBGCol = pickTagColor(tag);
298
}
299
} else if (!tag.empty()) {
300
hexBGCol = pickTagColor(tag);
301
continueBGCol = hexBGCol;
302
asciiBGCol = pickTagColor(tag);
303
hexLen = tagContinues ? 3 : 2;
304
}
305
306
setTextColors(hexTextCol, hexBGCol);
307
if (underline >= 0) {
308
SelectObject(hdc, underline == 0 ? (HGDIOBJ)underlineFont : (HGDIOBJ)font);
309
TextOutA(hdc, hexX, rowY, &temp[0], 1);
310
SelectObject(hdc, underline == 0 ? (HGDIOBJ)font : (HGDIOBJ)underlineFont);
311
TextOutA(hdc, hexX + charWidth_, rowY, &temp[1], 1);
312
SelectObject(hdc, (HGDIOBJ)font);
313
314
// If the tag keeps going, draw the BG too.
315
if (continueBGCol != standardBG) {
316
setTextColors(0x000000, continueBGCol);
317
TextOutA(hdc, hexX + charWidth_ * 2, rowY, &temp[2], 1);
318
}
319
} else {
320
if (continueBGCol != hexBGCol) {
321
TextOutA(hdc, hexX, rowY, temp, 2);
322
setTextColors(0x000000, continueBGCol);
323
TextOutA(hdc, hexX + charWidth_ * 2, rowY, &temp[2], 1);
324
} else {
325
TextOutA(hdc, hexX, rowY, temp, hexLen);
326
}
327
}
328
329
setTextColors(asciiTextCol, asciiBGCol);
330
TextOutA(hdc, asciiX, rowY, &c, 1);
331
}
332
}
333
334
setTextColors(0x000000, standardBG);
335
SelectObject(hdc,oldFont);
336
SelectObject(hdc,oldPen);
337
SelectObject(hdc,oldBrush);
338
339
// copy bitmap to the actual hdc
340
BitBlt(actualHdc, 0, 0, rect_.right, rect_.bottom, hdc, 0, 0, SRCCOPY);
341
DeleteObject(hBM);
342
DeleteDC(hdc);
343
344
DeleteObject(standardPen);
345
DeleteObject(standardBrush);
346
347
EndPaint(wnd, &ps);
348
}
349
350
void CtrlMemView::onVScroll(WPARAM wParam, LPARAM lParam) {
351
switch (wParam & 0xFFFF) {
352
case SB_LINEDOWN:
353
ScrollWindow(1, GotoModeFromModifiers(false));
354
break;
355
case SB_LINEUP:
356
ScrollWindow(-1, GotoModeFromModifiers(false));
357
break;
358
case SB_PAGEDOWN:
359
ScrollWindow(visibleRows_, GotoModeFromModifiers(false));
360
break;
361
case SB_PAGEUP:
362
ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));
363
break;
364
default:
365
return;
366
}
367
}
368
369
void CtrlMemView::onKeyDown(WPARAM wParam, LPARAM lParam) {
370
if (KeyDownAsync(VK_CONTROL)) {
371
switch (tolower(wParam & 0xFFFF)) {
372
case 'g':
373
{
374
u32 addr;
375
if (executeExpressionWindow(wnd, debugger_, addr) == false)
376
return;
377
gotoAddr(addr);
378
return;
379
}
380
break;
381
case 'f':
382
case 's':
383
search(false);
384
return;
385
case 'c':
386
search(true);
387
return;
388
}
389
}
390
391
switch (wParam & 0xFFFF) {
392
case VK_DOWN:
393
ScrollCursor(rowSize_, GotoModeFromModifiers(false));
394
break;
395
case VK_UP:
396
ScrollCursor(-rowSize_, GotoModeFromModifiers(false));
397
break;
398
case VK_LEFT:
399
ScrollCursor(-1, GotoModeFromModifiers(false));
400
break;
401
case VK_RIGHT:
402
ScrollCursor(1, GotoModeFromModifiers(false));
403
break;
404
case VK_NEXT:
405
ScrollWindow(visibleRows_, GotoModeFromModifiers(false));
406
break;
407
case VK_PRIOR:
408
ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));
409
break;
410
case VK_TAB:
411
SendMessage(GetParent(wnd),WM_DEB_TABPRESSED,0,0);
412
break;
413
default:
414
return;
415
}
416
}
417
418
void CtrlMemView::onChar(WPARAM wParam, LPARAM lParam) {
419
auto memLock = Memory::Lock();
420
if (!PSP_IsInited())
421
return;
422
423
if (KeyDownAsync(VK_CONTROL) || wParam == VK_TAB)
424
return;
425
426
if (!Memory::IsValidAddress(curAddress_)) {
427
ScrollCursor(1, GotoMode::RESET);
428
return;
429
}
430
431
bool active = Core_IsActive();
432
if (active)
433
Core_Break(BreakReason::MemoryAccess, curAddress_);
434
435
if (asciiSelected_) {
436
Memory::WriteUnchecked_U8((u8)wParam, curAddress_);
437
ScrollCursor(1, GotoMode::RESET);
438
} else {
439
wParam = tolower(wParam);
440
int inputValue = -1;
441
442
if (wParam >= '0' && wParam <= '9') inputValue = wParam - '0';
443
if (wParam >= 'a' && wParam <= 'f') inputValue = wParam -'a' + 10;
444
445
if (inputValue >= 0) {
446
int shiftAmount = (1 - selectedNibble_) * 4;
447
448
u8 oldValue = Memory::ReadUnchecked_U8(curAddress_);
449
oldValue &= ~(0xF << shiftAmount);
450
u8 newValue = oldValue | (inputValue << shiftAmount);
451
Memory::WriteUnchecked_U8(newValue, curAddress_);
452
ScrollCursor(1, GotoMode::RESET);
453
}
454
}
455
456
Reporting::NotifyDebugger();
457
if (active)
458
Core_Resume();
459
}
460
461
void CtrlMemView::redraw() {
462
GetClientRect(wnd, &rect_);
463
visibleRows_ = rect_.bottom / rowHeight_;
464
465
if (displayOffsetScale_) {
466
// 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
467
visibleRows_ -= offsetSpace;
468
}
469
470
if (!redrawScheduled_) {
471
SetTimer(wnd, IDT_REDRAW_DELAYED, REDRAW_DELAY, nullptr);
472
redrawScheduled_ = true;
473
}
474
}
475
476
CtrlMemView::GotoMode CtrlMemView::GotoModeFromModifiers(bool isRightClick) {
477
GotoMode mode = GotoMode::RESET;
478
if (isRightClick) {
479
mode = GotoMode::RESET_IF_OUTSIDE;
480
} else if (KeyDownAsync(VK_SHIFT)) {
481
if (KeyDownAsync(VK_CONTROL))
482
mode = GotoMode::EXTEND;
483
else
484
mode = GotoMode::FROM_CUR;
485
}
486
return mode;
487
}
488
489
void CtrlMemView::onMouseDown(WPARAM wParam, LPARAM lParam, int button) {
490
if (Achievements::HardcoreModeActive())
491
return;
492
493
int x = LOWORD(lParam);
494
int y = HIWORD(lParam);
495
496
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
497
}
498
499
void CtrlMemView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) {
500
if (Achievements::HardcoreModeActive())
501
return;
502
503
if (button == 2) {
504
int32_t selectedSize = selectRangeEnd_ - selectRangeStart_;
505
bool enable16 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 1) == 0);
506
bool enable32 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 3) == 0);
507
508
HMENU menu = GetContextMenu(ContextMenuID::MEMVIEW);
509
EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_16, enable16 ? MF_ENABLED : MF_GRAYED);
510
EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_32, enable32 ? MF_ENABLED : MF_GRAYED);
511
EnableMenuItem(menu, ID_MEMVIEW_COPYFLOAT_32, enable32 ? MF_ENABLED : MF_GRAYED);
512
513
switch (TriggerContextMenu(ContextMenuID::MEMVIEW, wnd, ContextPoint::FromEvent(lParam))) {
514
case ID_MEMVIEW_DUMP:
515
{
516
DumpMemoryWindow dump(wnd, debugger_);
517
dump.exec();
518
break;
519
}
520
521
case ID_MEMVIEW_COPYVALUE_8:
522
{
523
auto memLock = Memory::Lock();
524
size_t tempSize = 3 * selectedSize + 1;
525
char *temp = new char[tempSize];
526
memset(temp, 0, tempSize);
527
528
// it's admittedly not really useful like this
529
if (asciiSelected_) {
530
for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {
531
uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : '.';
532
if (c < 32 || c >= 128)
533
c = '.';
534
temp[p - selectRangeStart_] = c;
535
}
536
} else {
537
char *pos = temp;
538
for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {
539
uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : 0xFF;
540
pos += snprintf(pos, tempSize - (pos - temp + 1), "%02X ", c);
541
}
542
// Clear the last space.
543
if (pos > temp)
544
*(pos - 1) = '\0';
545
}
546
W32Util::CopyTextToClipboard(wnd, temp);
547
delete[] temp;
548
}
549
break;
550
551
case ID_MEMVIEW_COPYVALUE_16:
552
{
553
auto memLock = Memory::Lock();
554
size_t tempSize = 5 * ((selectedSize + 1) / 2) + 1;
555
char *temp = new char[tempSize];
556
memset(temp, 0, tempSize);
557
558
char *pos = temp;
559
for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 2) {
560
uint16_t c = Memory::IsValidRange(p, 2) ? Memory::ReadUnchecked_U16(p) : 0xFFFF;
561
pos += snprintf(pos, tempSize - (pos - temp + 1), "%04X ", c);
562
}
563
// Clear the last space.
564
if (pos > temp)
565
*(pos - 1) = '\0';
566
567
W32Util::CopyTextToClipboard(wnd, temp);
568
delete[] temp;
569
}
570
break;
571
572
case ID_MEMVIEW_COPYVALUE_32:
573
{
574
auto memLock = Memory::Lock();
575
size_t tempSize = 9 * ((selectedSize + 3) / 4) + 1;
576
char *temp = new char[tempSize];
577
memset(temp, 0, tempSize);
578
579
char *pos = temp;
580
for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 4) {
581
uint32_t c = Memory::IsValidRange(p, 4) ? Memory::ReadUnchecked_U32(p) : 0xFFFFFFFF;
582
pos += snprintf(pos, tempSize - (pos - temp + 1), "%08X ", c);
583
}
584
// Clear the last space.
585
if (pos > temp)
586
*(pos - 1) = '\0';
587
588
W32Util::CopyTextToClipboard(wnd, temp);
589
delete[] temp;
590
}
591
break;
592
593
case ID_MEMVIEW_COPYFLOAT_32:
594
{
595
auto memLock = Memory::Lock();
596
std::ostringstream stream;
597
stream << (Memory::IsValidAddress(curAddress_) ? Memory::Read_Float(curAddress_) : NAN);
598
auto temp_string = stream.str();
599
W32Util::CopyTextToClipboard(wnd, temp_string.c_str());
600
}
601
break;
602
603
case ID_MEMVIEW_EXTENTBEGIN:
604
{
605
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
606
uint32_t addr = curAddress_;
607
for (MemBlockInfo info : memRangeInfo) {
608
addr = info.start;
609
}
610
gotoAddr(addr);
611
break;
612
}
613
614
case ID_MEMVIEW_EXTENTEND:
615
{
616
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
617
uint32_t addr = curAddress_;
618
for (MemBlockInfo info : memRangeInfo) {
619
addr = info.start + info.size - 1;
620
}
621
gotoAddr(addr);
622
break;
623
}
624
625
case ID_MEMVIEW_COPYADDRESS:
626
{
627
char temp[24];
628
snprintf(temp, sizeof(temp), "0x%08X", curAddress_);
629
W32Util::CopyTextToClipboard(wnd, temp);
630
}
631
break;
632
633
case ID_MEMVIEW_GOTOINDISASM:
634
if (disasmWindow) {
635
disasmWindow->Goto(curAddress_);
636
disasmWindow->Show(true);
637
}
638
break;
639
}
640
return;
641
}
642
643
int x = LOWORD(lParam);
644
int y = HIWORD(lParam);
645
ReleaseCapture();
646
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
647
}
648
649
void CtrlMemView::onMouseMove(WPARAM wParam, LPARAM lParam, int button) {
650
if (Achievements::HardcoreModeActive())
651
return;
652
653
int x = LOWORD(lParam);
654
int y = HIWORD(lParam);
655
656
if (button & 1) {
657
GotoPoint(x, y, GotoModeFromModifiers(button == 2));
658
}
659
}
660
661
void CtrlMemView::updateStatusBarText() {
662
std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);
663
664
char text[512];
665
snprintf(text, sizeof(text), "%08X", curAddress_);
666
// There should only be one.
667
for (MemBlockInfo info : memRangeInfo) {
668
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);
669
}
670
671
SendMessage(GetParent(wnd), WM_DEB_SETSTATUSBARTEXT, 0, (LPARAM)text);
672
}
673
674
void CtrlMemView::UpdateSelectRange(uint32_t target, GotoMode mode) {
675
if (mode == GotoMode::FROM_CUR && lastSelectReset_ == 0) {
676
lastSelectReset_ = curAddress_;
677
}
678
679
switch (mode) {
680
case GotoMode::RESET:
681
selectRangeStart_ = target;
682
selectRangeEnd_ = target + 1;
683
lastSelectReset_ = target;
684
break;
685
686
case GotoMode::RESET_IF_OUTSIDE:
687
if (target < selectRangeStart_ || target >= selectRangeEnd_) {
688
selectRangeStart_ = target;
689
selectRangeEnd_ = target + 1;
690
lastSelectReset_ = target;
691
}
692
break;
693
694
case GotoMode::FROM_CUR:
695
selectRangeStart_ = lastSelectReset_ > target ? target : lastSelectReset_;
696
selectRangeEnd_ = selectRangeStart_ == lastSelectReset_ ? target + 1 : lastSelectReset_ + 1;
697
break;
698
699
case GotoMode::EXTEND:
700
if (target < selectRangeStart_)
701
selectRangeStart_ = target;
702
if (target > selectRangeEnd_)
703
selectRangeEnd_ = target;
704
break;
705
}
706
curAddress_ = target;
707
}
708
709
void CtrlMemView::GotoPoint(int x, int y, GotoMode mode) {
710
int line = y / rowHeight_;
711
int lineAddress = windowStart_ + line * rowSize_;
712
713
if (displayOffsetScale_) {
714
// ignore clicks on the offset space
715
if (line < offsetSpace) {
716
updateStatusBarText();
717
redraw();
718
return;
719
}
720
// 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
721
lineAddress -= rowSize_ * offsetSpace;
722
}
723
724
uint32_t target = curAddress_;
725
uint32_t targetNibble = selectedNibble_;
726
bool targetAscii = asciiSelected_;
727
if (x >= asciiStartX_) {
728
int col = (x - asciiStartX_) / (charWidth_ + 2);
729
if (col >= rowSize_)
730
return;
731
732
targetAscii = true;
733
target = lineAddress + col;
734
targetNibble = 0;
735
} else if (x >= hexStartX_) {
736
int col = (x - hexStartX_) / charWidth_;
737
if ((col/3) >= rowSize_)
738
return;
739
740
switch (col % 3) {
741
case 0: targetNibble = 0; break;
742
case 1: targetNibble = 1; break;
743
case 2: return; // don't change position when clicking on the space
744
}
745
746
targetAscii = false;
747
target = lineAddress + col / 3;
748
}
749
750
if (target != curAddress_ || targetNibble != selectedNibble_ || targetAscii != asciiSelected_) {
751
selectedNibble_ = targetNibble;
752
asciiSelected_ = targetAscii;
753
UpdateSelectRange(target, mode);
754
755
updateStatusBarText();
756
redraw();
757
}
758
}
759
760
void CtrlMemView::gotoAddr(unsigned int addr) {
761
int lines = rect_.bottom / rowHeight_;
762
u32 windowEnd = windowStart_ + lines * rowSize_;
763
764
curAddress_ = addr;
765
lastSelectReset_ = curAddress_;
766
selectRangeStart_ = curAddress_;
767
selectRangeEnd_ = curAddress_ + 1;
768
selectedNibble_ = 0;
769
770
if (curAddress_ < windowStart_ || curAddress_ >= windowEnd) {
771
windowStart_ = curAddress_ & ~15;
772
}
773
774
updateStatusBarText();
775
redraw();
776
}
777
778
void CtrlMemView::ScrollWindow(int lines, GotoMode mode) {
779
windowStart_ += lines * rowSize_;
780
781
UpdateSelectRange(curAddress_ + lines * rowSize_, mode);
782
783
updateStatusBarText();
784
redraw();
785
}
786
787
void CtrlMemView::ScrollCursor(int bytes, GotoMode mode) {
788
if (!asciiSelected_ && bytes == 1) {
789
if (selectedNibble_ == 0) {
790
selectedNibble_ = 1;
791
bytes = 0;
792
} else {
793
selectedNibble_ = 0;
794
}
795
} else if (!asciiSelected_ && bytes == -1) {
796
if (selectedNibble_ == 0) {
797
selectedNibble_ = 1;
798
} else {
799
selectedNibble_ = 0;
800
bytes = 0;
801
}
802
}
803
804
UpdateSelectRange(curAddress_ + bytes, mode);
805
806
u32 windowEnd = windowStart_ + visibleRows_ * rowSize_;
807
if (curAddress_ < windowStart_) {
808
windowStart_ = curAddress_ & ~15;
809
} else if (curAddress_ >= windowEnd) {
810
windowStart_ = (curAddress_ - (visibleRows_ - 1) * rowSize_) & ~15;
811
}
812
813
updateStatusBarText();
814
redraw();
815
}
816
817
bool CtrlMemView::ParseSearchString(const std::string &query, bool asHex, std::vector<uint8_t> &data) {
818
data.clear();
819
if (!asHex) {
820
for (size_t i = 0; i < query.length(); i++) {
821
data.push_back(query[i]);
822
}
823
return true;
824
}
825
826
for (size_t index = 0; index < query.size(); ) {
827
if (isspace(query[index])) {
828
index++;
829
continue;
830
}
831
832
u8 value = 0;
833
for (int i = 0; i < 2 && index < query.size(); i++) {
834
char c = tolower(query[index++]);
835
if (c >= 'a' && c <= 'f') {
836
value |= (c - 'a' + 10) << (1 - i) * 4;
837
} else if (c >= '0' && c <= '9') {
838
value |= (c - '0') << (1 - i) * 4;
839
} else {
840
return false;
841
}
842
}
843
844
data.push_back(value);
845
}
846
847
return true;
848
}
849
850
std::vector<u32> CtrlMemView::searchString(const std::string &searchQuery) {
851
std::vector<u32> searchResAddrs;
852
853
auto memLock = Memory::Lock();
854
if (!PSP_IsInited())
855
return searchResAddrs;
856
857
std::vector<u8> searchData;
858
if (!ParseSearchString(searchQuery, false, searchData))
859
return searchResAddrs;
860
861
if (searchData.empty())
862
return searchResAddrs;
863
864
std::vector<std::pair<u32, u32>> memoryAreas;
865
memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());
866
// Ignore the video memory mirrors.
867
memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);
868
memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());
869
870
for (const auto &area : memoryAreas) {
871
const u32 segmentStart = area.first;
872
const u32 segmentEnd = area.second - (u32)searchData.size();
873
874
for (u32 pos = segmentStart; pos < segmentEnd; pos++) {
875
if ((pos % 256) == 0 && KeyDownAsync(VK_ESCAPE)) {
876
return searchResAddrs;
877
}
878
879
const u8 *ptr = Memory::GetPointerUnchecked(pos);
880
if (memcmp(ptr, searchData.data(), searchData.size()) == 0) {
881
searchResAddrs.push_back(pos);
882
}
883
}
884
}
885
886
return searchResAddrs;
887
};
888
889
void CtrlMemView::search(bool continueSearch) {
890
auto memLock = Memory::Lock();
891
if (!PSP_IsInited())
892
return;
893
894
u32 searchAddress = 0;
895
u32 segmentStart = 0;
896
u32 segmentEnd = 0;
897
if (continueSearch == false || searchQuery_.empty()) {
898
if (InputBox_GetString(GetModuleHandle(NULL), wnd, L"Search for", searchQuery_, searchQuery_) == false) {
899
SetFocus(wnd);
900
return;
901
}
902
SetFocus(wnd);
903
searchAddress = curAddress_ + 1;
904
} else {
905
searchAddress = matchAddress_ + 1;
906
}
907
908
std::vector<u8> searchData;
909
if (!ParseSearchString(searchQuery_, !asciiSelected_, searchData)) {
910
MessageBox(wnd, L"Invalid search text.", L"Error", MB_OK);
911
return;
912
}
913
914
std::vector<std::pair<u32, u32>> memoryAreas;
915
// Ignore the video memory mirrors.
916
memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);
917
memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());
918
memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());
919
920
searching_ = true;
921
redraw(); // so the cursor is disabled
922
923
for (size_t i = 0; i < memoryAreas.size(); i++) {
924
segmentStart = memoryAreas[i].first;
925
segmentEnd = memoryAreas[i].second;
926
927
// better safe than sorry, I guess
928
if (!Memory::IsValidAddress(segmentStart))
929
continue;
930
const u8 *dataPointer = Memory::GetPointerUnchecked(segmentStart);
931
932
if (searchAddress < segmentStart)
933
searchAddress = segmentStart;
934
if (searchAddress >= segmentEnd)
935
continue;
936
937
int index = searchAddress-segmentStart;
938
int endIndex = segmentEnd-segmentStart - (int)searchData.size();
939
940
while (index < endIndex) {
941
// cancel search
942
if ((index % 256) == 0 && KeyDownAsync(VK_ESCAPE)) {
943
searching_ = false;
944
return;
945
}
946
if (memcmp(&dataPointer[index], searchData.data(), searchData.size()) == 0) {
947
matchAddress_ = index + segmentStart;
948
searching_ = false;
949
gotoAddr(matchAddress_);
950
return;
951
}
952
index++;
953
}
954
}
955
956
MessageBox(wnd, L"Not found", L"Search", MB_OK);
957
searching_ = false;
958
redraw();
959
}
960
961
void CtrlMemView::drawOffsetScale(HDC hdc) {
962
int currentX = addressStartX_;
963
964
SetTextColor(hdc, 0x600000);
965
TextOutA(hdc, currentX, offsetPositionY_, "Offset", 6);
966
967
// the start offset, the size of the hex addresses and one space
968
currentX = addressStartX_ + ((8 + 1) * charWidth_);
969
970
char temp[64];
971
for (int i = 0; i < 16; i++) {
972
snprintf(temp, sizeof(temp), "%02X", i);
973
TextOutA(hdc, currentX, offsetPositionY_, temp, 2);
974
currentX += 3 * charWidth_; // hex and space
975
}
976
}
977
978
void CtrlMemView::toggleOffsetScale(CommonToggles toggle) {
979
if (toggle == On)
980
displayOffsetScale_ = true;
981
else if (toggle == Off)
982
displayOffsetScale_ = false;
983
984
updateStatusBarText();
985
redraw();
986
}
987
988
void CtrlMemView::setHighlightType(MemBlockFlags flags) {
989
if (highlightFlags_ != flags) {
990
highlightFlags_ = flags;
991
updateStatusBarText();
992
redraw();
993
}
994
}
995
996
uint32_t CtrlMemView::pickTagColor(const std::string &tag) {
997
int colors[6] = { 0xe0FFFF, 0xFFE0E0, 0xE8E8FF, 0xFFE0FF, 0xE0FFE0, 0xFFFFE0 };
998
int which = XXH3_64bits(tag.c_str(), tag.length()) % ARRAY_SIZE(colors);
999
return colors[which];
1000
}
1001
1002