CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Windows/Debugger/CtrlMemView.cpp
Views: 1401
#include <cctype>1#include <tchar.h>2#include <cmath>3#include <iomanip>4#include <sstream>56#include "ext/xxhash.h"7#include "Common/StringUtils.h"8#include "Core/Config.h"9#include "Core/MemMap.h"10#include "Core/Reporting.h"11#include "Windows/W32Util/ContextMenu.h"12#include "Windows/W32Util/Misc.h"13#include "Windows/InputBox.h"14#include "Windows/main.h"15#include "Windows/resource.h"16#include "Common/System/Display.h"1718#include "Debugger_Disasm.h"19#include "DebuggerShared.h"20#include "CtrlMemView.h"21#include "DumpMemoryWindow.h"2223wchar_t CtrlMemView::szClassName[] = L"CtrlMemView";2425static constexpr UINT_PTR IDT_REDRAW_DELAYED = 0xC0DE0001;26static constexpr UINT REDRAW_DELAY = 1000 / 60;27// We also redraw regularly, since data changes during runtime.28static constexpr UINT_PTR IDT_REDRAW_AUTO = 0xC0DE0002;29static constexpr UINT REDRAW_INTERVAL = 1000;3031CtrlMemView::CtrlMemView(HWND _wnd) {32wnd=_wnd;33SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)this);34SetWindowLong(wnd, GWL_STYLE, GetWindowLong(wnd,GWL_STYLE) | WS_VSCROLL);35SetScrollRange(wnd, SB_VERT, -1,1,TRUE);3637const float fontScale = 1.0f / g_display.dpi_scale_real_y;38charWidth_ = g_Config.iFontWidth * fontScale;39rowHeight_ = g_Config.iFontHeight * fontScale;40offsetPositionY_ = offsetLine * rowHeight_;4142font = CreateFont(rowHeight_, charWidth_, 0, 0,43FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,44L"Lucida Console");45underlineFont = CreateFont(rowHeight_, charWidth_, 0, 0,46FW_DONTCARE, FALSE, TRUE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,47L"Lucida Console");4849windowStart_ = curAddress_;50selectRangeStart_ = curAddress_;51selectRangeEnd_ = curAddress_ + 1;52lastSelectReset_ = curAddress_;5354addressStartX_ = charWidth_;55hexStartX_ = addressStartX_ + 9 * charWidth_;56asciiStartX_ = hexStartX_ + (rowSize_ * 3 + 1) * charWidth_;5758// set redraw timer59SetTimer(wnd, IDT_REDRAW_AUTO, REDRAW_INTERVAL, nullptr);60}6162CtrlMemView::~CtrlMemView() {63DeleteObject(font);64DeleteObject(underlineFont);65}6667void CtrlMemView::init() {68WNDCLASSEX wc;6970wc.cbSize = sizeof(wc);71wc.lpszClassName = szClassName;72wc.hInstance = GetModuleHandle(0);73wc.lpfnWndProc = CtrlMemView::wndProc;74wc.hCursor = LoadCursor (NULL, IDC_ARROW);75wc.hIcon = 0;76wc.lpszMenuName = 0;77wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);78wc.style = 0;79wc.cbClsExtra = 0;80wc.cbWndExtra = sizeof( CtrlMemView * );81wc.hIconSm = 0;828384RegisterClassEx(&wc);85}8687void CtrlMemView::deinit() {88//UnregisterClass(szClassName, hInst)89}909192LRESULT CALLBACK CtrlMemView::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {93CtrlMemView *ccp = CtrlMemView::getFrom(hwnd);94static bool lmbDown=false,rmbDown=false;9596switch(msg) {97case WM_NCCREATE:98// Allocate a new CustCtrl structure for this window.99ccp = new CtrlMemView(hwnd);100101// Continue with window creation.102return ccp != NULL;103104// Clean up when the window is destroyed.105case WM_NCDESTROY:106delete ccp;107break;108case WM_SETFONT:109break;110case WM_SIZE:111ccp->redraw();112break;113case WM_PAINT:114ccp->onPaint(wParam,lParam);115break;116case WM_VSCROLL:117ccp->onVScroll(wParam,lParam);118break;119case WM_MOUSEWHEEL:120if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) {121ccp->ScrollWindow(-3, GotoModeFromModifiers(false));122} else if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) {123ccp->ScrollWindow(3, GotoModeFromModifiers(false));124}125break;126case WM_ERASEBKGND:127return FALSE;128case WM_KEYDOWN:129ccp->onKeyDown(wParam,lParam);130return 0;131case WM_CHAR:132ccp->onChar(wParam,lParam);133return 0;134case WM_KEYUP:135return 0;136case WM_LBUTTONDOWN: SetFocus(hwnd); lmbDown=true; ccp->onMouseDown(wParam,lParam,1); break;137case WM_RBUTTONDOWN: SetFocus(hwnd); rmbDown=true; ccp->onMouseDown(wParam,lParam,2); break;138case WM_MOUSEMOVE: ccp->onMouseMove(wParam,lParam,(lmbDown?1:0) | (rmbDown?2:0)); break;139case WM_LBUTTONUP: lmbDown=false; ccp->onMouseUp(wParam,lParam,1); break;140case WM_RBUTTONUP: rmbDown=false; ccp->onMouseUp(wParam,lParam,2); break;141case WM_SETFOCUS:142SetFocus(hwnd);143ccp->hasFocus_ = true;144ccp->redraw();145break;146case WM_KILLFOCUS:147ccp->hasFocus_ = false;148ccp->redraw();149break;150case WM_GETDLGCODE: // we want to process the arrow keys and all characters ourselves151return DLGC_WANTARROWS|DLGC_WANTCHARS|DLGC_WANTTAB;152break;153case WM_TIMER:154// This is actually delayed too, using another timer. That way we won't update twice.155if (wParam == IDT_REDRAW_AUTO && IsWindowVisible(ccp->wnd))156ccp->redraw();157158if (wParam == IDT_REDRAW_DELAYED) {159InvalidateRect(hwnd, nullptr, FALSE);160UpdateWindow(hwnd);161ccp->redrawScheduled_ = false;162KillTimer(hwnd, wParam);163}164break;165default:166break;167}168169return DefWindowProc(hwnd, msg, wParam, lParam);170}171172173CtrlMemView *CtrlMemView::getFrom(HWND hwnd) {174return (CtrlMemView *)GetWindowLongPtr(hwnd, GWLP_USERDATA);175}176177178void CtrlMemView::onPaint(WPARAM wParam, LPARAM lParam) {179auto memLock = Memory::Lock();180181// draw to a bitmap for double buffering182PAINTSTRUCT ps;183HDC actualHdc = BeginPaint(wnd, &ps);184HDC hdc = CreateCompatibleDC(actualHdc);185HBITMAP hBM = CreateCompatibleBitmap(actualHdc, rect_.right - rect_.left, rect_.bottom - rect_.top);186SelectObject(hdc, hBM);187188SetBkMode(hdc,OPAQUE);189HPEN standardPen = CreatePen(0,0,0xFFFFFF);190HBRUSH standardBrush = CreateSolidBrush(0xFFFFFF);191COLORREF standardBG = GetBkColor(hdc);192193HPEN oldPen = (HPEN) SelectObject(hdc,standardPen);194HBRUSH oldBrush = (HBRUSH) SelectObject(hdc,standardBrush);195HFONT oldFont = (HFONT) SelectObject(hdc,(HGDIOBJ)font);196197// white background198SelectObject(hdc,standardPen);199SelectObject(hdc,standardBrush);200Rectangle(hdc, 0, 0, rect_.right, rect_.bottom);201202if (displayOffsetScale_)203drawOffsetScale(hdc);204205std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, windowStart_, (visibleRows_ + 1) * rowSize_);206207COLORREF lastTextCol = 0x000000;208COLORREF lastBGCol = standardBG;209auto setTextColors = [&](COLORREF fg, COLORREF bg) {210if (lastTextCol != fg) {211SetTextColor(hdc, fg);212lastTextCol = fg;213}214if (lastBGCol != bg) {215SetBkColor(hdc, bg);216lastBGCol = bg;217}218};219220_assert_msg_(((windowStart_ | rowSize_) & 3) == 0, "readMemory() can't handle unaligned reads");221222// draw one extra row that may be partially visible223for (int i = 0; i < visibleRows_ + 1; i++) {224int rowY = rowHeight_ * i;225// Skip the first X rows to make space for the offsets.226if (displayOffsetScale_)227rowY += rowHeight_ * offsetSpace;228229char temp[32];230uint32_t address = windowStart_ + i * rowSize_;231snprintf(temp, sizeof(temp), "%08X", address);232233setTextColors(0x600000, standardBG);234TextOutA(hdc, addressStartX_, rowY, temp, (int)strlen(temp));235236union {237uint32_t words[4];238uint8_t bytes[16];239} memory;240int valid = debugger_ != nullptr && debugger_->isAlive() ? Memory::ValidSize(address, 16) / 4 : 0;241for (int i = 0; i < valid; ++i) {242memory.words[i] = debugger_->readMemory(address + i * 4);243}244245for (int j = 0; j < rowSize_; j++) {246const uint32_t byteAddress = (address + j) & ~0xC0000000;247std::string tag;248bool tagContinues = false;249for (auto info : memRangeInfo) {250if (info.start <= byteAddress && info.start + info.size > byteAddress) {251tag = info.tag;252tagContinues = byteAddress + 1 < info.start + info.size;253}254}255256int hexX = hexStartX_ + j * 3 * charWidth_;257int hexLen = 2;258int asciiX = asciiStartX_ + j * (charWidth_ + 2);259260char c;261if (valid) {262snprintf(temp, sizeof(temp), "%02X ", memory.bytes[j]);263c = (char)memory.bytes[j];264if (memory.bytes[j] < 32 || memory.bytes[j] >= 128)265c = '.';266} else {267truncate_cpy(temp, "??");268c = '.';269}270271COLORREF hexBGCol = standardBG;272COLORREF hexTextCol = 0x000000;273COLORREF continueBGCol = standardBG;274COLORREF asciiBGCol = standardBG;275COLORREF asciiTextCol = 0x000000;276int underline = -1;277278if (address + j >= selectRangeStart_ && address + j < selectRangeEnd_ && !searching_) {279if (asciiSelected_) {280hexBGCol = 0xC0C0C0;281hexTextCol = 0x000000;282asciiBGCol = hasFocus_ ? 0xFF9933 : 0xC0C0C0;283asciiTextCol = hasFocus_ ? 0xFFFFFF : 0x000000;284} else {285hexBGCol = hasFocus_ ? 0xFF9933 : 0xC0C0C0;286hexTextCol = hasFocus_ ? 0xFFFFFF : 0x000000;287asciiBGCol = 0xC0C0C0;288asciiTextCol = 0x000000;289if (address + j == curAddress_)290underline = selectedNibble_;291}292if (!tag.empty() && tagContinues) {293continueBGCol = pickTagColor(tag);294}295} else if (!tag.empty()) {296hexBGCol = pickTagColor(tag);297continueBGCol = hexBGCol;298asciiBGCol = pickTagColor(tag);299hexLen = tagContinues ? 3 : 2;300}301302setTextColors(hexTextCol, hexBGCol);303if (underline >= 0) {304SelectObject(hdc, underline == 0 ? (HGDIOBJ)underlineFont : (HGDIOBJ)font);305TextOutA(hdc, hexX, rowY, &temp[0], 1);306SelectObject(hdc, underline == 0 ? (HGDIOBJ)font : (HGDIOBJ)underlineFont);307TextOutA(hdc, hexX + charWidth_, rowY, &temp[1], 1);308SelectObject(hdc, (HGDIOBJ)font);309310// If the tag keeps going, draw the BG too.311if (continueBGCol != standardBG) {312setTextColors(0x000000, continueBGCol);313TextOutA(hdc, hexX + charWidth_ * 2, rowY, &temp[2], 1);314}315} else {316if (continueBGCol != hexBGCol) {317TextOutA(hdc, hexX, rowY, temp, 2);318setTextColors(0x000000, continueBGCol);319TextOutA(hdc, hexX + charWidth_ * 2, rowY, &temp[2], 1);320} else {321TextOutA(hdc, hexX, rowY, temp, hexLen);322}323}324325setTextColors(asciiTextCol, asciiBGCol);326TextOutA(hdc, asciiX, rowY, &c, 1);327}328}329330setTextColors(0x000000, standardBG);331SelectObject(hdc,oldFont);332SelectObject(hdc,oldPen);333SelectObject(hdc,oldBrush);334335// copy bitmap to the actual hdc336BitBlt(actualHdc, 0, 0, rect_.right, rect_.bottom, hdc, 0, 0, SRCCOPY);337DeleteObject(hBM);338DeleteDC(hdc);339340DeleteObject(standardPen);341DeleteObject(standardBrush);342343EndPaint(wnd, &ps);344}345346void CtrlMemView::onVScroll(WPARAM wParam, LPARAM lParam) {347switch (wParam & 0xFFFF) {348case SB_LINEDOWN:349ScrollWindow(1, GotoModeFromModifiers(false));350break;351case SB_LINEUP:352ScrollWindow(-1, GotoModeFromModifiers(false));353break;354case SB_PAGEDOWN:355ScrollWindow(visibleRows_, GotoModeFromModifiers(false));356break;357case SB_PAGEUP:358ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));359break;360default:361return;362}363}364365void CtrlMemView::onKeyDown(WPARAM wParam, LPARAM lParam) {366if (KeyDownAsync(VK_CONTROL)) {367switch (tolower(wParam & 0xFFFF)) {368case 'g':369{370u32 addr;371if (executeExpressionWindow(wnd, debugger_, addr) == false)372return;373gotoAddr(addr);374return;375}376break;377case 'f':378case 's':379search(false);380return;381case 'c':382search(true);383return;384}385}386387switch (wParam & 0xFFFF) {388case VK_DOWN:389ScrollCursor(rowSize_, GotoModeFromModifiers(false));390break;391case VK_UP:392ScrollCursor(-rowSize_, GotoModeFromModifiers(false));393break;394case VK_LEFT:395ScrollCursor(-1, GotoModeFromModifiers(false));396break;397case VK_RIGHT:398ScrollCursor(1, GotoModeFromModifiers(false));399break;400case VK_NEXT:401ScrollWindow(visibleRows_, GotoModeFromModifiers(false));402break;403case VK_PRIOR:404ScrollWindow(-visibleRows_, GotoModeFromModifiers(false));405break;406case VK_TAB:407SendMessage(GetParent(wnd),WM_DEB_TABPRESSED,0,0);408break;409default:410return;411}412}413414void CtrlMemView::onChar(WPARAM wParam, LPARAM lParam) {415auto memLock = Memory::Lock();416if (!PSP_IsInited())417return;418419if (KeyDownAsync(VK_CONTROL) || wParam == VK_TAB)420return;421422if (!Memory::IsValidAddress(curAddress_)) {423ScrollCursor(1, GotoMode::RESET);424return;425}426427bool active = Core_IsActive();428if (active)429Core_EnableStepping(true, "memory.access", curAddress_);430431if (asciiSelected_) {432Memory::WriteUnchecked_U8((u8)wParam, curAddress_);433ScrollCursor(1, GotoMode::RESET);434} else {435wParam = tolower(wParam);436int inputValue = -1;437438if (wParam >= '0' && wParam <= '9') inputValue = wParam - '0';439if (wParam >= 'a' && wParam <= 'f') inputValue = wParam -'a' + 10;440441if (inputValue >= 0) {442int shiftAmount = (1 - selectedNibble_) * 4;443444u8 oldValue = Memory::ReadUnchecked_U8(curAddress_);445oldValue &= ~(0xF << shiftAmount);446u8 newValue = oldValue | (inputValue << shiftAmount);447Memory::WriteUnchecked_U8(newValue, curAddress_);448ScrollCursor(1, GotoMode::RESET);449}450}451452Reporting::NotifyDebugger();453if (active)454Core_EnableStepping(false);455}456457void CtrlMemView::redraw() {458GetClientRect(wnd, &rect_);459visibleRows_ = rect_.bottom / rowHeight_;460461if (displayOffsetScale_) {462// visibleRows_ is calculated based on the size of the control, but X rows have already been used for the offsets and are no longer usable463visibleRows_ -= offsetSpace;464}465466if (!redrawScheduled_) {467SetTimer(wnd, IDT_REDRAW_DELAYED, REDRAW_DELAY, nullptr);468redrawScheduled_ = true;469}470}471472CtrlMemView::GotoMode CtrlMemView::GotoModeFromModifiers(bool isRightClick) {473GotoMode mode = GotoMode::RESET;474if (isRightClick) {475mode = GotoMode::RESET_IF_OUTSIDE;476} else if (KeyDownAsync(VK_SHIFT)) {477if (KeyDownAsync(VK_CONTROL))478mode = GotoMode::EXTEND;479else480mode = GotoMode::FROM_CUR;481}482return mode;483}484485void CtrlMemView::onMouseDown(WPARAM wParam, LPARAM lParam, int button) {486int x = LOWORD(lParam);487int y = HIWORD(lParam);488489GotoPoint(x, y, GotoModeFromModifiers(button == 2));490}491492void CtrlMemView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) {493if (button == 2) {494int32_t selectedSize = selectRangeEnd_ - selectRangeStart_;495bool enable16 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 1) == 0);496bool enable32 = !asciiSelected_ && (selectedSize == 1 || (selectedSize & 3) == 0);497498HMENU menu = GetContextMenu(ContextMenuID::MEMVIEW);499EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_16, enable16 ? MF_ENABLED : MF_GRAYED);500EnableMenuItem(menu, ID_MEMVIEW_COPYVALUE_32, enable32 ? MF_ENABLED : MF_GRAYED);501EnableMenuItem(menu, ID_MEMVIEW_COPYFLOAT_32, enable32 ? MF_ENABLED : MF_GRAYED);502503switch (TriggerContextMenu(ContextMenuID::MEMVIEW, wnd, ContextPoint::FromEvent(lParam))) {504case ID_MEMVIEW_DUMP:505{506DumpMemoryWindow dump(wnd, debugger_);507dump.exec();508break;509}510511case ID_MEMVIEW_COPYVALUE_8:512{513auto memLock = Memory::Lock();514size_t tempSize = 3 * selectedSize + 1;515char *temp = new char[tempSize];516memset(temp, 0, tempSize);517518// it's admittedly not really useful like this519if (asciiSelected_) {520for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {521uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : '.';522if (c < 32 || c >= 128)523c = '.';524temp[p - selectRangeStart_] = c;525}526} else {527char *pos = temp;528for (uint32_t p = selectRangeStart_; p != selectRangeEnd_; ++p) {529uint8_t c = Memory::IsValidAddress(p) ? Memory::ReadUnchecked_U8(p) : 0xFF;530pos += snprintf(pos, tempSize - (pos - temp + 1), "%02X ", c);531}532// Clear the last space.533if (pos > temp)534*(pos - 1) = '\0';535}536W32Util::CopyTextToClipboard(wnd, temp);537delete[] temp;538}539break;540541case ID_MEMVIEW_COPYVALUE_16:542{543auto memLock = Memory::Lock();544size_t tempSize = 5 * ((selectedSize + 1) / 2) + 1;545char *temp = new char[tempSize];546memset(temp, 0, tempSize);547548char *pos = temp;549for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 2) {550uint16_t c = Memory::IsValidRange(p, 2) ? Memory::ReadUnchecked_U16(p) : 0xFFFF;551pos += snprintf(pos, tempSize - (pos - temp + 1), "%04X ", c);552}553// Clear the last space.554if (pos > temp)555*(pos - 1) = '\0';556557W32Util::CopyTextToClipboard(wnd, temp);558delete[] temp;559}560break;561562case ID_MEMVIEW_COPYVALUE_32:563{564auto memLock = Memory::Lock();565size_t tempSize = 9 * ((selectedSize + 3) / 4) + 1;566char *temp = new char[tempSize];567memset(temp, 0, tempSize);568569char *pos = temp;570for (uint32_t p = selectRangeStart_; p < selectRangeEnd_; p += 4) {571uint32_t c = Memory::IsValidRange(p, 4) ? Memory::ReadUnchecked_U32(p) : 0xFFFFFFFF;572pos += snprintf(pos, tempSize - (pos - temp + 1), "%08X ", c);573}574// Clear the last space.575if (pos > temp)576*(pos - 1) = '\0';577578W32Util::CopyTextToClipboard(wnd, temp);579delete[] temp;580}581break;582583case ID_MEMVIEW_COPYFLOAT_32:584{585auto memLock = Memory::Lock();586std::ostringstream stream;587stream << (Memory::IsValidAddress(curAddress_) ? Memory::Read_Float(curAddress_) : NAN);588auto temp_string = stream.str();589W32Util::CopyTextToClipboard(wnd, temp_string.c_str());590}591break;592593case ID_MEMVIEW_EXTENTBEGIN:594{595std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);596uint32_t addr = curAddress_;597for (MemBlockInfo info : memRangeInfo) {598addr = info.start;599}600gotoAddr(addr);601break;602}603604case ID_MEMVIEW_EXTENTEND:605{606std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);607uint32_t addr = curAddress_;608for (MemBlockInfo info : memRangeInfo) {609addr = info.start + info.size - 1;610}611gotoAddr(addr);612break;613}614615case ID_MEMVIEW_COPYADDRESS:616{617char temp[24];618snprintf(temp, sizeof(temp), "0x%08X", curAddress_);619W32Util::CopyTextToClipboard(wnd, temp);620}621break;622623case ID_MEMVIEW_GOTOINDISASM:624if (disasmWindow) {625disasmWindow->Goto(curAddress_);626disasmWindow->Show(true);627}628break;629}630return;631}632633int x = LOWORD(lParam);634int y = HIWORD(lParam);635ReleaseCapture();636GotoPoint(x, y, GotoModeFromModifiers(button == 2));637}638639void CtrlMemView::onMouseMove(WPARAM wParam, LPARAM lParam, int button) {640int x = LOWORD(lParam);641int y = HIWORD(lParam);642643if (button & 1) {644GotoPoint(x, y, GotoModeFromModifiers(button == 2));645}646}647648void CtrlMemView::updateStatusBarText() {649std::vector<MemBlockInfo> memRangeInfo = FindMemInfoByFlag(highlightFlags_, curAddress_, 1);650651char text[512];652snprintf(text, sizeof(text), "%08X", curAddress_);653// There should only be one.654for (MemBlockInfo info : memRangeInfo) {655snprintf(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);656}657658SendMessage(GetParent(wnd), WM_DEB_SETSTATUSBARTEXT, 0, (LPARAM)text);659}660661void CtrlMemView::UpdateSelectRange(uint32_t target, GotoMode mode) {662if (mode == GotoMode::FROM_CUR && lastSelectReset_ == 0) {663lastSelectReset_ = curAddress_;664}665666switch (mode) {667case GotoMode::RESET:668selectRangeStart_ = target;669selectRangeEnd_ = target + 1;670lastSelectReset_ = target;671break;672673case GotoMode::RESET_IF_OUTSIDE:674if (target < selectRangeStart_ || target >= selectRangeEnd_) {675selectRangeStart_ = target;676selectRangeEnd_ = target + 1;677lastSelectReset_ = target;678}679break;680681case GotoMode::FROM_CUR:682selectRangeStart_ = lastSelectReset_ > target ? target : lastSelectReset_;683selectRangeEnd_ = selectRangeStart_ == lastSelectReset_ ? target + 1 : lastSelectReset_ + 1;684break;685686case GotoMode::EXTEND:687if (target < selectRangeStart_)688selectRangeStart_ = target;689if (target > selectRangeEnd_)690selectRangeEnd_ = target;691break;692}693curAddress_ = target;694}695696void CtrlMemView::GotoPoint(int x, int y, GotoMode mode) {697int line = y / rowHeight_;698int lineAddress = windowStart_ + line * rowSize_;699700if (displayOffsetScale_) {701// ignore clicks on the offset space702if (line < offsetSpace) {703updateStatusBarText();704redraw();705return;706}707// 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 adjusted708lineAddress -= rowSize_ * offsetSpace;709}710711uint32_t target = curAddress_;712uint32_t targetNibble = selectedNibble_;713bool targetAscii = asciiSelected_;714if (x >= asciiStartX_) {715int col = (x - asciiStartX_) / (charWidth_ + 2);716if (col >= rowSize_)717return;718719targetAscii = true;720target = lineAddress + col;721targetNibble = 0;722} else if (x >= hexStartX_) {723int col = (x - hexStartX_) / charWidth_;724if ((col/3) >= rowSize_)725return;726727switch (col % 3) {728case 0: targetNibble = 0; break;729case 1: targetNibble = 1; break;730case 2: return; // don't change position when clicking on the space731}732733targetAscii = false;734target = lineAddress + col / 3;735}736737if (target != curAddress_ || targetNibble != selectedNibble_ || targetAscii != asciiSelected_) {738selectedNibble_ = targetNibble;739asciiSelected_ = targetAscii;740UpdateSelectRange(target, mode);741742updateStatusBarText();743redraw();744}745}746747void CtrlMemView::gotoAddr(unsigned int addr) {748int lines = rect_.bottom / rowHeight_;749u32 windowEnd = windowStart_ + lines * rowSize_;750751curAddress_ = addr;752lastSelectReset_ = curAddress_;753selectRangeStart_ = curAddress_;754selectRangeEnd_ = curAddress_ + 1;755selectedNibble_ = 0;756757if (curAddress_ < windowStart_ || curAddress_ >= windowEnd) {758windowStart_ = curAddress_ & ~15;759}760761updateStatusBarText();762redraw();763}764765void CtrlMemView::ScrollWindow(int lines, GotoMode mode) {766windowStart_ += lines * rowSize_;767768UpdateSelectRange(curAddress_ + lines * rowSize_, mode);769770updateStatusBarText();771redraw();772}773774void CtrlMemView::ScrollCursor(int bytes, GotoMode mode) {775if (!asciiSelected_ && bytes == 1) {776if (selectedNibble_ == 0) {777selectedNibble_ = 1;778bytes = 0;779} else {780selectedNibble_ = 0;781}782} else if (!asciiSelected_ && bytes == -1) {783if (selectedNibble_ == 0) {784selectedNibble_ = 1;785} else {786selectedNibble_ = 0;787bytes = 0;788}789}790791UpdateSelectRange(curAddress_ + bytes, mode);792793u32 windowEnd = windowStart_ + visibleRows_ * rowSize_;794if (curAddress_ < windowStart_) {795windowStart_ = curAddress_ & ~15;796} else if (curAddress_ >= windowEnd) {797windowStart_ = (curAddress_ - (visibleRows_ - 1) * rowSize_) & ~15;798}799800updateStatusBarText();801redraw();802}803804bool CtrlMemView::ParseSearchString(const std::string &query, bool asHex, std::vector<uint8_t> &data) {805data.clear();806if (!asHex) {807for (size_t i = 0; i < query.length(); i++) {808data.push_back(query[i]);809}810return true;811}812813for (size_t index = 0; index < query.size(); ) {814if (isspace(query[index])) {815index++;816continue;817}818819u8 value = 0;820for (int i = 0; i < 2 && index < query.size(); i++) {821char c = tolower(query[index++]);822if (c >= 'a' && c <= 'f') {823value |= (c - 'a' + 10) << (1 - i) * 4;824} else if (c >= '0' && c <= '9') {825value |= (c - '0') << (1 - i) * 4;826} else {827return false;828}829}830831data.push_back(value);832}833834return true;835}836837std::vector<u32> CtrlMemView::searchString(const std::string &searchQuery) {838std::vector<u32> searchResAddrs;839840auto memLock = Memory::Lock();841if (!PSP_IsInited())842return searchResAddrs;843844std::vector<u8> searchData;845if (!ParseSearchString(searchQuery, false, searchData))846return searchResAddrs;847848if (searchData.empty())849return searchResAddrs;850851std::vector<std::pair<u32, u32>> memoryAreas;852memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());853// Ignore the video memory mirrors.854memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);855memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());856857for (const auto &area : memoryAreas) {858const u32 segmentStart = area.first;859const u32 segmentEnd = area.second - (u32)searchData.size();860861for (u32 pos = segmentStart; pos < segmentEnd; pos++) {862if ((pos % 256) == 0 && KeyDownAsync(VK_ESCAPE)) {863return searchResAddrs;864}865866const u8 *ptr = Memory::GetPointerUnchecked(pos);867if (memcmp(ptr, searchData.data(), searchData.size()) == 0) {868searchResAddrs.push_back(pos);869}870}871}872873return searchResAddrs;874};875876void CtrlMemView::search(bool continueSearch) {877auto memLock = Memory::Lock();878if (!PSP_IsInited())879return;880881u32 searchAddress = 0;882u32 segmentStart = 0;883u32 segmentEnd = 0;884if (continueSearch == false || searchQuery_.empty()) {885if (InputBox_GetString(GetModuleHandle(NULL), wnd, L"Search for", searchQuery_, searchQuery_) == false) {886SetFocus(wnd);887return;888}889SetFocus(wnd);890searchAddress = curAddress_ + 1;891} else {892searchAddress = matchAddress_ + 1;893}894895std::vector<u8> searchData;896if (!ParseSearchString(searchQuery_, !asciiSelected_, searchData)) {897MessageBox(wnd, L"Invalid search text.", L"Error", MB_OK);898return;899}900901std::vector<std::pair<u32, u32>> memoryAreas;902// Ignore the video memory mirrors.903memoryAreas.emplace_back(PSP_GetVidMemBase(), 0x04200000);904memoryAreas.emplace_back(PSP_GetKernelMemoryBase(), PSP_GetUserMemoryEnd());905memoryAreas.emplace_back(PSP_GetScratchpadMemoryBase(), PSP_GetScratchpadMemoryEnd());906907searching_ = true;908redraw(); // so the cursor is disabled909910for (size_t i = 0; i < memoryAreas.size(); i++) {911segmentStart = memoryAreas[i].first;912segmentEnd = memoryAreas[i].second;913914// better safe than sorry, I guess915if (!Memory::IsValidAddress(segmentStart))916continue;917const u8 *dataPointer = Memory::GetPointerUnchecked(segmentStart);918919if (searchAddress < segmentStart)920searchAddress = segmentStart;921if (searchAddress >= segmentEnd)922continue;923924int index = searchAddress-segmentStart;925int endIndex = segmentEnd-segmentStart - (int)searchData.size();926927while (index < endIndex) {928// cancel search929if ((index % 256) == 0 && KeyDownAsync(VK_ESCAPE)) {930searching_ = false;931return;932}933if (memcmp(&dataPointer[index], searchData.data(), searchData.size()) == 0) {934matchAddress_ = index + segmentStart;935searching_ = false;936gotoAddr(matchAddress_);937return;938}939index++;940}941}942943MessageBox(wnd, L"Not found", L"Search", MB_OK);944searching_ = false;945redraw();946}947948void CtrlMemView::drawOffsetScale(HDC hdc) {949int currentX = addressStartX_;950951SetTextColor(hdc, 0x600000);952TextOutA(hdc, currentX, offsetPositionY_, "Offset", 6);953954// the start offset, the size of the hex addresses and one space955currentX = addressStartX_ + ((8 + 1) * charWidth_);956957char temp[64];958for (int i = 0; i < 16; i++) {959snprintf(temp, sizeof(temp), "%02X", i);960TextOutA(hdc, currentX, offsetPositionY_, temp, 2);961currentX += 3 * charWidth_; // hex and space962}963}964965void CtrlMemView::toggleOffsetScale(CommonToggles toggle) {966if (toggle == On)967displayOffsetScale_ = true;968else if (toggle == Off)969displayOffsetScale_ = false;970971updateStatusBarText();972redraw();973}974975void CtrlMemView::setHighlightType(MemBlockFlags flags) {976if (highlightFlags_ != flags) {977highlightFlags_ = flags;978updateStatusBarText();979redraw();980}981}982983uint32_t CtrlMemView::pickTagColor(const std::string &tag) {984int colors[6] = { 0xe0FFFF, 0xFFE0E0, 0xE8E8FF, 0xFFE0FF, 0xE0FFE0, 0xFFFFE0 };985int which = XXH3_64bits(tag.c_str(), tag.length()) % ARRAY_SIZE(colors);986return colors[which];987}988989990