2012-11-01 16:19:01 +01:00
// NOTE: Apologies for the quality of this code, this is really from pre-opensource Dolphin - that is, 2003.
2022-02-10 20:39:29 -08:00
# include <cctype>
2012-11-01 16:19:01 +01:00
# include <tchar.h>
# include <math.h>
2020-05-24 18:18:48 +01:00
# include <iomanip>
2021-02-07 12:02:47 -08:00
# include "ext/xxhash.h"
2013-07-10 11:10:08 +02:00
# include "Core/Config.h"
2019-03-03 09:18:11 +01:00
# include "Windows/resource.h"
# include "Core/MemMap.h"
2021-10-07 19:47:16 -07:00
# include "Windows/W32Util/ContextMenu.h"
2019-03-03 09:18:11 +01:00
# include "Windows/W32Util/Misc.h"
2013-09-01 18:43:41 +02:00
# include "Windows/InputBox.h"
2019-03-03 09:18:11 +01:00
# include "Windows/main.h"
2020-10-04 10:10:55 +02:00
# include "Common/System/Display.h"
2012-11-01 16:19:01 +01:00
# include "Debugger_Disasm.h"
2013-06-30 13:42:19 +02:00
# include "DebuggerShared.h"
2012-11-01 16:19:01 +01:00
# include "CtrlMemView.h"
2013-10-01 21:11:41 +02:00
# include "DumpMemoryWindow.h"
2012-11-01 16:19:01 +01:00
2013-08-26 19:00:16 +02:00
wchar_t CtrlMemView : : szClassName [ ] = L " CtrlMemView " ;
2012-11-01 16:19:01 +01:00
2021-12-12 10:22:21 -08:00
static constexpr UINT_PTR IDT_REDRAW_DELAYED = 0xC0DE0001 ;
static constexpr UINT REDRAW_DELAY = 1000 / 60 ;
// We also redraw regularly, since data changes during runtime.
static constexpr UINT_PTR IDT_REDRAW_AUTO = 0xC0DE0002 ;
static constexpr UINT REDRAW_INTERVAL = 1000 ;
2012-11-01 16:19:01 +01:00
CtrlMemView : : CtrlMemView ( HWND _wnd )
{
2013-08-26 20:29:24 +02:00
wnd = _wnd ;
2015-07-15 20:02:42 +02:00
SetWindowLongPtr ( wnd , GWLP_USERDATA , ( LONG_PTR ) this ) ;
2013-08-26 20:29:24 +02:00
SetWindowLong ( wnd , GWL_STYLE , GetWindowLong ( wnd , GWL_STYLE ) | WS_VSCROLL ) ;
SetScrollRange ( wnd , SB_VERT , - 1 , 1 , TRUE ) ;
2013-07-10 11:10:08 +02:00
2019-03-03 09:18:11 +01:00
const float fontScale = 1.0f / g_dpi_scale_real_y ;
rowHeight = g_Config . iFontHeight * fontScale ;
charWidth = g_Config . iFontWidth * fontScale ;
offsetPositionY = offsetLine * rowHeight ;
2013-07-10 11:10:08 +02:00
2019-03-03 09:18:11 +01:00
font = CreateFont ( rowHeight , charWidth , 0 , 0 ,
FW_DONTCARE , FALSE , FALSE , FALSE , DEFAULT_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , DEFAULT_QUALITY , DEFAULT_PITCH ,
L " Lucida Console " ) ;
underlineFont = CreateFont ( rowHeight , charWidth , 0 , 0 ,
FW_DONTCARE , FALSE , TRUE , FALSE , DEFAULT_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , DEFAULT_QUALITY , DEFAULT_PITCH ,
L " Lucida Console " ) ;
curAddress = 0 ;
2013-08-26 20:29:24 +02:00
debugger = 0 ;
2019-03-03 09:18:11 +01:00
2013-09-01 18:43:41 +02:00
searchQuery = " " ;
matchAddress = - 1 ;
searching = false ;
2013-06-25 02:43:31 +02:00
hasFocus = false ;
windowStart = curAddress ;
asciiSelected = false ;
selectedNibble = 0 ;
rowSize = 16 ;
addressStart = charWidth ;
hexStart = addressStart + 9 * charWidth ;
asciiStart = hexStart + ( rowSize * 3 + 1 ) * charWidth ;
2013-11-07 18:39:41 +01:00
// set redraw timer
2021-12-12 10:22:21 -08:00
SetTimer ( wnd , IDT_REDRAW_AUTO , REDRAW_INTERVAL , nullptr ) ;
2012-11-01 16:19:01 +01:00
}
CtrlMemView : : ~ CtrlMemView ( )
{
2014-02-14 21:08:24 -08:00
DeleteObject ( font ) ;
DeleteObject ( underlineFont ) ;
2012-11-01 16:19:01 +01:00
}
void CtrlMemView : : init ( )
{
WNDCLASSEX wc ;
wc . cbSize = sizeof ( wc ) ;
wc . lpszClassName = szClassName ;
wc . hInstance = GetModuleHandle ( 0 ) ;
wc . lpfnWndProc = CtrlMemView : : wndProc ;
wc . hCursor = LoadCursor ( NULL , IDC_ARROW ) ;
wc . hIcon = 0 ;
wc . lpszMenuName = 0 ;
wc . hbrBackground = ( HBRUSH ) GetSysColorBrush ( COLOR_WINDOW ) ;
wc . style = 0 ;
wc . cbClsExtra = 0 ;
wc . cbWndExtra = sizeof ( CtrlMemView * ) ;
wc . hIconSm = 0 ;
RegisterClassEx ( & wc ) ;
}
void CtrlMemView : : deinit ( )
{
//UnregisterClass(szClassName, hInst)
}
LRESULT CALLBACK CtrlMemView : : wndProc ( HWND hwnd , UINT msg , WPARAM wParam , LPARAM lParam )
{
CtrlMemView * ccp = CtrlMemView : : getFrom ( hwnd ) ;
static bool lmbDown = false , rmbDown = false ;
2018-10-21 07:37:35 +01:00
2012-11-01 16:19:01 +01:00
switch ( msg )
{
case WM_NCCREATE :
// Allocate a new CustCtrl structure for this window.
ccp = new CtrlMemView ( hwnd ) ;
// Continue with window creation.
return ccp ! = NULL ;
// Clean up when the window is destroyed.
case WM_NCDESTROY :
delete ccp ;
break ;
case WM_SETFONT :
break ;
case WM_SIZE :
ccp - > redraw ( ) ;
break ;
case WM_PAINT :
ccp - > onPaint ( wParam , lParam ) ;
break ;
case WM_VSCROLL :
ccp - > onVScroll ( wParam , lParam ) ;
break ;
2013-06-25 02:43:31 +02:00
case WM_MOUSEWHEEL :
if ( GET_WHEEL_DELTA_WPARAM ( wParam ) > 0 )
{
ccp - > scrollWindow ( - 3 ) ;
} else if ( GET_WHEEL_DELTA_WPARAM ( wParam ) < 0 ) {
ccp - > scrollWindow ( 3 ) ;
}
break ;
2012-11-01 16:19:01 +01:00
case WM_ERASEBKGND :
return FALSE ;
case WM_KEYDOWN :
ccp - > onKeyDown ( wParam , lParam ) ;
2013-06-29 22:25:17 +02:00
return 0 ;
2013-06-25 02:43:31 +02:00
case WM_CHAR :
ccp - > onChar ( wParam , lParam ) ;
2013-06-29 22:25:17 +02:00
return 0 ;
case WM_KEYUP :
return 0 ;
2012-11-01 16:19:01 +01:00
case WM_LBUTTONDOWN : SetFocus ( hwnd ) ; lmbDown = true ; ccp - > onMouseDown ( wParam , lParam , 1 ) ; break ;
2013-06-25 13:17:27 +02:00
case WM_RBUTTONDOWN : SetFocus ( hwnd ) ; rmbDown = true ; ccp - > onMouseDown ( wParam , lParam , 2 ) ; break ;
2012-11-01 16:19:01 +01:00
case WM_MOUSEMOVE : ccp - > onMouseMove ( wParam , lParam , ( lmbDown ? 1 : 0 ) | ( rmbDown ? 2 : 0 ) ) ; break ;
case WM_LBUTTONUP : lmbDown = false ; ccp - > onMouseUp ( wParam , lParam , 1 ) ; break ;
case WM_RBUTTONUP : rmbDown = false ; ccp - > onMouseUp ( wParam , lParam , 2 ) ; break ;
case WM_SETFOCUS :
SetFocus ( hwnd ) ;
ccp - > hasFocus = true ;
ccp - > redraw ( ) ;
break ;
case WM_KILLFOCUS :
ccp - > hasFocus = false ;
ccp - > redraw ( ) ;
break ;
2013-06-25 02:43:31 +02:00
case WM_GETDLGCODE : // we want to process the arrow keys and all characters ourselves
2013-06-30 13:42:19 +02:00
return DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTTAB ;
2013-06-25 02:43:31 +02:00
break ;
2013-11-07 18:39:41 +01:00
case WM_TIMER :
2021-12-12 10:22:21 -08:00
// This is actually delayed too, using another timer. That way we won't update twice.
if ( wParam = = IDT_REDRAW_AUTO & & IsWindowVisible ( ccp - > wnd ) )
2013-11-07 18:39:41 +01:00
ccp - > redraw ( ) ;
2021-12-12 10:22:21 -08:00
if ( wParam = = IDT_REDRAW_DELAYED ) {
InvalidateRect ( hwnd , nullptr , FALSE ) ;
UpdateWindow ( hwnd ) ;
ccp - > redrawScheduled_ = false ;
KillTimer ( hwnd , wParam ) ;
}
2013-11-07 18:39:41 +01:00
break ;
2012-11-01 16:19:01 +01:00
default :
break ;
}
return DefWindowProc ( hwnd , msg , wParam , lParam ) ;
}
CtrlMemView * CtrlMemView : : getFrom ( HWND hwnd )
{
return ( CtrlMemView * ) GetWindowLongPtr ( hwnd , GWLP_USERDATA ) ;
}
2021-02-07 11:39:51 -08:00
void CtrlMemView : : onPaint ( WPARAM wParam , LPARAM lParam ) {
2018-05-06 10:35:56 -07:00
auto memLock = Memory : : Lock ( ) ;
2013-06-25 02:43:31 +02:00
// draw to a bitmap for double buffering
PAINTSTRUCT ps ;
HDC actualHdc = BeginPaint ( wnd , & ps ) ;
HDC hdc = CreateCompatibleDC ( actualHdc ) ;
HBITMAP hBM = CreateCompatibleBitmap ( actualHdc , rect . right - rect . left , rect . bottom - rect . top ) ;
SelectObject ( hdc , hBM ) ;
SetBkMode ( hdc , OPAQUE ) ;
HPEN standardPen = CreatePen ( 0 , 0 , 0xFFFFFF ) ;
HBRUSH standardBrush = CreateSolidBrush ( 0xFFFFFF ) ;
2021-02-07 11:39:51 -08:00
COLORREF standardBG = GetBkColor ( hdc ) ;
2013-06-25 02:43:31 +02:00
HPEN oldPen = ( HPEN ) SelectObject ( hdc , standardPen ) ;
HBRUSH oldBrush = ( HBRUSH ) SelectObject ( hdc , standardBrush ) ;
HFONT oldFont = ( HFONT ) SelectObject ( hdc , ( HGDIOBJ ) font ) ;
// white background
SelectObject ( hdc , standardPen ) ;
SelectObject ( hdc , standardBrush ) ;
Rectangle ( hdc , 0 , 0 , rect . right , rect . bottom ) ;
2018-10-22 03:17:45 +01:00
if ( displayOffsetScale )
2018-10-21 07:37:35 +01:00
drawOffsetScale ( hdc ) ;
2021-02-07 11:39:51 -08:00
std : : vector < MemBlockInfo > memRangeInfo = FindMemInfoByFlag ( highlightFlags_ , windowStart , ( visibleRows + 1 ) * rowSize ) ;
COLORREF lastTextCol = 0x000000 ;
COLORREF lastBGCol = standardBG ;
auto setTextColors = [ & ] ( COLORREF fg , COLORREF bg ) {
if ( lastTextCol ! = fg ) {
SetTextColor ( hdc , fg ) ;
lastTextCol = fg ;
}
if ( lastBGCol ! = bg ) {
SetBkColor ( hdc , bg ) ;
lastBGCol = bg ;
}
} ;
2018-10-21 07:37:35 +01:00
2013-06-25 02:43:31 +02:00
// draw one extra row that may be partially visible
2021-02-07 11:39:51 -08:00
for ( int i = 0 ; i < visibleRows + 1 ; i + + ) {
int rowY = rowHeight * i ;
// Skip the first X rows to make space for the offsets.
2018-10-22 03:17:45 +01:00
if ( displayOffsetScale )
2021-02-07 11:39:51 -08:00
rowY + = rowHeight * offsetSpace ;
2013-06-25 02:43:31 +02:00
2021-02-07 11:39:51 -08:00
char temp [ 32 ] ;
uint32_t address = windowStart + i * rowSize ;
sprintf ( temp , " %08X " , address ) ;
2013-08-26 20:29:24 +02:00
2021-02-07 11:39:51 -08:00
setTextColors ( 0x600000 , standardBG ) ;
TextOutA ( hdc , addressStart , rowY , temp , ( int ) strlen ( temp ) ) ;
union {
uint32_t words [ 4 ] ;
uint8_t bytes [ 16 ] ;
} memory ;
bool valid = debugger ! = nullptr & & debugger - > isAlive ( ) & & Memory : : IsValidAddress ( address ) ;
for ( int i = 0 ; valid & & i < 4 ; + + i ) {
memory . words [ i ] = debugger - > readMemory ( address + i * 4 ) ;
2013-08-26 20:29:24 +02:00
}
2012-11-01 16:19:01 +01:00
2021-02-07 11:39:51 -08:00
for ( int j = 0 ; j < rowSize ; j + + ) {
2021-02-07 17:06:14 -08:00
const uint32_t byteAddress = ( address + j ) & ~ 0xC0000000 ;
2021-02-07 11:39:51 -08:00
std : : string tag ;
2021-02-07 12:02:47 -08:00
bool tagContinues = false ;
2021-02-07 11:39:51 -08:00
for ( auto info : memRangeInfo ) {
2021-02-07 12:02:47 -08:00
if ( info . start < = byteAddress & & info . start + info . size > byteAddress ) {
2021-02-07 11:39:51 -08:00
tag = info . tag ;
2021-02-07 12:02:47 -08:00
tagContinues = byteAddress + 1 < info . start + info . size ;
}
2012-11-01 16:19:01 +01:00
}
2021-02-07 11:39:51 -08:00
int hexX = hexStart + j * 3 * charWidth ;
2021-02-07 12:02:47 -08:00
int hexLen = 2 ;
2021-02-07 11:39:51 -08:00
int asciiX = asciiStart + j * ( charWidth + 2 ) ;
char c ;
if ( valid ) {
2021-02-07 12:02:47 -08:00
sprintf ( temp , " %02X " , memory . bytes [ j ] ) ;
2021-02-07 11:39:51 -08:00
c = ( char ) memory . bytes [ j ] ;
if ( memory . bytes [ j ] < 32 | | memory . bytes [ j ] > = 128 )
c = ' . ' ;
} else {
strcpy ( temp , " ?? " ) ;
c = ' . ' ;
}
COLORREF hexBGCol = standardBG ;
COLORREF hexTextCol = 0x000000 ;
2021-02-07 12:18:59 -08:00
COLORREF continueBGCol = standardBG ;
2021-02-07 11:39:51 -08:00
COLORREF asciiBGCol = standardBG ;
COLORREF asciiTextCol = 0x000000 ;
int underline = - 1 ;
2021-02-07 17:06:14 -08:00
if ( address + j = = curAddress & & searching = = false ) {
2021-02-07 11:39:51 -08:00
if ( asciiSelected ) {
hexBGCol = 0xC0C0C0 ;
hexTextCol = 0x000000 ;
asciiBGCol = hasFocus ? 0xFF9933 : 0xC0C0C0 ;
asciiTextCol = hasFocus ? 0xFFFFFF : 0x000000 ;
} else {
hexBGCol = hasFocus ? 0xFF9933 : 0xC0C0C0 ;
hexTextCol = hasFocus ? 0xFFFFFF : 0x000000 ;
asciiBGCol = 0xC0C0C0 ;
asciiTextCol = 0x000000 ;
underline = selectedNibble ;
}
2021-02-07 12:18:59 -08:00
if ( ! tag . empty ( ) & & tagContinues ) {
continueBGCol = pickTagColor ( tag ) ;
}
2021-02-07 11:39:51 -08:00
} else if ( ! tag . empty ( ) ) {
hexBGCol = pickTagColor ( tag ) ;
2021-02-07 12:18:59 -08:00
continueBGCol = hexBGCol ;
2021-02-07 11:39:51 -08:00
asciiBGCol = pickTagColor ( tag ) ;
2021-02-07 12:02:47 -08:00
hexLen = tagContinues ? 3 : 2 ;
2021-02-07 11:39:51 -08:00
}
setTextColors ( hexTextCol , hexBGCol ) ;
2021-02-07 12:18:59 -08:00
if ( underline > = 0 ) {
SelectObject ( hdc , underline = = 0 ? ( HGDIOBJ ) underlineFont : ( HGDIOBJ ) font ) ;
2021-02-07 11:39:51 -08:00
TextOutA ( hdc , hexX , rowY , & temp [ 0 ] , 1 ) ;
2021-02-07 12:18:59 -08:00
SelectObject ( hdc , underline = = 0 ? ( HGDIOBJ ) font : ( HGDIOBJ ) underlineFont ) ;
2021-02-07 11:39:51 -08:00
TextOutA ( hdc , hexX + charWidth , rowY , & temp [ 1 ] , 1 ) ;
SelectObject ( hdc , ( HGDIOBJ ) font ) ;
2021-02-07 12:18:59 -08:00
// If the tag keeps going, draw the BG too.
if ( continueBGCol ! = standardBG ) {
setTextColors ( 0x000000 , continueBGCol ) ;
TextOutA ( hdc , hexX + charWidth * 2 , rowY , & temp [ 2 ] , 1 ) ;
}
2021-02-07 11:39:51 -08:00
} else {
2021-02-07 12:02:47 -08:00
TextOutA ( hdc , hexX , rowY , temp , hexLen ) ;
2021-02-07 11:39:51 -08:00
}
setTextColors ( asciiTextCol , asciiBGCol ) ;
TextOutA ( hdc , asciiX , rowY , & c , 1 ) ;
2012-11-01 16:19:01 +01:00
}
}
2021-02-07 11:39:51 -08:00
setTextColors ( 0x000000 , standardBG ) ;
2012-11-01 16:19:01 +01:00
SelectObject ( hdc , oldFont ) ;
SelectObject ( hdc , oldPen ) ;
SelectObject ( hdc , oldBrush ) ;
2013-06-25 02:43:31 +02:00
// copy bitmap to the actual hdc
2018-10-21 07:37:35 +01:00
BitBlt ( actualHdc , 0 , 0 , rect . right , rect . bottom , hdc , 0 , 0 , SRCCOPY ) ;
2013-06-25 02:43:31 +02:00
DeleteObject ( hBM ) ;
DeleteDC ( hdc ) ;
2012-11-01 16:19:01 +01:00
2013-06-25 02:43:31 +02:00
DeleteObject ( standardPen ) ;
DeleteObject ( standardBrush ) ;
2012-11-01 16:19:01 +01:00
EndPaint ( wnd , & ps ) ;
}
void CtrlMemView : : onVScroll ( WPARAM wParam , LPARAM lParam )
{
switch ( wParam & 0xFFFF )
{
case SB_LINEDOWN :
2013-06-25 02:43:31 +02:00
scrollWindow ( 1 ) ;
2012-11-01 16:19:01 +01:00
break ;
case SB_LINEUP :
2013-06-25 02:43:31 +02:00
scrollWindow ( - 1 ) ;
2012-11-01 16:19:01 +01:00
break ;
case SB_PAGEDOWN :
2013-06-25 02:43:31 +02:00
scrollWindow ( visibleRows ) ;
2012-11-01 16:19:01 +01:00
break ;
case SB_PAGEUP :
2013-06-25 02:43:31 +02:00
scrollWindow ( - visibleRows ) ;
2012-11-01 16:19:01 +01:00
break ;
default :
return ;
}
}
void CtrlMemView : : onKeyDown ( WPARAM wParam , LPARAM lParam )
{
2013-11-05 11:29:55 +01:00
if ( KeyDownAsync ( VK_CONTROL ) )
2013-09-01 18:43:41 +02:00
{
switch ( tolower ( wParam & 0xFFFF ) )
{
case ' g ' :
{
u32 addr ;
if ( executeExpressionWindow ( wnd , debugger , addr ) = = false ) return ;
gotoAddr ( addr ) ;
return ;
}
break ;
case ' f ' :
case ' s ' :
search ( false ) ;
return ;
case ' c ' :
search ( true ) ;
return ;
}
2013-06-29 22:25:17 +02:00
}
2012-11-01 16:19:01 +01:00
switch ( wParam & 0xFFFF )
{
case VK_DOWN :
2013-06-25 02:43:31 +02:00
scrollCursor ( rowSize ) ;
2012-11-01 16:19:01 +01:00
break ;
case VK_UP :
2013-06-25 02:43:31 +02:00
scrollCursor ( - rowSize ) ;
break ;
case VK_LEFT :
scrollCursor ( - 1 ) ;
break ;
case VK_RIGHT :
scrollCursor ( 1 ) ;
2012-11-01 16:19:01 +01:00
break ;
case VK_NEXT :
2013-06-25 02:43:31 +02:00
scrollWindow ( visibleRows ) ;
2012-11-01 16:19:01 +01:00
break ;
case VK_PRIOR :
2013-06-25 02:43:31 +02:00
scrollWindow ( - visibleRows ) ;
2012-11-01 16:19:01 +01:00
break ;
2013-06-30 13:42:19 +02:00
case VK_TAB :
SendMessage ( GetParent ( wnd ) , WM_DEB_TABPRESSED , 0 , 0 ) ;
break ;
2012-11-01 16:19:01 +01:00
default :
return ;
}
}
2013-06-25 02:43:31 +02:00
void CtrlMemView : : onChar ( WPARAM wParam , LPARAM lParam )
{
2014-07-19 23:25:32 -07:00
auto memLock = Memory : : Lock ( ) ;
if ( ! PSP_IsInited ( ) )
return ;
2013-11-05 11:29:55 +01:00
if ( KeyDownAsync ( VK_CONTROL ) | | wParam = = VK_TAB ) return ;
2013-06-29 22:25:17 +02:00
2013-06-25 02:43:31 +02:00
if ( ! Memory : : IsValidAddress ( curAddress ) )
{
scrollCursor ( 1 ) ;
return ;
}
bool active = Core_IsActive ( ) ;
2021-10-23 16:56:15 -07:00
if ( active ) Core_EnableStepping ( true , " memory.access " , curAddress ) ;
2013-06-25 02:43:31 +02:00
if ( asciiSelected )
{
u8 newValue = wParam ;
Memory : : WriteUnchecked_U8 ( newValue , curAddress ) ;
scrollCursor ( 1 ) ;
} else {
wParam = tolower ( wParam ) ;
int inputValue = - 1 ;
if ( wParam > = ' 0 ' & & wParam < = ' 9 ' ) inputValue = wParam - ' 0 ' ;
if ( wParam > = ' a ' & & wParam < = ' f ' ) inputValue = wParam - ' a ' + 10 ;
if ( inputValue > = 0 )
{
int shiftAmount = ( 1 - selectedNibble ) * 4 ;
u8 oldValue = Memory : : ReadUnchecked_U8 ( curAddress ) ;
oldValue & = ~ ( 0xF < < shiftAmount ) ;
u8 newValue = oldValue | ( inputValue < < shiftAmount ) ;
Memory : : WriteUnchecked_U8 ( newValue , curAddress ) ;
scrollCursor ( 1 ) ;
}
}
if ( active ) Core_EnableStepping ( false ) ;
}
2012-11-01 16:19:01 +01:00
void CtrlMemView : : redraw ( )
{
2013-06-25 02:43:31 +02:00
GetClientRect ( wnd , & rect ) ;
visibleRows = ( rect . bottom / rowHeight ) ;
2018-10-22 03:17:45 +01:00
if ( displayOffsetScale ) {
2018-10-21 07:37:35 +01:00
visibleRows - = offsetSpace ; // 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
2018-10-21 05:17:38 +01:00
}
2021-12-12 10:22:21 -08:00
if ( ! redrawScheduled_ ) {
SetTimer ( wnd , IDT_REDRAW_DELAYED , REDRAW_DELAY , nullptr ) ;
redrawScheduled_ = true ;
}
2012-11-01 16:19:01 +01:00
}
void CtrlMemView : : onMouseDown ( WPARAM wParam , LPARAM lParam , int button )
{
int x = LOWORD ( lParam ) ;
2013-06-25 02:43:31 +02:00
int y = HIWORD ( lParam ) ;
gotoPoint ( x , y ) ;
2012-11-01 16:19:01 +01:00
}
void CtrlMemView : : onMouseUp ( WPARAM wParam , LPARAM lParam , int button )
{
if ( button = = 2 )
{
2013-11-07 19:49:46 +01:00
bool enable16 = ! asciiSelected & & ( curAddress % 2 ) = = 0 ;
bool enable32 = ! asciiSelected & & ( curAddress % 4 ) = = 0 ;
2021-10-07 19:47:16 -07:00
HMENU menu = GetContextMenu ( ContextMenuID : : MEMVIEW ) ;
2013-11-07 19:49:46 +01:00
EnableMenuItem ( menu , ID_MEMVIEW_COPYVALUE_16 , enable16 ? MF_ENABLED : MF_GRAYED ) ;
EnableMenuItem ( menu , ID_MEMVIEW_COPYVALUE_32 , enable32 ? MF_ENABLED : MF_GRAYED ) ;
2021-10-07 19:47:16 -07:00
switch ( TriggerContextMenu ( ContextMenuID : : MEMVIEW , wnd , ContextPoint : : FromEvent ( lParam ) ) )
2012-11-01 16:19:01 +01:00
{
case ID_MEMVIEW_DUMP :
2013-03-22 12:14:10 +01:00
{
2013-11-20 16:57:12 +01:00
DumpMemoryWindow dump ( wnd , debugger ) ;
2013-11-19 19:37:09 +02:00
dump . exec ( ) ;
break ;
2013-11-20 16:57:12 +01:00
}
2013-11-20 17:43:26 +02:00
2013-11-07 19:49:46 +01:00
case ID_MEMVIEW_COPYVALUE_8 :
2012-11-01 16:19:01 +01:00
{
2018-05-06 10:35:56 -07:00
auto memLock = Memory : : Lock ( ) ;
2012-11-01 16:19:01 +01:00
char temp [ 24 ] ;
2013-06-25 13:17:27 +02:00
// it's admittedly not really useful like this
if ( asciiSelected )
{
2013-11-07 19:49:46 +01:00
unsigned char c = Memory : : IsValidAddress ( curAddress ) ? Memory : : Read_U8 ( curAddress ) : ' . ' ;
2013-09-01 18:47:58 +02:00
if ( c < 32 | | c > = 128 ) c = ' . ' ;
2013-06-25 13:17:27 +02:00
sprintf ( temp , " %c " , c ) ;
} else {
2013-11-07 19:49:46 +01:00
sprintf ( temp , " %02X " , Memory : : IsValidAddress ( curAddress ) ? Memory : : Read_U8 ( curAddress ) : 0xFF ) ;
2013-06-25 13:17:27 +02:00
}
2012-11-01 16:19:01 +01:00
W32Util : : CopyTextToClipboard ( wnd , temp ) ;
}
break ;
2013-11-07 19:49:46 +01:00
case ID_MEMVIEW_COPYVALUE_16 :
{
2018-05-06 10:35:56 -07:00
auto memLock = Memory : : Lock ( ) ;
2013-11-07 19:49:46 +01:00
char temp [ 24 ] ;
sprintf ( temp , " %04X " , Memory : : IsValidAddress ( curAddress ) ? Memory : : Read_U16 ( curAddress ) : 0xFFFF ) ;
W32Util : : CopyTextToClipboard ( wnd , temp ) ;
}
break ;
case ID_MEMVIEW_COPYVALUE_32 :
{
2018-05-06 10:35:56 -07:00
auto memLock = Memory : : Lock ( ) ;
2013-11-07 19:49:46 +01:00
char temp [ 24 ] ;
sprintf ( temp , " %08X " , Memory : : IsValidAddress ( curAddress ) ? Memory : : Read_U32 ( curAddress ) : 0xFFFFFFFF ) ;
W32Util : : CopyTextToClipboard ( wnd , temp ) ;
}
break ;
2013-08-30 20:55:58 +02:00
2021-04-03 16:03:21 -07:00
case ID_MEMVIEW_EXTENTBEGIN :
{
std : : vector < MemBlockInfo > memRangeInfo = FindMemInfoByFlag ( highlightFlags_ , curAddress , 1 ) ;
uint32_t addr = curAddress ;
for ( MemBlockInfo info : memRangeInfo ) {
addr = info . start ;
}
gotoAddr ( addr ) ;
break ;
}
case ID_MEMVIEW_EXTENTEND :
{
std : : vector < MemBlockInfo > memRangeInfo = FindMemInfoByFlag ( highlightFlags_ , curAddress , 1 ) ;
uint32_t addr = curAddress ;
for ( MemBlockInfo info : memRangeInfo ) {
addr = info . start + info . size - 1 ;
}
gotoAddr ( addr ) ;
break ;
}
2013-08-30 20:55:58 +02:00
case ID_MEMVIEW_COPYADDRESS :
{
char temp [ 24 ] ;
sprintf ( temp , " 0x%08X " , curAddress ) ;
W32Util : : CopyTextToClipboard ( wnd , temp ) ;
}
2021-10-03 06:48:40 -07:00
break ;
case ID_MEMVIEW_GOTOINDISASM :
if ( disasmWindow ) {
disasmWindow - > Goto ( curAddress ) ;
disasmWindow - > Show ( true ) ;
}
2013-08-30 20:55:58 +02:00
break ;
2012-11-01 16:19:01 +01:00
}
return ;
}
2013-06-25 02:43:31 +02:00
2012-11-01 16:19:01 +01:00
int x = LOWORD ( lParam ) ;
2013-06-25 02:43:31 +02:00
int y = HIWORD ( lParam ) ;
ReleaseCapture ( ) ;
gotoPoint ( x , y ) ;
2012-11-01 16:19:01 +01:00
}
void CtrlMemView : : onMouseMove ( WPARAM wParam , LPARAM lParam , int button )
{
2013-06-25 02:43:31 +02:00
2012-11-01 16:19:01 +01:00
}
2021-02-07 14:07:13 -08:00
void CtrlMemView : : updateStatusBarText ( ) {
std : : vector < MemBlockInfo > memRangeInfo = FindMemInfoByFlag ( highlightFlags_ , curAddress , 1 ) ;
char text [ 512 ] ;
snprintf ( text , sizeof ( text ) , " %08X " , curAddress ) ;
// There should only be one.
for ( MemBlockInfo info : memRangeInfo ) {
2021-02-07 17:06:52 -08:00
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 ) ;
2021-02-07 14:07:13 -08:00
}
SendMessage ( GetParent ( wnd ) , WM_DEB_SETSTATUSBARTEXT , 0 , ( LPARAM ) text ) ;
2013-07-30 16:19:05 +02:00
}
2012-11-01 16:19:01 +01:00
2013-06-25 02:43:31 +02:00
void CtrlMemView : : gotoPoint ( int x , int y )
2012-11-01 16:19:01 +01:00
{
2013-06-25 02:43:31 +02:00
int line = y / rowHeight ;
int lineAddress = windowStart + line * rowSize ;
2018-10-22 03:17:45 +01:00
if ( displayOffsetScale )
2018-10-21 07:37:35 +01:00
{
if ( line < offsetSpace ) // ignore clicks on the offset space
{
updateStatusBarText ( ) ;
redraw ( ) ;
return ;
}
lineAddress - = ( rowSize * offsetSpace ) ; // 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
}
2018-10-21 05:17:38 +01:00
2013-06-25 02:43:31 +02:00
if ( x > = asciiStart )
{
int col = ( x - asciiStart ) / ( charWidth + 2 ) ;
if ( col > = rowSize ) return ;
asciiSelected = true ;
curAddress = lineAddress + col ;
selectedNibble = 0 ;
2013-07-30 16:19:05 +02:00
updateStatusBarText ( ) ;
2013-06-25 02:43:31 +02:00
redraw ( ) ;
} else if ( x > = hexStart )
{
int col = ( x - hexStart ) / charWidth ;
if ( ( col / 3 ) > = rowSize ) return ;
switch ( col % 3 )
{
case 0 : selectedNibble = 0 ; break ;
case 1 : selectedNibble = 1 ; break ;
case 2 : return ; // don't change position when clicking on the space
}
asciiSelected = false ;
curAddress = lineAddress + col / 3 ;
2013-07-30 16:19:05 +02:00
updateStatusBarText ( ) ;
2013-06-25 02:43:31 +02:00
redraw ( ) ;
}
2012-11-01 16:19:01 +01:00
}
2013-06-25 02:43:31 +02:00
void CtrlMemView : : gotoAddr ( unsigned int addr )
{
int lines = ( rect . bottom / rowHeight ) ;
2013-06-29 10:59:42 -07:00
u32 windowEnd = windowStart + lines * rowSize ;
2013-06-25 02:43:31 +02:00
curAddress = addr ;
selectedNibble = 0 ;
if ( curAddress < windowStart | | curAddress > = windowEnd )
{
windowStart = curAddress & ~ 15 ;
}
2013-07-30 16:19:05 +02:00
updateStatusBarText ( ) ;
2013-06-25 02:43:31 +02:00
redraw ( ) ;
}
void CtrlMemView : : scrollWindow ( int lines )
{
windowStart + = lines * rowSize ;
curAddress + = lines * rowSize ;
2021-02-07 17:06:52 -08:00
updateStatusBarText ( ) ;
2013-06-25 02:43:31 +02:00
redraw ( ) ;
}
void CtrlMemView : : scrollCursor ( int bytes )
{
if ( ! asciiSelected & & bytes = = 1 )
{
if ( selectedNibble = = 0 )
{
selectedNibble = 1 ;
bytes = 0 ;
} else {
selectedNibble = 0 ;
}
} else if ( ! asciiSelected & & bytes = = - 1 )
{
if ( selectedNibble = = 0 )
{
selectedNibble = 1 ;
} else {
selectedNibble = 0 ;
bytes = 0 ;
}
}
curAddress + = bytes ;
2013-06-29 10:59:42 -07:00
u32 windowEnd = windowStart + visibleRows * rowSize ;
2013-06-25 02:43:31 +02:00
if ( curAddress < windowStart )
{
windowStart = curAddress & ~ 15 ;
} else if ( curAddress > = windowEnd )
{
windowStart = ( curAddress - ( visibleRows - 1 ) * rowSize ) & ~ 15 ;
}
2013-07-30 16:19:05 +02:00
updateStatusBarText ( ) ;
2013-06-25 02:43:31 +02:00
redraw ( ) ;
2013-09-01 18:43:41 +02:00
}
2022-02-10 20:39:29 -08:00
bool CtrlMemView : : ParseSearchString ( const std : : string & query , bool asHex , std : : vector < uint8_t > & data ) {
data . clear ( ) ;
if ( ! asHex ) {
for ( size_t i = 0 ; i < query . length ( ) ; i + + ) {
data . push_back ( query [ i ] ) ;
}
return true ;
}
2020-05-24 18:18:48 +01:00
2022-02-10 20:39:29 -08:00
for ( size_t index = 0 ; index < query . size ( ) ; ) {
if ( isspace ( query [ index ] ) ) {
index + + ;
continue ;
}
u8 value = 0 ;
for ( int i = 0 ; i < 2 & & index < query . size ( ) ; i + + ) {
char c = tolower ( query [ index + + ] ) ;
if ( c > = ' a ' & & c < = ' f ' ) {
value | = ( c - ' a ' + 10 ) < < ( 1 - i ) * 4 ;
} else if ( c > = ' 0 ' & & c < = ' 9 ' ) {
value | = ( c - ' 0 ' ) < < ( 1 - i ) * 4 ;
} else {
return false ;
}
}
data . push_back ( value ) ;
}
return true ;
}
std : : vector < u32 > CtrlMemView : : searchString ( const std : : string & searchQuery ) {
2020-05-24 18:18:48 +01:00
std : : vector < u32 > searchResAddrs ;
auto memLock = Memory : : Lock ( ) ;
if ( ! PSP_IsInited ( ) )
return searchResAddrs ;
2020-06-06 14:29:39 +02:00
2022-02-10 20:39:29 -08:00
std : : vector < u8 > searchData ;
if ( ! ParseSearchString ( searchQuery , false , searchData ) )
2021-02-07 10:18:14 -08:00
return searchResAddrs ;
2022-02-10 20:39:29 -08:00
if ( searchData . empty ( ) )
return searchResAddrs ;
2020-05-24 18:18:48 +01:00
2022-02-10 20:39:29 -08:00
std : : vector < std : : pair < u32 , u32 > > memoryAreas ;
memoryAreas . push_back ( std : : pair < u32 , u32 > ( PSP_GetScratchpadMemoryBase ( ) , PSP_GetScratchpadMemoryEnd ( ) ) ) ;
// Ignore the video memory mirrors.
memoryAreas . push_back ( std : : pair < u32 , u32 > ( PSP_GetVidMemBase ( ) , 0x04200000 ) ) ;
memoryAreas . push_back ( std : : pair < u32 , u32 > ( PSP_GetKernelMemoryBase ( ) , PSP_GetUserMemoryEnd ( ) ) ) ;
2020-05-24 18:18:48 +01:00
2022-02-10 20:39:29 -08:00
for ( const auto & area : memoryAreas ) {
const u32 segmentStart = area . first ;
const u32 segmentEnd = area . second - ( u32 ) searchData . size ( ) ;
for ( u32 pos = segmentStart ; pos < segmentEnd ; pos + + ) {
if ( ( pos % 256 ) = = 0 & & KeyDownAsync ( VK_ESCAPE ) ) {
return searchResAddrs ;
}
u8 * ptr = Memory : : GetPointerUnchecked ( pos ) ;
if ( memcmp ( ptr , searchData . data ( ) , searchData . size ( ) ) = = 0 ) {
searchResAddrs . push_back ( pos ) ;
}
2020-05-24 18:18:48 +01:00
}
2022-02-10 20:39:29 -08:00
}
2020-06-06 14:29:39 +02:00
2020-05-24 18:18:48 +01:00
return searchResAddrs ;
} ;
2013-09-01 18:43:41 +02:00
void CtrlMemView : : search ( bool continueSearch )
{
2014-07-19 23:25:32 -07:00
auto memLock = Memory : : Lock ( ) ;
if ( ! PSP_IsInited ( ) )
return ;
2020-06-06 14:29:39 +02:00
u32 searchAddress = 0 ;
u32 segmentStart = 0 ;
u32 segmentEnd = 0 ;
u8 * dataPointer = 0 ;
if ( continueSearch = = false | | searchQuery . empty ( ) )
2013-09-01 18:43:41 +02:00
{
2021-04-21 19:47:18 -07:00
if ( InputBox_GetString ( GetModuleHandle ( NULL ) , wnd , L " Search for " , searchQuery , searchQuery ) = = false )
2013-09-01 18:43:41 +02:00
{
SetFocus ( wnd ) ;
return ;
}
SetFocus ( wnd ) ;
searchAddress = curAddress + 1 ;
} else {
searchAddress = matchAddress + 1 ;
}
std : : vector < u8 > searchData ;
2022-02-10 20:39:29 -08:00
if ( ! ParseSearchString ( searchQuery , ! asciiSelected , searchData ) ) {
MessageBox ( wnd , L " Invalid search text. " , L " Error " , MB_OK ) ;
return ;
2013-09-01 18:43:41 +02:00
}
2022-02-10 20:39:29 -08:00
std : : vector < std : : pair < u32 , u32 > > memoryAreas ;
// Ignore the video memory mirrors.
memoryAreas . push_back ( std : : pair < u32 , u32 > ( PSP_GetVidMemBase ( ) , 0x04200000 ) ) ;
memoryAreas . push_back ( std : : pair < u32 , u32 > ( PSP_GetKernelMemoryBase ( ) , PSP_GetUserMemoryEnd ( ) ) ) ;
memoryAreas . push_back ( std : : pair < u32 , u32 > ( PSP_GetScratchpadMemoryBase ( ) , PSP_GetScratchpadMemoryEnd ( ) ) ) ;
2013-09-01 18:43:41 +02:00
searching = true ;
redraw ( ) ; // so the cursor is disabled
2020-05-24 18:18:48 +01:00
2020-06-06 14:29:39 +02:00
for ( size_t i = 0 ; i < memoryAreas . size ( ) ; i + + ) {
2020-05-24 18:18:48 +01:00
segmentStart = memoryAreas [ i ] . first ;
segmentEnd = memoryAreas [ i ] . second ;
2020-06-06 14:29:39 +02:00
2020-05-24 18:18:48 +01:00
dataPointer = Memory : : GetPointer ( segmentStart ) ;
2013-09-01 18:43:41 +02:00
if ( dataPointer = = NULL ) continue ; // better safe than sorry, I guess
if ( searchAddress < segmentStart ) searchAddress = segmentStart ;
if ( searchAddress > = segmentEnd ) continue ;
int index = searchAddress - segmentStart ;
2020-06-06 14:29:39 +02:00
int endIndex = segmentEnd - segmentStart - ( int ) searchData . size ( ) ;
2013-09-01 18:43:41 +02:00
2020-06-06 14:29:39 +02:00
while ( index < endIndex ) {
2013-09-01 18:43:41 +02:00
// cancel search
2020-06-06 14:29:39 +02:00
if ( ( index % 256 ) = = 0 & & KeyDownAsync ( VK_ESCAPE ) ) {
2013-09-01 18:43:41 +02:00
searching = false ;
return ;
}
2020-06-06 14:29:39 +02:00
if ( memcmp ( & dataPointer [ index ] , searchData . data ( ) , searchData . size ( ) ) = = 0 ) {
2013-09-01 18:43:41 +02:00
matchAddress = index + segmentStart ;
searching = false ;
gotoAddr ( matchAddress ) ;
return ;
}
index + + ;
}
}
MessageBox ( wnd , L " Not found " , L " Search " , MB_OK ) ;
searching = false ;
redraw ( ) ;
2013-11-18 12:30:52 +02:00
}
2018-10-21 07:37:35 +01:00
void CtrlMemView : : drawOffsetScale ( HDC hdc )
{
int currentX = addressStart ;
SetTextColor ( hdc , 0x600000 ) ;
TextOutA ( hdc , currentX , offsetPositionY , " Offset " , 6 ) ;
currentX = addressStart + ( ( 8 + 1 ) * charWidth ) ; // the start offset, the size of the hex addresses and one space
char temp [ 64 ] ;
for ( int i = 0 ; i < 16 ; i + + )
{
sprintf ( temp , " %02X " , i ) ;
TextOutA ( hdc , currentX , offsetPositionY , temp , 2 ) ;
currentX + = 3 * charWidth ; // hex and space
}
}
2020-05-24 18:18:48 +01:00
void CtrlMemView : : toggleOffsetScale ( CommonToggles toggle )
2018-10-21 07:37:35 +01:00
{
if ( toggle = = On )
2018-10-22 03:17:45 +01:00
displayOffsetScale = true ;
2018-10-21 07:37:35 +01:00
else if ( toggle = = Off )
2018-10-22 03:17:45 +01:00
displayOffsetScale = false ;
2018-10-21 07:37:35 +01:00
updateStatusBarText ( ) ;
redraw ( ) ;
}
2021-02-07 10:49:22 -08:00
void CtrlMemView : : setHighlightType ( MemBlockFlags flags ) {
if ( highlightFlags_ ! = flags ) {
highlightFlags_ = flags ;
2021-04-03 16:03:21 -07:00
updateStatusBarText ( ) ;
2021-02-07 10:49:22 -08:00
redraw ( ) ;
}
}
2021-02-07 11:39:51 -08:00
uint32_t CtrlMemView : : pickTagColor ( const std : : string & tag ) {
2021-02-07 12:02:47 -08:00
int colors [ 6 ] = { 0xe0FFFF , 0xFFE0E0 , 0xE8E8FF , 0xFFE0FF , 0xE0FFE0 , 0xFFFFE0 } ;
int which = XXH3_64bits ( tag . c_str ( ) , tag . length ( ) ) % ARRAY_SIZE ( colors ) ;
return colors [ which ] ;
2021-02-07 11:39:51 -08:00
}