From 5c2aa06f7c1c989b2d14e262f37a97287893c464 Mon Sep 17 00:00:00 2001 From: shygoo Date: Sat, 30 Nov 2019 11:48:46 -0600 Subject: [PATCH] [Debugger] Rewrite memory viewer replace the list control with a custom hex editor control add ability to select/copy/paste/delete multiple bytes add byte group size option make window resizable fix DPI-related issues speed up auto-refresh remove option to disable auto-refresh (new control's cpu usage is insignificant) move the dump and search buttons into the context menu change colors of breakpoints to match the command window's highlight CPU read and write targets while stepping move address info into a status bar allow window to open before MMU is initialized add shortcut keys for various actions add "follow pointer" feature add "safe mode" feature add copy gameshark code feature add copy with row/group addresses feature add tabs feature add jump menu for hardware regions fix cartridge ROM addresses not being viewable allow writes to cartridge ROM use thread-safe functions for reading/writing memory --- Source/Project64-core/Settings.cpp | 1 - Source/Project64-core/Settings/SettingsID.h | 1 - Source/Project64/Project64.vcxproj | 4 + Source/Project64/Project64.vcxproj.filters | 12 + .../UserInterface/Debugger/DebugMMU.cpp | 304 +++ .../UserInterface/Debugger/DebugMMU.h | 15 + .../Debugger/Debugger-ViewMemory.cpp | 1749 ++++++++++------- .../Debugger/Debugger-ViewMemory.h | 314 ++- .../UserInterface/Debugger/Debugger.cpp | 190 -- .../UserInterface/Debugger/MemoryScanner.cpp | 6 +- .../UserInterface/Debugger/debugger.h | 8 +- Source/Project64/UserInterface/MainMenu.cpp | 1 - Source/Project64/UserInterface/UIResources.rc | 64 +- .../UserInterface/WTLControls/HexEditCtrl.cpp | 1747 ++++++++++++++++ .../UserInterface/WTLControls/HexEditCtrl.h | 309 +++ Source/Project64/UserInterface/resource.h | 33 +- 16 files changed, 3799 insertions(+), 959 deletions(-) create mode 100644 Source/Project64/UserInterface/Debugger/DebugMMU.cpp create mode 100644 Source/Project64/UserInterface/Debugger/DebugMMU.h create mode 100644 Source/Project64/UserInterface/WTLControls/HexEditCtrl.cpp create mode 100644 Source/Project64/UserInterface/WTLControls/HexEditCtrl.h diff --git a/Source/Project64-core/Settings.cpp b/Source/Project64-core/Settings.cpp index f487df6b7..abaedb785 100644 --- a/Source/Project64-core/Settings.cpp +++ b/Source/Project64-core/Settings.cpp @@ -350,7 +350,6 @@ void CSettings::AddHowToHandleSetting(const char * BaseDirectory) AddHandler(Debugger_WriteBPExists, new CSettingTypeTempBool(false)); AddHandler(Debugger_ReadBPExists, new CSettingTypeTempBool(false)); AddHandler(Debugger_WaitingForStep, new CSettingTypeTempBool(false)); - AddHandler(Debugger_AutoRefreshMemoryView, new CSettingTypeApplication("Debugger", "Auto Refresh Memory View", true)); AddHandler(Debugger_CPULoggingEnabled, new CSettingTypeApplication("Debugger", "Enable CPU Logging", false)); AddHandler(Debugger_CPULogBufferSize, new CSettingTypeApplication("Debugger", "CPU Log Buffer Size", (uint32_t)1024)); AddHandler(Debugger_ExceptionBreakpoints, new CSettingTypeApplication("Debugger", "Exception Breakpoints", (uint32_t)0)); diff --git a/Source/Project64-core/Settings/SettingsID.h b/Source/Project64-core/Settings/SettingsID.h index 0992da1a2..0ecdd7971 100644 --- a/Source/Project64-core/Settings/SettingsID.h +++ b/Source/Project64-core/Settings/SettingsID.h @@ -260,7 +260,6 @@ enum SettingID Debugger_WriteBPExists, Debugger_ReadBPExists, Debugger_WaitingForStep, - Debugger_AutoRefreshMemoryView, Debugger_CPULoggingEnabled, Debugger_CPULogBufferSize, Debugger_ExceptionBreakpoints, diff --git a/Source/Project64/Project64.vcxproj b/Source/Project64/Project64.vcxproj index 769c032f2..3794474f6 100644 --- a/Source/Project64/Project64.vcxproj +++ b/Source/Project64/Project64.vcxproj @@ -99,6 +99,7 @@ + @@ -131,6 +132,7 @@ + @@ -166,6 +168,7 @@ + @@ -201,6 +204,7 @@ + diff --git a/Source/Project64/Project64.vcxproj.filters b/Source/Project64/Project64.vcxproj.filters index 3c6f1726f..81e4cb585 100644 --- a/Source/Project64/Project64.vcxproj.filters +++ b/Source/Project64/Project64.vcxproj.filters @@ -237,6 +237,12 @@ Source Files\User Interface Source\Settings Source + + Source Files\User Interface Source\WTL Controls Source + + + Source Files\User Interface Source\Debugger Source + @@ -455,6 +461,12 @@ Header Files\User Interface Headers\Settings Header + + Header Files\User Interface Headers\WTL Controls Headers + + + Header Files\User Interface Headers\Debugger Headers + diff --git a/Source/Project64/UserInterface/Debugger/DebugMMU.cpp b/Source/Project64/UserInterface/Debugger/DebugMMU.cpp new file mode 100644 index 000000000..ee817e9b0 --- /dev/null +++ b/Source/Project64/UserInterface/Debugger/DebugMMU.cpp @@ -0,0 +1,304 @@ +#include + +#include "DebugMMU.h" +#include + +uint32_t* CDebugMMU::PAddrWordPtr(uint32_t paddr) +{ + if (g_MMU == NULL) + { + return NULL; + } + + paddr = paddr & ~3; + + // RDRAM & DMEM/IMEM + if ((paddr < g_MMU->RdramSize()) || + (paddr >= 0x04000000 && paddr <= 0x04001FFF)) + { + return (uint32_t*)(g_MMU->Rdram() + paddr); + } + + // 64DD buffer + if (paddr >= 0x05000000 && paddr <= 0x050004FF) + { + // todo + return NULL; + } + + // Cartridge Domain 1 (Address 1) (64DD IPL ROM) + if (paddr >= 0x06000000 && paddr <= 0x06FFFFFF) + { + uint32_t iplRomOffset = paddr - 0x06000000; + + if (g_DDRom != NULL && iplRomOffset < g_DDRom->GetRomSize()) + { + return (uint32_t*)(g_MMU->Rdram() + paddr); + } + return NULL; + } + + // Cartridge Domain 2 (Address 2) (SRAM/FlashRAM) + if (paddr >= 0x08000000 && paddr < 0x08FFFFFF) + { + // stored in a file + return NULL; + } + + // Cartridge ROM + if (paddr >= 0x10000000 && paddr <= 0x15FFFFFF) + { + uint32_t cartRomOffset = paddr - 0x10000000; + if (g_Rom != NULL && cartRomOffset < g_Rom->GetRomSize()) + { + return (uint32_t*)(g_Rom->GetRomAddress() + cartRomOffset); + } + return false; + } + + // PIF ROM + if (paddr >= 0x1FC00000 && paddr <= 0x1FC007BF) + { + return NULL; + } + + // PIF RAM + if (paddr >= 0x1FC007C0 && paddr <= 0x1FC007FF) + { + uint32_t pifRamOffset = paddr - 0x1FC007C0; + return (uint32_t*)(g_MMU->PifRam() + pifRamOffset); + } + + switch (paddr) + { + case 0x03F00000: return &g_Reg->RDRAM_CONFIG_REG; + case 0x03F00004: return &g_Reg->RDRAM_DEVICE_ID_REG; + case 0x03F00008: return &g_Reg->RDRAM_DELAY_REG; + case 0x03F0000C: return &g_Reg->RDRAM_MODE_REG; + case 0x03F00010: return &g_Reg->RDRAM_REF_INTERVAL_REG; + case 0x03F00014: return &g_Reg->RDRAM_REF_ROW_REG; + case 0x03F00018: return &g_Reg->RDRAM_RAS_INTERVAL_REG; + case 0x03F0001C: return &g_Reg->RDRAM_MIN_INTERVAL_REG; + case 0x03F00020: return &g_Reg->RDRAM_ADDR_SELECT_REG; + case 0x03F00024: return &g_Reg->RDRAM_DEVICE_MANUF_REG; + case 0x04040010: return &g_Reg->SP_STATUS_REG; + case 0x04040014: return &g_Reg->SP_DMA_FULL_REG; + case 0x04040018: return &g_Reg->SP_DMA_BUSY_REG; + case 0x0404001C: return &g_Reg->SP_SEMAPHORE_REG; + case 0x04080000: return &g_Reg->SP_PC_REG; + case 0x0410000C: return &g_Reg->DPC_STATUS_REG; + case 0x04100010: return &g_Reg->DPC_CLOCK_REG; + case 0x04100014: return &g_Reg->DPC_BUFBUSY_REG; + case 0x04100018: return &g_Reg->DPC_PIPEBUSY_REG; + case 0x0410001C: return &g_Reg->DPC_TMEM_REG; + case 0x04300000: return &g_Reg->MI_MODE_REG; + case 0x04300004: return &g_Reg->MI_VERSION_REG; + case 0x04300008: return &g_Reg->MI_INTR_REG; + case 0x0430000C: return &g_Reg->MI_INTR_MASK_REG; + case 0x04400000: return &g_Reg->VI_STATUS_REG; + case 0x04400004: return &g_Reg->VI_ORIGIN_REG; + case 0x04400008: return &g_Reg->VI_WIDTH_REG; + case 0x0440000C: return &g_Reg->VI_INTR_REG; + case 0x04400010: return &g_Reg->VI_V_CURRENT_LINE_REG; + case 0x04400014: return &g_Reg->VI_BURST_REG; + case 0x04400018: return &g_Reg->VI_V_SYNC_REG; + case 0x0440001C: return &g_Reg->VI_H_SYNC_REG; + case 0x04400020: return &g_Reg->VI_LEAP_REG; + case 0x04400024: return &g_Reg->VI_H_START_REG; + case 0x04400028: return &g_Reg->VI_V_START_REG; + case 0x0440002C: return &g_Reg->VI_V_BURST_REG; + case 0x04400030: return &g_Reg->VI_X_SCALE_REG; + case 0x04400034: return &g_Reg->VI_Y_SCALE_REG; + case 0x04600000: return &g_Reg->PI_DRAM_ADDR_REG; + case 0x04600004: return &g_Reg->PI_CART_ADDR_REG; + case 0x04600008: return &g_Reg->PI_RD_LEN_REG; + case 0x0460000C: return &g_Reg->PI_WR_LEN_REG; + case 0x04600010: return &g_Reg->PI_STATUS_REG; + case 0x04600014: return &g_Reg->PI_DOMAIN1_REG; + case 0x04600018: return &g_Reg->PI_BSD_DOM1_PWD_REG; + case 0x0460001C: return &g_Reg->PI_BSD_DOM1_PGS_REG; + case 0x04600020: return &g_Reg->PI_BSD_DOM1_RLS_REG; + case 0x04600024: return &g_Reg->PI_DOMAIN2_REG; + case 0x04600028: return &g_Reg->PI_BSD_DOM2_PWD_REG; + case 0x0460002C: return &g_Reg->PI_BSD_DOM2_PGS_REG; + case 0x04600030: return &g_Reg->PI_BSD_DOM2_RLS_REG; + case 0x04700000: return &g_Reg->RI_MODE_REG; + case 0x04700004: return &g_Reg->RI_CONFIG_REG; + case 0x04700008: return &g_Reg->RI_CURRENT_LOAD_REG; + case 0x0470000C: return &g_Reg->RI_SELECT_REG; + case 0x04700010: return &g_Reg->RI_REFRESH_REG; + case 0x04700014: return &g_Reg->RI_LATENCY_REG; + case 0x04700018: return &g_Reg->RI_RERROR_REG; + case 0x0470001C: return &g_Reg->RI_WERROR_REG; + case 0x04800018: return &g_Reg->SI_STATUS_REG; + case 0x05000500: return &g_Reg->ASIC_DATA; + case 0x05000504: return &g_Reg->ASIC_MISC_REG; + case 0x05000508: return &g_Reg->ASIC_STATUS; + case 0x0500050C: return &g_Reg->ASIC_CUR_TK; + case 0x05000510: return &g_Reg->ASIC_BM_STATUS; + case 0x05000514: return &g_Reg->ASIC_ERR_SECTOR; + case 0x05000518: return &g_Reg->ASIC_SEQ_STATUS; + case 0x0500051C: return &g_Reg->ASIC_CUR_SECTOR; + case 0x05000520: return &g_Reg->ASIC_HARD_RESET; + case 0x05000524: return &g_Reg->ASIC_C1_S0; + case 0x05000528: return &g_Reg->ASIC_HOST_SECBYTE; + case 0x0500052C: return &g_Reg->ASIC_C1_S2; + case 0x05000530: return &g_Reg->ASIC_SEC_BYTE; + case 0x05000534: return &g_Reg->ASIC_C1_S4; + case 0x05000538: return &g_Reg->ASIC_C1_S6; + case 0x0500053C: return &g_Reg->ASIC_CUR_ADDR; + case 0x05000540: return &g_Reg->ASIC_ID_REG; + case 0x05000544: return &g_Reg->ASIC_TEST_REG; + case 0x05000548: return &g_Reg->ASIC_TEST_PIN_SEL; + } + + return NULL; +} + +bool CDebugMMU::DebugLW_PAddr(uint32_t paddr, uint32_t& value) +{ + if (g_MMU == NULL) + { + return false; + } + + uint32_t* ptr = PAddrWordPtr(paddr); + + if (ptr != NULL) + { + value = *ptr; + return true; + } + + if (paddr >= 0x08000000 && paddr < 0x08FFFFFF) // Cartridge Domain 2 (Address 2) + { + uint32_t saveOffset = paddr & 0x000FFFFF; + + if (g_System->m_SaveUsing == SaveChip_Sram && saveOffset <= 0x7FFF) // sram + { + uint8_t tmp[4] = ""; + CSram *sram = g_MMU->GetSram(); + sram->DmaFromSram(tmp, paddr - 0x08000000, 4); + value = tmp[3] << 24 | tmp[2] << 16 | tmp[1] << 8 | tmp[0]; + return true; + } + else if (g_System->m_SaveUsing == SaveChip_FlashRam && saveOffset == 0) // flash ram status + { + CFlashram* flashRam = g_MMU->GetFlashram(); + value = flashRam->ReadFromFlashStatus(0x08000000); + return true; + } + } + + if (paddr == 0x04500004) + { + if (g_System->bFixedAudio()) + { + value = g_Audio->GetLength(); + } + else + { + CAudioPlugin* audioPlg = g_Plugins->Audio(); + value = (audioPlg->AiReadLength != NULL) ? audioPlg->AiReadLength() : 0; + } + return true; + } + + if (paddr == 0x0450000C) + { + value = g_System->bFixedAudio() ? g_Audio->GetStatus() : g_Reg->AI_STATUS_REG; + return true; + } + + return false; +} + +bool CDebugMMU::DebugLW_VAddr(uint32_t vaddr, uint32_t& value) +{ + if (vaddr <= 0x7FFFFFFF || vaddr >= 0xC0000000) // KUSEG, KSEG2 (TLB) + { + if (g_MMU == NULL) + { + return false; + } + + return g_MMU->LW_VAddr(vaddr, value); + } + + uint32_t paddr = vaddr & 0x1FFFFFFF; + return DebugLW_PAddr(paddr, value); +} + +bool CDebugMMU::DebugLB_PAddr(uint32_t vaddr, uint8_t& value) +{ + uint32_t word; + if (!DebugLW_PAddr(vaddr & ~3, word)) + { + return false; + } + value = (word >> (24 - (vaddr & 3) * 8)) & 0xFF; + return true; +} + +bool CDebugMMU::DebugLB_VAddr(uint32_t vaddr, uint8_t& value) +{ + uint32_t word; + if (!DebugLW_VAddr(vaddr & ~3, word)) + { + return false; + } + value = (word >> (24 - (vaddr & 3) * 8)) & 0xFF; + return true; +} + +bool CDebugMMU::DebugSB_PAddr(uint32_t paddr, uint8_t value) +{ + bool bWriteToRom = false; + + if (paddr >= 0x10000000 && paddr <= 0x1FBFFFFF) + { + uint32_t romOffset = paddr - 0x10000000; + if (romOffset > g_Rom->GetRomSize()) + { + return false; + } + bWriteToRom = true; + } + + int nbyte = 3 - (paddr & 3); + uint8_t* ptr = (uint8_t*)PAddrWordPtr(paddr & ~3); + + if (ptr == NULL) + { + return false; + } + + if (bWriteToRom) + { + ProtectMemory(g_Rom->GetRomAddress(), g_Rom->GetRomSize(), MEM_READWRITE); + } + + ptr[nbyte] = value; + + if (bWriteToRom) + { + ProtectMemory(g_Rom->GetRomAddress(), g_Rom->GetRomSize(), MEM_READONLY); + } + return true; +} + +bool CDebugMMU::DebugSB_VAddr(uint32_t vaddr, uint8_t value) +{ + if (vaddr <= 0x7FFFFFFF || vaddr >= 0xC0000000) // KUSEG, KSEG2 (TLB) + { + if (g_MMU == NULL) + { + return false; + } + + return g_MMU->SB_VAddr(vaddr, value); + } + + uint32_t paddr = vaddr & 0x1FFFFFFF; + return DebugSB_PAddr(paddr, value); +} diff --git a/Source/Project64/UserInterface/Debugger/DebugMMU.h b/Source/Project64/UserInterface/Debugger/DebugMMU.h new file mode 100644 index 000000000..857a32340 --- /dev/null +++ b/Source/Project64/UserInterface/Debugger/DebugMMU.h @@ -0,0 +1,15 @@ +#pragma once +#include + +class CDebugMMU +{ +private: + uint32_t* PAddrWordPtr(uint32_t paddr); +public: + bool DebugLW_PAddr(uint32_t paddr, uint32_t& value); + bool DebugLW_VAddr(uint32_t vaddr, uint32_t& value); + bool DebugLB_PAddr(uint32_t paddr, uint8_t& value); + bool DebugLB_VAddr(uint32_t vaddr, uint8_t& value); + bool DebugSB_PAddr(uint32_t paddr, uint8_t value); + bool DebugSB_VAddr(uint32_t vaddr, uint8_t value); +}; \ No newline at end of file diff --git a/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.cpp b/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.cpp index fc7a87ea2..dceabfc7d 100644 --- a/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.cpp +++ b/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.cpp @@ -12,23 +12,49 @@ #include #include +#include + #include "DebuggerUI.h" #include "Symbols.h" #include "DMALog.h" -CDebugMemoryView* CDebugMemoryView::_this = NULL; -HHOOK CDebugMemoryView::hWinMessageHook = NULL; +CDebugMemoryView::jump_item_t CDebugMemoryView::JumpItems[] = { + { 0x80000000, 0x00000000, 0x0800000, "RDRAM" }, + { 0xA3F00000, 0x03F00000, 0x0000028, "RDRAM Registers" }, + { 0xA4000000, 0x04000000, 0x0001000, "SP DMEM" }, + { 0xA4001000, 0x04001000, 0x0001000, "SP IMEM" }, + { 0xA4040000, 0x04040000, 0x0000020, "SP Registers" }, + { 0xA4080000, 0x04080000, 0x0000004, "SP PC Register" }, + { 0xA4100000, 0x04100000, 0x0000020, "DP Control Registers" }, + { 0xA4300000, 0x04300000, 0x0000010, "MI Registers" }, + { 0xA4400000, 0x04400000, 0x0000038, "VI Registers" }, + { 0xA4500000, 0x04500000, 0x0000018, "AI Registers" }, + { 0xA4600000, 0x04600000, 0x0000034, "PI Registers" }, + { 0xA4700000, 0x04700000, 0x0000020, "RI Registers" }, + { 0xA4800000, 0x04800000, 0x0000010, "SI Registers" }, + { 0xA5000500, 0x05000500, 0x000004C, "DD Registers" }, + { 0xA8000000, 0xA8000000, 0x0000000, "Cartridge Save Data" }, + { 0xB0000000, 0x10000000, 0xFC00000, "Cartridge ROM" }, + { 0xBFC00000, 0x1FC00000, 0x00007C0, "PIF ROM" }, + { 0xBFC007C0, 0x1FC007C0, 0x0000040, "PIF RAM" }, + { 0, NULL} +}; CDebugMemoryView::CDebugMemoryView(CDebuggerUI * debugger) : CDebugDialog(debugger), - m_MemoryList(NULL) + CDialogResize(), + CToolTipDialog(), + m_Breakpoints(NULL), + m_WriteTargetColorStride(0), + m_ReadTargetColorStride(0), + m_SymbolColorStride(0), + m_SymbolColorPhase(0), + m_bIgnoreAddressInput(false), + m_HotAddress(0), + m_bVirtualMemory(true), + m_ContextMenuAddress(0), + m_bSafeEditMode(false) { - if (m_MemoryList == NULL) - { - m_MemoryList = new CListCtrl; - m_MemoryList->RegisterClass(); - } - m_Breakpoints = m_Debugger->Breakpoints(); } @@ -36,16 +62,258 @@ CDebugMemoryView::~CDebugMemoryView() { } +void CDebugMemoryView::ShowAddress(uint32_t address, bool bVirtual) +{ + if (m_hWnd == NULL) + { + return; + } + + SendMessage(WM_SHOWADDRESS, (WPARAM)address, (LPARAM)bVirtual); +} + +bool CDebugMemoryView::GetByte(uint32_t address, uint8_t* value) +{ + if (m_bVirtualMemory) + { + return m_Debugger->DebugLB_VAddr(address, *value); + } + else + { + return m_Debugger->DebugLB_PAddr(address, *value); + } +} + +bool CDebugMemoryView::SetByte(uint32_t address, uint8_t value) +{ + if (m_bVirtualMemory) + { + return m_Debugger->DebugSB_VAddr(address, value); + } + else + { + return m_Debugger->DebugSB_PAddr(address, value); + } +} + +void CDebugMemoryView::CopyTextToClipboard(const char* text) +{ + size_t length = strlen(text); + HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, length + 1); + strcpy((char*)GlobalLock(hMem), text); + GlobalUnlock(hMem); + OpenClipboard(); + EmptyClipboard(); + SetClipboardData(CF_TEXT, hMem); + CloseClipboard(); +} + +void CDebugMemoryView::CopyBytesToClipboard(uint32_t startAddress, uint32_t endAddress, bool bHex, bool bIncludeAddresses, bool bRowAddresses) +{ + uint32_t baseAddress = m_HexEditCtrl.GetBaseAddress(); + int groupSize = m_HexEditCtrl.GetNumBytesPerGroup(); + int rowSize = m_HexEditCtrl.GetNumBytesPerRow(); + + stdstr str = ""; + + for (uint32_t address = startAddress; address <= endAddress; address++) + { + int offsetFromBase = address - baseAddress; + int offsetFromSelStart = address - startAddress; + + uint8_t value; + GetByte(address, &value); + + if (bIncludeAddresses) + { + if ((bRowAddresses && offsetFromBase % rowSize == 0) || + (!bRowAddresses && offsetFromBase % groupSize == 0) || + (offsetFromSelStart == 0)) + { + str += stdstr_f("%08X: ", address); + } + } + + if (bHex) + { + str += stdstr_f("%02X", value); + } + else + { + str += CHexEditCtrl::ByteAscii(value); + } + + if ((offsetFromBase + 1) % rowSize == 0 || (bIncludeAddresses && !bRowAddresses && (offsetFromBase + 1) % groupSize == 0)) + { + str += "\r\n"; + } + else if (bHex && (offsetFromBase + 1) % groupSize == 0) + { + str += " "; + } + } + + CopyTextToClipboard(str.Trim(" \r\n").c_str()); +} + +void CDebugMemoryView::CopyGameSharkCodeToClipboard(uint32_t startAddress, uint32_t endAddress) +{ + stdstr str = ""; + + if (startAddress & 1) + { + uint8_t value = 0; + GetByte(startAddress, &value); + str += stdstr_f("%08X %04X\r\n", startAddress, value); + startAddress++; + } + + for (uint32_t address = startAddress; address < endAddress; address += 2) + { + uint8_t value0 = 0, value1 = 0; + GetByte(address + 0, &value0); + GetByte(address + 1, &value1); + str += stdstr_f("%08X %02X%02X\r\n", address | 0x01000000, value0, value1); + } + + if (!(endAddress & 1)) + { + uint8_t value = 0; + GetByte(endAddress, &value); + str += stdstr_f("%08X %04X\r\n", endAddress, value); + } + + CopyTextToClipboard(str.Trim("\n").c_str()); +} + +void CDebugMemoryView::FillRange(uint32_t startAddress, uint32_t endAddress, uint8_t value) +{ + for (uint32_t address = startAddress; address <= endAddress; address++) + { + SetByte(address, value); + } +} + +void CDebugMemoryView::FollowPointer(bool bContextMenuAddress) +{ + uint32_t address; + + if (bContextMenuAddress) + { + address = m_ContextMenuAddress - (m_ContextMenuAddress % 4); + } + else + { + address = m_HexEditCtrl.GetCaretAddress(); + address -= (address % 4); + } + + address += (m_bVirtualMemory ? 0 : 0x80000000); + + uint32_t pointer; + if (m_Debugger->DebugLW_VAddr(address, pointer)) + { + OpenNewTab(pointer, m_bVirtualMemory, 4, true, true); + } +} + + +void CDebugMemoryView::JumpToSelection(void) +{ + uint32_t startAddress, endAddress; + bool bHaveSelection = m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress); + uint32_t targetAddress = bHaveSelection ? startAddress : m_HexEditCtrl.GetCaretAddress(); + m_MemAddr.SetValue(targetAddress, false, true); +} + +bool CDebugMemoryView::GetSafeEditValue(uint32_t address, uint8_t* value) +{ + if (m_SafeEditQueue.size() == 0) + { + return false; + } + + for(size_t i = m_SafeEditQueue.size(); i-- > 0;) + { + edit_t edit = m_SafeEditQueue[i]; + if (address >= edit.startAddress && address <= edit.endAddress) + { + *value = edit.value; + return true; + } + } + return false; +} + +void CDebugMemoryView::ApplySafeEdits(void) +{ + for (size_t i = 0; i < m_SafeEditQueue.size(); i++) + { + edit_t edit = m_SafeEditQueue[i]; + if (edit.type == SE_FILL) + { + FillRange(edit.startAddress, edit.endAddress, edit.value); + } + } + + m_SafeEditQueue.clear(); +} + +void CDebugMemoryView::SetupJumpMenu(bool bVirtual) +{ + m_CmbJump.SetRedraw(FALSE); + m_CmbJump.ResetContent(); + + for (int i = 0;; i++) + { + jump_item_t* item = &JumpItems[i]; + + if (item->caption == NULL) + { + break; + } + + m_CmbJump.AddString(stdstr_f("%08X %s", bVirtual ? item->vaddr : item->paddr, item->caption).c_str()); + } + + m_CmbJump.SetRedraw(TRUE); +} + +int CDebugMemoryView::GetJumpItemIndex(uint32_t address, bool bVirtual) +{ + for (int nItem = 0;; nItem++) + { + if (JumpItems[nItem].caption == NULL) + { + break; + } + + uint32_t start = bVirtual ? JumpItems[nItem].vaddr : JumpItems[nItem].paddr; + uint32_t end = start + JumpItems[nItem].size - 1; + + if (address >= start && address <= end) + { + return nItem; + } + } + return -1; +} + LRESULT CDebugMemoryView::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { + DlgResize_Init(false, true); DlgSavePos_Init(DebuggerUI_MemoryPos); + DlgToolTip_Init(); + + m_HexEditCtrl.Attach(GetDlgItem(IDC_HEXEDIT)); + m_TabCtrl.Attach(GetDlgItem(IDC_MEMTABS)); + m_MemAddr.Attach(GetDlgItem(IDC_ADDR_EDIT)); + m_VirtualCheckbox.Attach(GetDlgItem(IDC_CHK_VADDR)); + m_StatusBar.Attach(GetDlgItem(IDC_STATUSBAR)); + m_CmbJump.Attach(GetDlgItem(IDC_CMB_JUMP)); m_SymbolColorStride = 0; m_SymbolColorPhase = 0; - m_DataStartLoc = (DWORD)-1; - m_CompareStartLoc = (DWORD)-1; - memset(m_CompareData, 0, sizeof(m_CompareData)); - memset(m_CompareValid, 0, sizeof(m_CompareValid)); HWND hScrlBar = GetDlgItem(IDC_SCRL_BAR); if (hScrlBar) @@ -61,227 +329,574 @@ LRESULT CDebugMemoryView::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM ::SetScrollInfo(hScrlBar, SB_CTL, &si, TRUE); } - m_MemAddr.Attach(GetDlgItem(IDC_ADDR_EDIT)); m_MemAddr.SetDisplayType(CEditNumber32::DisplayHex); m_MemAddr.SetValue(0x80000000, false, true); - SendDlgItemMessage(IDC_CHK_VADDR, BM_SETCHECK, BST_CHECKED, 0); + m_VirtualCheckbox.SetCheck(BST_CHECKED); - if (m_MemoryList == NULL) - { - m_MemoryList = new CListCtrl; - m_MemoryList->RegisterClass(); - } - m_MemoryList->SubclassWindow(GetDlgItem(IDC_MEM_DETAILS)); - m_MemoryList->ShowHeader(false); - m_MemoryList->SetSortEnabled(FALSE); - m_MemoryList->AddColumn(_T("Address"), 90); - m_MemoryList->AddColumn(_T("1"), 20); - m_MemoryList->AddColumn(_T("2"), 20); - m_MemoryList->AddColumn(_T("3"), 20); - m_MemoryList->AddColumn(_T("4"), 20); - m_MemoryList->AddColumn(_T("-"), 10); - m_MemoryList->AddColumn(_T("5"), 20); - m_MemoryList->AddColumn(_T("6"), 20); - m_MemoryList->AddColumn(_T("7"), 20); - m_MemoryList->AddColumn(_T("8"), 20); - m_MemoryList->AddColumn(_T("-"), 10); - m_MemoryList->AddColumn(_T("9"), 20); - m_MemoryList->AddColumn(_T("10"), 20); - m_MemoryList->AddColumn(_T("11"), 20); - m_MemoryList->AddColumn(_T("12"), 20); - m_MemoryList->AddColumn(_T("-"), 10); - m_MemoryList->AddColumn(_T("13"), 20); - m_MemoryList->AddColumn(_T("14"), 20); - m_MemoryList->AddColumn(_T("15"), 20); - m_MemoryList->AddColumn(_T("16"), 35); - m_MemoryList->AddColumn(_T("Memory Ascii"), 140); - ::SetWindowLongPtr(m_MemoryList->m_hWnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE); - RefreshMemory(false); - int height = m_MemoryList->GetTotalHeight(); + float dpiScale = CClientDC(m_hWnd).GetDeviceCaps(LOGPIXELSX) / 96.0f; - RECT MemoryListRect = { 0 }; - ::GetClientRect(GetDlgItem(IDC_MEM_DETAILS), &MemoryListRect); + int statusPaneWidths[MEMSB_NUM_PANES] = { + (int)(MEMSB_HOTADDR_W * dpiScale), + (int)(MEMSB_BLOCK_W * dpiScale), + (int)(MEMSB_BLOCKLEN_W * dpiScale), + (int)(MEMSB_DMAINFO_W * dpiScale), + (int)(MEMSB_SAFEMODE_W * dpiScale) + }; - if (height > MemoryListRect.bottom) - { - RECT MemoryListWindow = { 0 }; - GetWindowRect(&MemoryListWindow); - SetWindowPos(NULL, 0, 0, MemoryListWindow.right - MemoryListWindow.left, (MemoryListWindow.bottom - MemoryListWindow.top) + (height - MemoryListRect.bottom), SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOZORDER); + m_StatusBar.SetParts(MEMSB_NUM_PANES, statusPaneWidths); - RECT DlgItemRect = { 0 }; + SetupJumpMenu(true); + m_CmbJump.SetCurSel(0); + + AddTab(0x80000000, true, 4); - ::GetWindowRect(GetDlgItem(IDC_MEM_DETAILS), &DlgItemRect); - ::SetWindowPos(GetDlgItem(IDC_MEM_DETAILS), NULL, 0, 0, DlgItemRect.right - DlgItemRect.left, (DlgItemRect.bottom - DlgItemRect.top) + (height - MemoryListRect.bottom), SWP_NOMOVE); - - ::GetWindowRect(GetDlgItem(IDC_SCRL_BAR), &DlgItemRect); - ::SetWindowPos(GetDlgItem(IDC_SCRL_BAR), NULL, 0, 0, DlgItemRect.right - DlgItemRect.left, (DlgItemRect.bottom - DlgItemRect.top) + (height - MemoryListRect.bottom), SWP_NOMOVE); - } - - m_SymInfo.Attach(GetDlgItem(IDC_SYM_INFO)); - m_DMAInfo.Attach(GetDlgItem(IDC_DMA_INFO)); - - m_bAutoRefreshEnabled = g_Settings->LoadBool(Debugger_AutoRefreshMemoryView); - SendDlgItemMessage(IDC_CHK_AUTOREFRESH, BM_SETCHECK, m_bAutoRefreshEnabled ? BST_CHECKED : BST_UNCHECKED, 0); - - _this = this; - - DWORD dwThreadID = ::GetCurrentThreadId(); - hWinMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)HookProc, NULL, dwThreadID); + m_HexEditCtrl.Draw(); LoadWindowPos(); WindowCreated(); - m_AutoRefreshThread = CreateThread(NULL, 0, AutoRefreshProc, (void*)this, 0, NULL); - return TRUE; } -DWORD WINAPI CDebugMemoryView::AutoRefreshProc(void* _self) -{ - CDebugMemoryView* self = (CDebugMemoryView*)_self; - while (true) - { - if (self->m_bAutoRefreshEnabled) - { - self->RefreshMemory(true); - } - Sleep(100); - } -} - -void CDebugMemoryView::OnExitSizeMove() -{ - SaveWindowPos(0); -} - -void CDebugMemoryView::InterceptMouseWheel(WPARAM wParam, LPARAM /*lParam*/) -{ - uint32_t newAddress = m_DataStartLoc - ((short)HIWORD(wParam) / WHEEL_DELTA) * 16; - - m_DataStartLoc = newAddress; - - m_MemAddr.SetValue(m_DataStartLoc, false, true); -} - -LRESULT CALLBACK CDebugMemoryView::HookProc(int nCode, WPARAM wParam, LPARAM lParam) -{ - MSG *pMsg = (MSG*)lParam; - - if (pMsg->message == WM_MOUSEWHEEL) - { - _this->InterceptMouseWheel(pMsg->wParam, pMsg->lParam); - } - - if (nCode < 0) - { - return CallNextHookEx(hWinMessageHook, nCode, wParam, lParam); - } - - return 0; -} - LRESULT CDebugMemoryView::OnDestroy(void) { - if (m_AutoRefreshThread != NULL) - { - TerminateThread(m_AutoRefreshThread, 0); - CloseHandle(m_AutoRefreshThread); - } - if (m_MemoryList) - { - m_MemoryList->UnsubclassWindow(); - delete m_MemoryList; - m_MemoryList = NULL; - } + m_HexEditCtrl.Detach(); m_MemAddr.Detach(); - m_SymInfo.Detach(); - m_DMAInfo.Detach(); - UnhookWindowsHookEx(hWinMessageHook); + m_VirtualCheckbox.Detach(); + m_TabCtrl.Detach(); + m_StatusBar.Detach(); + m_CmbJump.Detach(); return 0; } +LRESULT CDebugMemoryView::OnShowAddress(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) +{ + uint32_t address = (uint32_t)wParam; + bool bVirtual = (lParam != 0); + m_CmbJump.SetCurSel(GetJumpItemIndex(address, bVirtual)); + OpenNewTab(address, bVirtual, 4, true, true); + return FALSE; +} + +void CDebugMemoryView::OnExitSizeMove() +{ + SaveWindowPos(true); +} + LRESULT CDebugMemoryView::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND, BOOL& /*bHandled*/) { switch (wID) { - case IDC_REFRSH_MEM: - RefreshMemory(true); - break; case IDC_CHK_VADDR: - RefreshMemory(false); - break; - case IDC_DUMP_MEM: - m_Debugger->OpenMemoryDump(); - break; - case IDC_SEARCH_MEM: - m_Debugger->OpenMemorySearch(); + m_bVirtualMemory = (m_VirtualCheckbox.GetCheck() == BST_CHECKED); + SetupJumpMenu(m_bVirtualMemory); + UpdateCurrentTab(m_MemAddr.GetValue()); break; case IDC_SYMBOLS_BTN: m_Debugger->OpenSymbolsWindow(); break; - case IDC_CHK_AUTOREFRESH: - m_bAutoRefreshEnabled = (SendMessage(GetDlgItem(IDC_CHK_AUTOREFRESH), BM_GETSTATE, 0, 0) & BST_CHECKED) != 0; - g_Settings->SaveBool(Debugger_AutoRefreshMemoryView, m_bAutoRefreshEnabled); - break; case IDCANCEL: EndDialog(0); break; case ID_POPUPMENU_TOGGLERBP: - m_Breakpoints->RBPToggle(m_CtxMenuAddr); - RefreshMemory(true); + m_Breakpoints->RBPToggle(m_ContextMenuAddress); break; case ID_POPUPMENU_TOGGLEWBP: - m_Breakpoints->WBPToggle(m_CtxMenuAddr); - RefreshMemory(true); + m_Breakpoints->WBPToggle(m_ContextMenuAddress); break; case ID_POPUPMENU_CLEARALLBPS: m_Breakpoints->RBPClear(); m_Breakpoints->WBPClear(); - RefreshMemory(true); break; case ID_POPUPMENU_TOGGLELOCK: - m_Breakpoints->ToggleMemLock(m_CtxMenuAddr); - RefreshMemory(true); + m_Breakpoints->ToggleMemLock(m_ContextMenuAddress); break; case ID_POPUPMENU_CLEARLOCKS: m_Breakpoints->ClearMemLocks(); - RefreshMemory(true); + break; + case ID_POPUPMENU_JUMPHERE: + JumpToSelection(); + break; + case ID_POPUPMENU_FOLLOWPOINTER: + FollowPointer(); break; case ID_POPUPMENU_VIEWDISASM: - m_Debugger->Debug_ShowCommandsLocation(m_CtxMenuAddr, true); + m_Debugger->Debug_ShowCommandsLocation(m_ContextMenuAddress, true); break; case ID_POPUPMENU_ADDSYMBOL: - m_AddSymbolDlg.DoModal(m_Debugger, m_CtxMenuAddr); + m_AddSymbolDlg.DoModal(m_Debugger, m_ContextMenuAddress); break; - case ID_POPUPMENU_COPY_WORD: - CopyNumber(m_CtxMenuAddr, sizeof(uint32_t)); + case ID_POPUPMENU_COPY: + m_HexEditCtrl.Copy(); break; - case ID_POPUPMENU_COPY_HALFWORD: - CopyNumber(m_CtxMenuAddr, sizeof(uint16_t)); + case ID_POPUPMENU_COPYGAMESHARKCODE: + { + uint32_t startAddress, endAddress; + m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress); + CopyGameSharkCodeToClipboard(startAddress, endAddress); + } break; - case ID_POPUPMENU_COPY_BYTE: - CopyNumber(m_CtxMenuAddr, sizeof(uint8_t)); + case ID_POPUPMENU_COPYDATAWITHGROUPADDRESSES: + { + uint32_t startAddress, endAddress; + m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress); + CopyBytesToClipboard(startAddress, endAddress, m_HexEditCtrl.GetFocusedColumn() == HX_COL_HEXDATA, true, false); + } + break; + case ID_POPUPMENU_COPYDATAWITHROWADDRESSES: + { + uint32_t startAddress, endAddress; + m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress); + CopyBytesToClipboard(startAddress, endAddress, m_HexEditCtrl.GetFocusedColumn() == HX_COL_HEXDATA, true, true); + } + break; + case ID_POPUPMENU_PASTE: + m_HexEditCtrl.Paste(); + break; + case ID_POPUPMENU_SAFEMODE: + m_HexEditCtrl.SendMessage(WM_KEYDOWN, VK_INSERT, 0); + break; + case ID_POPUPMENU_ZEROFILL: + m_HexEditCtrl.SendMessage(WM_KEYDOWN, VK_DELETE, 0); + break; + case ID_POPUPMENU_BYTEGROUPSIZE_1: + m_HexEditCtrl.SetByteGroupSize(1); + break; + case ID_POPUPMENU_BYTEGROUPSIZE_2: + m_HexEditCtrl.SetByteGroupSize(2); + break; + case ID_POPUPMENU_BYTEGROUPSIZE_4: + m_HexEditCtrl.SetByteGroupSize(4); + break; + case ID_POPUPMENU_BYTEGROUPSIZE_8: + m_HexEditCtrl.SetByteGroupSize(8); + break; + case ID_POPUPMENU_DUMP: + m_Debugger->OpenMemoryDump(); + break; + case ID_POPUPMENU_SEARCH: + m_Debugger->OpenMemorySearch(); break; } return FALSE; } -LRESULT CDebugMemoryView::OnMemoryRightClicked(LPNMHDR lpNMHDR) +void CDebugMemoryView::OnAddrChanged(UINT /*Code*/, int /*id*/, HWND /*ctl*/) { - uint32_t address; - bool bData = GetItemAddress(lpNMHDR, address); - - if (!bData) + if (m_bIgnoreAddressInput) { - return 0; + m_bIgnoreAddressInput = false; + return; } - m_CtxMenuAddr = address; + uint32_t address = m_MemAddr.GetValue(); + m_HexEditCtrl.SetBaseAddress(address); + UpdateCurrentTab(address); +} + +void CDebugMemoryView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar) +{ + if (pScrollBar != GetDlgItem(IDC_SCRL_BAR)) + { + return; + } + + uint32_t address = m_MemAddr.GetValue(); + int numBytesPerRow = m_HexEditCtrl.GetNumBytesPerRow(); + int numVisibleBytes = m_HexEditCtrl.GetNumVisibleBytes(); + + switch (nSBCode) + { + case SB_LINEDOWN: + m_MemAddr.SetValue(address < 0xFFFFFFEF ? address + numBytesPerRow : 0xFFFFFFFF, false, true); + break; + case SB_LINEUP: + m_MemAddr.SetValue(address > (uint32_t)numBytesPerRow ? address - numBytesPerRow : 0, false, true); + break; + case SB_PAGEDOWN: + m_MemAddr.SetValue(address < 0xFFFFFEFF ? address + numVisibleBytes : 0xFFFFFFFF, false, true); + break; + case SB_PAGEUP: + m_MemAddr.SetValue(address >(uint32_t)numVisibleBytes ? address - numVisibleBytes : 0, false, true); + break; + case SB_THUMBPOSITION: + m_MemAddr.SetValue((DWORD)nPos << 0x10, false, true); + break; + default: + break; + } + + m_CmbJump.SetCurSel(GetJumpItemIndex(address, m_bVirtualMemory)); +} + +LRESULT CDebugMemoryView::OnHxCtrlKeyPressed(LPNMHDR lpNMHDR) +{ + NMHXCTRLKEYPRESSED* nmck = reinterpret_cast(lpNMHDR); + uint32_t address = m_HexEditCtrl.GetCaretAddress(); + + if (nmck->nChar >= '1' && nmck->nChar <= '9') + { + int nBytes = nmck->nChar - '0'; + m_HexEditCtrl.SetByteGroupSize(nBytes); + return FALSE; + } + + switch (nmck->nChar) + { + case 'G': + { + JumpToSelection(); + } + break; + case 'W': + m_Breakpoints->WBPToggle(address); + break; + case 'R': + m_Breakpoints->RBPToggle(address); + break; + case 'E': + m_Breakpoints->ToggleMemLock(address); + break; + case 'Q': + m_Breakpoints->WBPClear(); + m_Breakpoints->RBPClear(); + m_Breakpoints->ClearMemLocks(); + break; + case 'F': + // todo put selection in the textbox + m_Debugger->OpenMemorySearch(); + break; + case 'S': + // todo set start and end addrs to selection + m_Debugger->OpenMemoryDump(); + break; + case 'T': + OpenDuplicateTab(); + break; + case 'Z': + if (m_SafeEditQueue.size() != 0) + { + m_SafeEditQueue.pop_back(); + } + break; + case VK_F4: + CloseCurrentTab(); + break; + case VK_SPACE: + FollowPointer(false); + break; + case VK_TAB: + { + int curSel = m_TabCtrl.GetCurSel(); + if (m_TabCtrl.SetCurSel(curSel + 1) == -1) + { + m_TabCtrl.SetCurSel(0); + } + TabSelChanged(); + } + break; + } + + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxSetNibble(LPNMHDR lpNMHDR) +{ + if (g_MMU == NULL) + { + return FALSE; + } + + NMHXSETNIBBLE* nmsn = reinterpret_cast(lpNMHDR); + + uint8_t curValue; + bool bValid = GetByte(nmsn->address, &curValue); + + if (!bValid) + { + return false; + } + + uint8_t mask = (nmsn->bLoNibble ? 0xF0 : 0x0F); + uint8_t newValue = (curValue & mask) | (nmsn->value << (nmsn->bLoNibble ? 0 : 4)); + + if (nmsn->bInsert) + { + if (GetSafeEditValue(nmsn->address, &curValue)) + { + newValue = (curValue & mask) | (nmsn->value << (nmsn->bLoNibble ? 0 : 4)); + } + + m_SafeEditQueue.push_back({ SE_FILL, nmsn->address, nmsn->address, newValue }); + } + else + { + SetByte(nmsn->address, newValue); + } + + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxSetByte(LPNMHDR lpNMHDR) +{ + NMHXSETBYTE* nmsb = reinterpret_cast(lpNMHDR); + + if (g_MMU == NULL) + { + return FALSE; + } + + if (nmsb->bInsert) + { + m_SafeEditQueue.push_back({ SE_FILL, nmsb->address, nmsb->address, nmsb->value }); + } + else + { + SetByte(nmsb->address, nmsb->value); + } + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxFillRange(LPNMHDR lpNMHDR) +{ + NMHXFILLRANGE* nmfr = reinterpret_cast(lpNMHDR); + + if (nmfr->bInsert) + { + m_SafeEditQueue.push_back({ SE_FILL, nmfr->startAddress, nmfr->endAddress, nmfr->value }); + return FALSE; + } + + FillRange(nmfr->startAddress, nmfr->endAddress, nmfr->value); + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxInsertModeChanged(LPNMHDR /*lpNMHDR*/) +{ + m_SafeEditQueue.clear(); + m_bSafeEditMode = m_HexEditCtrl.GetInsertMode(); + m_StatusBar.SetText(MEMSB_SAFEMODE, m_bSafeEditMode ? "Safe mode" : ""); + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxSelectionChanged(LPNMHDR /*lpNMHDR*/) +{ + uint32_t startAddress, endAddress; + bool bHaveSelection = m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress); + + stdstr strBlock, strLength; + + if (bHaveSelection) + { + strBlock = stdstr_f("%08X:%08X", startAddress, endAddress); + strLength = stdstr_f("%X", endAddress - startAddress + 1); + m_StatusBar.SetText(MEMSB_BLOCK, strBlock.c_str()); + m_StatusBar.SetText(MEMSB_BLOCKLEN, strLength.c_str()); + } + else + { + strBlock = stdstr_f("%08X", startAddress); + m_StatusBar.SetText(MEMSB_BLOCK, strBlock.c_str()); + m_StatusBar.SetText(MEMSB_BLOCKLEN, ""); + } + + uint32_t romAddr, offset; + DMALOGENTRY* entry = m_Debugger->DMALog()->GetEntryByRamAddress(startAddress, &romAddr, &offset); + m_StatusBar.SetText(MEMSB_DMAINFO, entry != NULL ? "Have DMA" : ""); + + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxGroupSizeChanged(LPNMHDR /*lpNMHDR*/) +{ + int groupSize = m_HexEditCtrl.GetNumBytesPerGroup(); + + int nItem = m_TabCtrl.GetCurSel(); + + if (nItem == -1) + { + return FALSE; + } + + m_TabData[nItem].numBytesPerGroup = groupSize; + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxEnterPressed(LPNMHDR /*lpNMHDR*/) +{ + ApplySafeEdits(); + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxRedrawStarted(LPNMHDR /*lpNMHDR*/) +{ + m_SymbolColorPhase = 0; + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxGetByteInfo(LPNMHDR lpNMHDR) +{ + NMHXGETBYTEINFO *nmgbi = reinterpret_cast(lpNMHDR); + + bool bHaveWriteTarget = false, bHaveReadTarget = false; + uint32_t cpuReadWriteAddress = 0; + int cpuReadWriteNumBytes = 0; + + if (g_Settings->LoadBool(Debugger_SteppingOps)) + { + COpInfo opInfo(R4300iOp::m_Opcode); + if (opInfo.IsStoreCommand()) + { + cpuReadWriteAddress = opInfo.GetLoadStoreAddress(); + cpuReadWriteNumBytes = opInfo.NumBytesToStore(); + bHaveWriteTarget = true; + } + else if (opInfo.IsLoadCommand()) + { + cpuReadWriteAddress = opInfo.GetLoadStoreAddress(); + cpuReadWriteNumBytes = opInfo.NumBytesToLoad(); + bHaveReadTarget = true; + } + } + + for (uint32_t i = 0; i < nmgbi->numBytes; i++) + { + uint32_t address = nmgbi->address + i; + uint32_t paddress = address; + HXBYTEINFO* oldByte = &nmgbi->oldBytes[i]; + HXBYTEINFO* newByte = &nmgbi->newBytes[i]; + + newByte->bkColor = BKCOLOR_DEFAULT; + newByte->color = COLOR_DEFAULT; + + if (m_bVirtualMemory && (g_MMU == NULL || !g_MMU->TranslateVaddr(address, paddress))) + { + newByte->bValid = false; + continue; + } + + newByte->bValid = GetByte(address, &newByte->value); + + if (!newByte->bValid) + { + continue; + } + + // always use virtual addresses for breakpoint & symbol info + // todo should be the other way around + uint32_t vaddress = m_bVirtualMemory ? address : address + 0x80000000; + + CSymbols::EnterCriticalSection(); + CSymbolEntry* symbol = CSymbols::GetEntryByAddress(vaddress); + + if (symbol != NULL) + { + m_SymbolColorStride = symbol->TypeSize(); + m_SymbolColorPhase = m_SymbolColorPhase ? 0 : 1; + } + + CSymbols::LeaveCriticalSection(); + + if (bHaveWriteTarget && address == cpuReadWriteAddress) + { + m_WriteTargetColorStride = cpuReadWriteNumBytes; + } + else if (bHaveReadTarget && address == cpuReadWriteAddress) + { + m_ReadTargetColorStride = cpuReadWriteNumBytes; + } + + bool bLocked = m_Breakpoints->MemLockExists(vaddress, 1); + bool bReadBP = m_Breakpoints->ReadBPExists8(vaddress) == CBreakpoints::BP_SET; + bool bWriteBP = m_Breakpoints->WriteBPExists8(vaddress) == CBreakpoints::BP_SET; + + if (bLocked) + { + newByte->bkColor = BKCOLOR_LOCKED; + newByte->color = COLOR_BP; + } + else if (bReadBP && bWriteBP) + { + newByte->bkColor = BKCOLOR_RWBP; + newByte->color = COLOR_BP; + } + else if (bReadBP) + { + newByte->bkColor = BKCOLOR_RBP; + newByte->color = COLOR_BP; + } + else if (bWriteBP) + { + newByte->bkColor = BKCOLOR_WBP; + newByte->color = COLOR_BP; + } + else if (m_ReadTargetColorStride > 0) + { + newByte->bkColor = BKCOLOR_CPUREAD; + } + else if (m_WriteTargetColorStride > 0) + { + newByte->bkColor = BKCOLOR_CPUWRITE; + } + else if (m_SymbolColorStride > 0) + { + newByte->bkColor = m_SymbolColorPhase ? BKCOLOR_SYMBOL0 : BKCOLOR_SYMBOL1; + } + + if (g_Rom != NULL && paddress >= 0x10000000 && paddress < 0x10000000 + g_Rom->GetRomSize()) + { + newByte->color = COLOR_READONLY; + } + + if (!nmgbi->bIgnoreDiff && oldByte->value != newByte->value) + { + newByte->color = COLOR_CHANGED; + } + + if (m_SymbolColorStride > 0) + { + m_SymbolColorStride--; + } + + if (m_ReadTargetColorStride > 0) + { + m_ReadTargetColorStride--; + } + + if (m_WriteTargetColorStride > 0) + { + m_WriteTargetColorStride--; + } + + uint8_t safeEditValue; + if (GetSafeEditValue(address, &safeEditValue)) + { + newByte->bValid = true; + newByte->value = safeEditValue; + newByte->bkColor = RGB(0xFF, 0xCC, 0xFF); + newByte->color = RGB(0xFF, 0x00, 0xFF); + } + + newByte->bHidden = false; + } + + return FALSE; +} + +LRESULT CDebugMemoryView::OnHxRightClick(LPNMHDR lpNMHDR) +{ + NMHXRCLICK *nmrc = reinterpret_cast(lpNMHDR); + + m_ContextMenuAddress = nmrc->address; HMENU hMenu = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MEM_BP_POPUP)); HMENU hPopupMenu = GetSubMenu(hMenu, 0); + bool bHaveLock = m_Breakpoints->MemLockExists(m_ContextMenuAddress, 1); + bool bHaveReadBP = (m_Breakpoints->ReadBPExists8(m_ContextMenuAddress) != CBreakpoints::BPSTATE::BP_NOT_SET); + bool bHaveWriteBP = (m_Breakpoints->WriteBPExists8(m_ContextMenuAddress) != CBreakpoints::BPSTATE::BP_NOT_SET); + if (m_Breakpoints->ReadMem().size() == 0 && m_Breakpoints->WriteMem().size() == 0) { EnableMenuItem(hPopupMenu, ID_POPUPMENU_CLEARALLBPS, MF_DISABLED | MF_GRAYED); @@ -291,539 +906,349 @@ LRESULT CDebugMemoryView::OnMemoryRightClicked(LPNMHDR lpNMHDR) EnableMenuItem(hPopupMenu, ID_POPUPMENU_CLEARLOCKS, MF_DISABLED | MF_GRAYED); } + CheckMenuItem(hPopupMenu, ID_POPUPMENU_TOGGLELOCK, bHaveLock ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hPopupMenu, ID_POPUPMENU_TOGGLERBP, bHaveReadBP ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hPopupMenu, ID_POPUPMENU_TOGGLEWBP, bHaveWriteBP ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hPopupMenu, ID_POPUPMENU_SAFEMODE, m_bSafeEditMode ? MF_CHECKED : MF_UNCHECKED); + POINT mouse; GetCursorPos(&mouse); - TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN, mouse.x, mouse.y, 0, m_hWnd, NULL); - DestroyMenu(hMenu); - - return 0; + return FALSE; } -LRESULT CDebugMemoryView::OnHotItemChanged(LPNMHDR lpNMHDR) +LRESULT CDebugMemoryView::OnHxHotAddrChanged(LPNMHDR /*lpNMHDR*/) { - uint32_t address; - bool bData = GetItemAddress(lpNMHDR, address); - - if (!bData) - { - return 0; - } + m_HotAddress = m_HexEditCtrl.GetHotAddress(); + stdstr strAddrInfo = ""; CSymbols::EnterCriticalSection(); + + CSymbolEntry* foundSymbol = NULL; + int numSymbols = CSymbols::GetCount(); - CSymbolEntry* lpSymbol = CSymbols::GetEntryByAddress(address); - - stdstr symbolInfo; - - if (lpSymbol != NULL) + for (int i = 0; i < numSymbols; i++) { - char* desc = lpSymbol->m_Description; - desc = desc ? desc : ""; - symbolInfo = stdstr_f("%08X: %s %s // %s", address, lpSymbol->TypeName(), lpSymbol->m_Name, desc); + CSymbolEntry* symbol = CSymbols::GetEntryByIndex(i); + if (m_HotAddress >= symbol->m_Address && m_HotAddress < symbol->m_Address + symbol->TypeSize()) + { + foundSymbol = symbol; + break; + } + } + + if (foundSymbol != NULL) + { + strAddrInfo += stdstr_f("%08X %s %s", foundSymbol->m_Address, foundSymbol->TypeName(), foundSymbol->m_Name); } else { - symbolInfo = stdstr_f("%08X", address); + strAddrInfo += stdstr_f("%08X\n", m_HotAddress); } + m_StatusBar.SetText(MEMSB_HOTADDR, strAddrInfo.c_str()); + CSymbols::LeaveCriticalSection(); - - m_SymInfo.SetWindowTextA(symbolInfo.c_str()); - - uint32_t romAddr, offset; - DMALOGENTRY* lpEntry = m_Debugger->DMALog()->GetEntryByRamAddress(address, &romAddr, &offset); - - stdstr dmaInfo; - - if (lpEntry != NULL) - { - dmaInfo = stdstr_f("Last DMA: %08X -> %08X [%X] (%08X, +%X) ", lpEntry->romAddr, lpEntry->ramAddr, lpEntry->length, romAddr, offset); - } - else - { - dmaInfo = stdstr_f("Last DMA: ?"); - } - - m_DMAInfo.SetWindowTextA(dmaInfo.c_str()); - - return 0; + return FALSE; } -LRESULT CDebugMemoryView::OnMemoryModified(LPNMHDR lpNMHDR) +LRESULT CDebugMemoryView::OnHxBaseAddrChanged(LPNMHDR /*lpNMHDR*/) { - uint32_t Pos = 0; - bool bData = GetItemOffset(lpNMHDR, Pos); - - if (!bData) - { - return 0; - } - - CListNotify *pListNotify = reinterpret_cast(lpNMHDR); - int LineNumber = pListNotify->m_nItem; - - LPCSTR strValue = m_MemoryList->GetItemText(pListNotify->m_nItem, pListNotify->m_nSubItem); - unsigned long long Value = strtoull(strValue, NULL, 16); - - if (m_CurrentData[Pos] == Value) - { - return 0; - } - - if (m_CompareStartLoc != m_DataStartLoc || - m_CompareVAddrr != m_DataVAddrr) - { - // copy current data for change comparison - m_CompareStartLoc = m_DataStartLoc; - m_CompareVAddrr = m_DataVAddrr; - memcpy(m_CompareData, m_CurrentData, sizeof(m_CurrentData)); - memcpy(m_CompareValid, m_DataValid, sizeof(m_CompareValid)); - } - - m_CompareData[Pos] = m_CurrentData[Pos]; - m_CurrentData[Pos] = (BYTE)Value; - - //sb - __except_try() - { - if (m_DataVAddrr) - { - if (!g_MMU->SB_VAddr(m_DataStartLoc + Pos, (BYTE)Value)) - { - WriteTrace(TraceUserInterface, TraceError, "failed to store at %X", m_DataStartLoc + Pos); - } - } - else - { - if (!g_MMU->SB_PAddr(m_DataStartLoc + Pos, (BYTE)Value)) - { - WriteTrace(TraceUserInterface, TraceError, "failed to store at %X", m_DataStartLoc + Pos); - } - } - uint32_t PhysicalAddress = m_DataStartLoc + Pos; - if (!m_DataVAddrr || g_MMU->TranslateVaddr(PhysicalAddress, PhysicalAddress)) - { - if (PhysicalAddress > 0x10000000 && (PhysicalAddress - 0x10000000) < g_Rom->GetRomSize()) - { - uint8_t * ROM = g_Settings->LoadBool(Game_LoadRomToMemory) ? g_MMU->Rdram() + 0x10000000: g_Rom->GetRomAddress(); - ProtectMemory(ROM, g_Rom->GetRomSize(), MEM_READWRITE); - ROM[(PhysicalAddress - 0x10000000) ^ 3] = (uint8_t)Value; - ProtectMemory(ROM, g_Rom->GetRomSize(), MEM_READONLY); - } - } - if (g_Recompiler != NULL) - { - g_Recompiler->ClearRecompCode_Phys(PhysicalAddress & ~0xFFF, 0x1000, CRecompiler::Remove_MemViewer); - } - } - __except_catch() - { - g_Notify->FatalError(GS(MSG_UNKNOWN_MEM_ACTION)); - } - Insert_MemoryLineDump(LineNumber); - - return 0; + // address was updated from the control + uint32_t address = m_HexEditCtrl.GetBaseAddress(); + m_bIgnoreAddressInput = true; + m_MemAddr.SetValue(address, false, true); + m_CmbJump.SetCurSel(GetJumpItemIndex(address, m_bVirtualMemory)); + UpdateCurrentTab(address); + return FALSE; } -void CDebugMemoryView::OnAddrChanged(UINT /*Code*/, int /*id*/, HWND /*ctl*/) +LRESULT CDebugMemoryView::OnHxCopy(LPNMHDR /*lpNMHDR*/) { - RefreshMemory(false); + HXCOLUMN column = m_HexEditCtrl.GetFocusedColumn(); + uint32_t startAddress, endAddress; + m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress); + CopyBytesToClipboard(startAddress, endAddress, column == HX_COL_HEXDATA); + return FALSE; } -void CDebugMemoryView::OnVScroll(int request, short Pos, HWND ctrl) +LRESULT CDebugMemoryView::OnHxPaste(LPNMHDR lpNMHDR) { - if (ctrl != GetDlgItem(IDC_SCRL_BAR)) - { - return; - } - DWORD Location = m_MemAddr.GetValue(); - switch (request) - { - case SB_LINEDOWN: - m_MemAddr.SetValue(Location < 0xFFFFFFEF ? Location + 0x10 : 0xFFFFFFFF, false, true); - break; - case SB_LINEUP: - m_MemAddr.SetValue(Location > 0x10 ? Location - 0x10 : 0, false, true); - break; - case SB_PAGEDOWN: - m_MemAddr.SetValue(Location < 0xFFFFFEFF ? Location + 0x100 : 0xFFFFFFFF, false, true); - break; - case SB_PAGEUP: - m_MemAddr.SetValue(Location > 0x100 ? Location - 0x100 : 0, false, true); - break; - case SB_THUMBPOSITION: - m_MemAddr.SetValue((DWORD)Pos << 0x10, false, true); - break; - default: - break; - } -} + NMHXPASTE *nmp = reinterpret_cast(lpNMHDR); -LRESULT CDebugMemoryView::OnActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) -{ - WORD type = LOWORD(wParam); - - if (type == WA_INACTIVE) + if (g_MMU == NULL) { return FALSE; } - RefreshMemory(false); + OpenClipboard(); + HANDLE hData = GetClipboardData(CF_TEXT); + char* text = (char*)GlobalLock(hData); + int retDataLength = 0; + + if (nmp->column == HX_COL_HEXDATA) + { + char* data = NULL; + // todo move this function to some utility class + int length = CMemoryScanner::ParseHexString(NULL, text); + + if (length != 0) + { + data = (char*)malloc(length); + CMemoryScanner::ParseHexString(data, text); + + for (int i = 0; i < length; i++) + { + SetByte(nmp->address + i, data[i]); + } + + free(data); + } + + retDataLength = length; + } + else if (nmp->column == HX_COL_ASCII) + { + size_t length = strlen(text); + for (size_t i = 0; i < length; i++) + { + SetByte(nmp->address + i, text[i]); + } + + retDataLength = length; + } + + GlobalUnlock(hData); + CloseClipboard(); + + return retDataLength; +} + + +LRESULT CDebugMemoryView::OnTabSelChange(LPNMHDR /*lpNMHDR*/) +{ + TabSelChanged(); + return FALSE; +} + +void CDebugMemoryView::TabSelChanged(void) +{ + TCITEM item = { 0 }; + item.mask = TCIF_PARAM; + + int nItem = m_TabCtrl.GetCurSel(); + + if (m_TabCtrl.GetItem(nItem, &item)) + { + tab_info_t tabInfo = m_TabData[nItem]; + uint32_t address = tabInfo.address; + + m_MemAddr.SetValue(address, false, true); + + if (m_bVirtualMemory != tabInfo.bVirtual) + { + m_bVirtualMemory = tabInfo.bVirtual; + m_VirtualCheckbox.SetCheck(m_bVirtualMemory ? BST_CHECKED : BST_UNCHECKED); + SetupJumpMenu(m_bVirtualMemory); + } + + m_CmbJump.SetCurSel(GetJumpItemIndex(address, m_bVirtualMemory)); + m_HexEditCtrl.SetByteGroupSize(tabInfo.numBytesPerGroup); + } +} + +int CDebugMemoryView::AddTab(uint32_t address, bool bVirtual, int numBytesPerGroup) +{ + char szAddress[12]; + sprintf(szAddress, "%08X", address); + m_TabData.push_back({ address, bVirtual, numBytesPerGroup }); + return m_TabCtrl.AddItem(TCIF_TEXT | TCIF_PARAM, szAddress, 0, (LPARAM)address); +} + +int CDebugMemoryView::InsertTab(int nItem, uint32_t address, bool bVirtual, int numBytesPerGroup) +{ + m_TabData.insert(m_TabData.begin() + nItem + 1, { address, bVirtual, numBytesPerGroup }); + m_TabCtrl.SetRedraw(FALSE); + m_TabCtrl.DeleteAllItems(); + for (size_t i = 0; i < m_TabData.size(); i++) + { + m_TabCtrl.AddItem(TCIF_TEXT, stdstr_f("%08X", m_TabData[i].address).c_str(), 0, 0); + } + m_TabCtrl.SetRedraw(TRUE); + return nItem + 1; +} + +void CDebugMemoryView::DeleteTab(int nItem) +{ + m_TabData.erase(m_TabData.begin() + nItem); + m_TabCtrl.DeleteItem(nItem); +} + +void CDebugMemoryView::UpdateCurrentTab(uint32_t address) +{ + char szAddress[12]; + sprintf(szAddress, "%08X", address); + + int nItem = m_TabCtrl.GetCurSel(); + + if (nItem == -1) + { + return; + } + + TCITEM item = { 0 }; + item.mask = TCIF_TEXT; + item.pszText = szAddress; + + m_TabCtrl.SetRedraw(FALSE); + m_TabCtrl.SetItem(nItem, &item); + m_TabData[nItem].address = address; + m_TabData[nItem].bVirtual = m_bVirtualMemory; + m_TabCtrl.SetRedraw(TRUE); +} + +void CDebugMemoryView::OpenNewTab(uint32_t address, bool bVirtual, int numBytesPerGroup, bool bInsert, bool bOpenExisting) +{ + int nItem; + + if (bOpenExisting) + { + for (size_t i = 0; i < m_TabData.size(); i++) + { + if (m_TabData[i].address == address && m_TabData[i].bVirtual == bVirtual) + { + m_TabCtrl.SetCurSel(i); + TabSelChanged(); + return; + } + } + } + + if (bInsert) + { + int nCurSelItem = m_TabCtrl.GetCurSel(); + nItem = InsertTab(nCurSelItem, address, bVirtual, numBytesPerGroup); + } + else + { + nItem = AddTab(address, bVirtual, numBytesPerGroup); + } + + m_TabCtrl.SetCurSel(nItem); + TabSelChanged(); +} + +void CDebugMemoryView::OpenDuplicateTab(void) +{ + int nItem = m_TabCtrl.GetCurSel(); + tab_info_t tabInfo = m_TabData[nItem]; + int nItemNew = InsertTab(nItem, tabInfo.address, tabInfo.bVirtual, tabInfo.numBytesPerGroup); + m_TabCtrl.SetCurSel(nItemNew); + TabSelChanged(); +} + +void CDebugMemoryView::CloseTab(int nItem) +{ + int itemCount = m_TabCtrl.GetItemCount(); + int nSelItem = m_TabCtrl.GetCurSel(); + + if (itemCount < 2 || nItem == -1) + { + return; + } + + if (nItem == nSelItem) + { + if (nItem == m_TabCtrl.GetItemCount() - 1) + { + // last tab + m_TabCtrl.SetCurSel(nItem - 1); + } + else if (nItem == nSelItem) + { + m_TabCtrl.SetCurSel(nItem + 1); + } + + TabSelChanged(); + } + + DeleteTab(nItem); +} + +void CDebugMemoryView::CloseCurrentTab(void) +{ + CloseTab(m_TabCtrl.GetCurSel()); +} + +LRESULT CDebugMemoryView::OnTabDblClick(LPNMHDR /*lpNMHDR*/) +{ + OpenDuplicateTab(); + return FALSE; +} + +LRESULT CDebugMemoryView::OnTabRClick(LPNMHDR lpNMHDR) +{ + NMMTRCLICK *nmrc = reinterpret_cast(lpNMHDR); + CloseTab(nmrc->nItem); + return FALSE; +} + +LRESULT CDebugMemoryView::OnStatusBarClick(LPNMHDR lpNMHDR) +{ + NMMOUSE *nmm = reinterpret_cast(lpNMHDR); + + uint32_t startAddress, endAddress; + bool bHaveSelection = m_HexEditCtrl.GetSelectionRange(&startAddress, &endAddress); + + if (nmm->dwItemSpec == MEMSB_DMAINFO) + { + uint32_t romAddress, blockOffset; + DMALOGENTRY* entry = m_Debugger->DMALog()->GetEntryByRamAddress(startAddress, &romAddress, &blockOffset); + + if (entry == NULL) + { + return FALSE; + } + + stdstr strDmaTitle = stdstr_f("DMA Information for 0x%08X", startAddress); + stdstr strDmaInfo = stdstr_f("Block:\nROM 0x%08X -> RAM 0x%08X ( 0x%X bytes )\n\nROM address of byte:\n0x%08X ( 0x%08X + 0x%08X )", + entry->romAddr, entry->ramAddr, entry->length, romAddress, entry->romAddr, blockOffset); + MessageBox(strDmaInfo.c_str(), strDmaTitle.c_str(), MB_OK); + } + else if (nmm->dwItemSpec == MEMSB_BLOCK) + { + stdstr strAddrRange; + + if (bHaveSelection) + { + strAddrRange = stdstr_f("%08X:%08X", startAddress, endAddress); + } + else + { + strAddrRange = stdstr_f("%08X", startAddress); + } + + CopyTextToClipboard(strAddrRange.c_str()); + } return FALSE; } -void CDebugMemoryView::ShowAddress(DWORD Address, bool VAddr) +void CDebugMemoryView::OnJumpComboSelChange(UINT /*uNotifyCode*/, int /*nID*/, CWindow /*wndCtl*/) { - if (m_hWnd == NULL) - { - return; - } + int nItem = m_CmbJump.GetCurSel(); + uint32_t address; - SendDlgItemMessage(IDC_CHK_VADDR, BM_SETCHECK, VAddr ? BST_CHECKED : BST_UNCHECKED, 0); - m_MemAddr.SetValue(Address, false, true); - RefreshMemory(true); -} - -void CDebugMemoryView::Insert_MemoryLineDump(int LineNumber) -{ - if (m_MemoryList == NULL || m_MemoryList->GetColumnCount() == 0) + if (m_bVirtualMemory) { - return; - } - char Output[20], Hex[60], Ascii[20], AsciiAddOn[15]; - sprintf(Output, "0x%08X", m_DataStartLoc + (LineNumber << 4)); - if (m_MemoryList->GetItemCount() <= LineNumber) - { - HFONT hFont = (HFONT)GetStockObject(ANSI_FIXED_FONT); - m_MemoryList->AddItemAt(LineNumber, Output); - for (int i = 0; i < m_MemoryList->GetColumnCount(); i++) - { - m_MemoryList->SetItemFont(LineNumber, i, hFont); - if (i == 5 || i == 10 || i == 15) - { - m_MemoryList->SetItemText(LineNumber, i, "-"); - } - } + address = JumpItems[nItem].vaddr; } else { - if (strcmp(Output, m_MemoryList->GetItemText(LineNumber, 0)) != 0) - { - m_MemoryList->SetItemText(LineNumber, 0, Output); - } - } - - Hex[0] = 0; - Ascii[0] = 0; - int CompareStartPos = m_DataStartLoc - m_CompareStartLoc; - - for (int i = 0, col = 1; i < 0x10; i++, col++) - { - int Pos = ((LineNumber << 4) + i); - if (m_DataValid[Pos]) - { - int ComparePos = CompareStartPos + Pos; - bool Changed = false; - - if (ComparePos >= 0 && ComparePos < MemoryToDisplay && - m_DataVAddrr == m_CompareVAddrr && - m_DataValid[ComparePos] && - m_CurrentData[Pos] != m_CompareData[ComparePos]) - { - Changed = true; - } - - sprintf(Hex, "%02X", m_CurrentData[Pos]); - - m_MemoryList->SetItemText(LineNumber, col, Hex); - m_MemoryList->SetItemFormat(LineNumber, col, ITEM_FORMAT_EDIT, ITEM_FLAGS_EDIT_HEX); - m_MemoryList->SetItemMaxEditLen(LineNumber, col, 2); - - uint32_t vaddr = 0x80000000 | (m_DataStartLoc + Pos); - - COLORREF bgColor, fgColor, fgHiColor; - SelectColors(vaddr, Changed, bgColor, fgColor, fgHiColor); - - m_MemoryList->SetItemColours(LineNumber, col, bgColor, fgColor); - m_MemoryList->SetItemHighlightColours(LineNumber, col, fgHiColor); - - if (m_CurrentData[Pos] < 30) - { - strcat(Ascii, "."); - } - else - { - sprintf(AsciiAddOn, "%c", m_CurrentData[Pos]); - strcat(Ascii, AsciiAddOn); - } - } - else - { - m_MemoryList->SetItemText(LineNumber, col, "**"); - m_MemoryList->SetItemFormat(LineNumber, col, ITEM_FORMAT_NONE, ITEM_FLAGS_NONE); - m_MemoryList->SetItemColours(LineNumber, col, GetSysColor(COLOR_WINDOW), GetSysColor(COLOR_WINDOWTEXT)); - strcat(Ascii, "*"); - } - if (i != 0xF) - { - if ((i & 3) == 3) - { - col += 1; - } - } - } - - if (strcmp(Ascii, m_MemoryList->GetItemText(LineNumber, 20)) != 0) - { - m_MemoryList->SetItemText(LineNumber, 20, Ascii); + address = JumpItems[nItem].paddr; } -} - -void CDebugMemoryView::RefreshMemory(bool ResetCompare) -{ - m_SymbolColorPhase = 0; - - if (g_MMU == NULL) - { - return; - } - - if (m_MemoryList && m_MemoryList->GetHasEditItem()) - { - m_MemoryList->SetFocus(); - } - - DWORD NewAddress = m_MemAddr.GetValue(); - if (NewAddress != m_DataStartLoc) - { - HWND hScrlBar = GetDlgItem(IDC_SCRL_BAR); - if (hScrlBar) - { - SCROLLINFO si; - - si.cbSize = sizeof(si); - si.fMask = SIF_POS; - si.nPos = NewAddress >> 0x10; - ::SetScrollInfo(hScrlBar, SB_CTL, &si, TRUE); - } - } - - if (ResetCompare) - { - // copy current data for change comparison - m_CompareStartLoc = m_DataStartLoc; - m_CompareVAddrr = m_DataVAddrr; - memcpy(m_CompareData, m_CurrentData, sizeof(m_CurrentData)); - memcpy(m_CompareValid, m_DataValid, sizeof(m_CompareValid)); - } - - m_DataStartLoc = m_MemAddr.GetValue(); - if (m_DataStartLoc > 0xFFFFFF00) { m_DataStartLoc = 0xFFFFFF00; } - int WritePos = 0; - - m_DataVAddrr = (SendDlgItemMessage(IDC_CHK_VADDR, BM_GETCHECK, 0, 0) & BST_CHECKED) != 0; - - if ((m_DataStartLoc & 3) != 0) - { - MIPS_WORD word; - bool ValidData = true; - - if (m_DataVAddrr) - { - if (!m_Debugger->DebugLW_VAddr(m_DataStartLoc & ~3, word.UW)) - { - ValidData = false; - } - } - else - { - if (!m_Debugger->DebugLW_PAddr(m_DataStartLoc & ~3, word.UW)) - { - ValidData = false; - } - } - - int Offset = (m_DataStartLoc & 3); - for (int i = 0; i < (4 - Offset); i++) - { - if (WritePos >= MemoryToDisplay) - { - break; - } - m_DataValid[WritePos + i] = ValidData; - if (ValidData) - { - m_CurrentData[WritePos + i] = word.UB[3 - (i + Offset)]; - } - } - WritePos = 4 - Offset; - } - - for (DWORD Pos = ((m_DataStartLoc + 3) & ~3); Pos < (m_DataStartLoc + MemoryToDisplay); WritePos += 4, Pos += 4) - { - MIPS_WORD word; - bool ValidData = true; - - if (m_DataVAddrr) - { - if (!m_Debugger->DebugLW_VAddr(Pos, word.UW)) - { - ValidData = false; - } - } - else - { - if (!m_Debugger->DebugLW_PAddr(Pos, word.UW)) - { - ValidData = false; - } - } - - for (int i = 0; i < 4; i++) - { - if ((WritePos + i) >= MemoryToDisplay) - { - break; - } - m_DataValid[WritePos + i] = ValidData; - if (ValidData) - { - m_CurrentData[WritePos + i] = word.UB[3 - i]; - } - } - } - - for (int count = 0; count < 16; count++) - { - Insert_MemoryLineDump(count); - } -} - -bool CDebugMemoryView::GetItemOffset(LPNMHDR lpNMHDR, uint32_t &offset) -{ - CListNotify *pListNotify = reinterpret_cast(lpNMHDR); - - int nRow = pListNotify->m_nItem; - int nCol = pListNotify->m_nSubItem - 1; - - if (nCol < 0 || (nCol % 5) == 4) - { - return false; - } - - offset = (nRow * 0x10) + (nCol / 5) * 4 + (nCol % 5); - - return true; -} - -bool CDebugMemoryView::GetItemAddress(LPNMHDR lpNMHDR, uint32_t &address) -{ - uint32_t offset; - bool bData = GetItemOffset(lpNMHDR, offset); - - if (!bData) - { - return false; - } - - address = 0x80000000 | (m_DataStartLoc + offset); - - return true; -} - -void CDebugMemoryView::SelectColors(uint32_t vaddr, bool changed, COLORREF& bgColor, COLORREF& fgColor, COLORREF& fgHiColor) -{ - CSymbols::EnterCriticalSection(); - CSymbolEntry* lpSymbol = CSymbols::GetEntryByAddress(vaddr); - - if (lpSymbol != NULL) - { - m_SymbolColorStride = lpSymbol->TypeSize(); - m_SymbolColorPhase = m_SymbolColorPhase ? 0 : 1; - } - - CSymbols::LeaveCriticalSection(); - - bool bLocked = m_Breakpoints->MemLockExists(vaddr, 1); - bool bHaveReadBP = m_Breakpoints->ReadBPExists8(vaddr) == CBreakpoints::BP_SET; - bool bHaveWriteBP = m_Breakpoints->WriteBPExists8(vaddr) == CBreakpoints::BP_SET; - - fgHiColor = RGB(0x00, 0x00, 0x00); - - if (bLocked) - { - bgColor = RGB(0xDD, 0xAA, 0xAA); - } - else if (bHaveReadBP && bHaveWriteBP) - { - bgColor = RGB(0xAA, 0xDD, 0xDD); - } - else if (bHaveReadBP) - { - bgColor = RGB(0xDD, 0xDD, 0xAA); - } - else if (bHaveWriteBP) - { - bgColor = RGB(0xAA, 0xAA, 0xDD); - } - else if (m_SymbolColorStride > 0) - { - bgColor = m_SymbolColorPhase ? RGB(0xD0, 0xF0, 0xD0) : RGB(0xAA, 0xCC, 0xAA); - } - else - { - bgColor = GetSysColor(COLOR_WINDOW); - fgHiColor = (changed ? RGB(255, 0, 0) : GetSysColor(COLOR_HIGHLIGHTTEXT)); - fgColor = (changed ? RGB(255, 0, 0) : GetSysColor(COLOR_WINDOWTEXT)); - } - - if (m_SymbolColorStride > 0) - { - m_SymbolColorStride--; - } -} - -void CDebugMemoryView::CopyNumber(uint32_t address, int numBytes) -{ - stdstr str; - - uint32_t u32; - uint16_t u16; - uint8_t u8; - - switch (numBytes) - { - case 4: - address &= ~3; - g_MMU->LW_VAddr(address, u32); - str = stdstr_f("%08X", u32); - break; - case 2: - address &= ~1; - g_MMU->LH_VAddr(address, u16); - str = stdstr_f("%04X", u16); - break; - case 1: - g_MMU->LB_VAddr(address, u8); - str = stdstr_f("%02X", u8); - break; - default: - return; - } - - if (str.length() == 0) - { - return; - } - - HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, str.length() + 1); - str.copy((char*)GlobalLock(hMem), str.length() + 1); - GlobalUnlock(hMem); - OpenClipboard(); - EmptyClipboard(); - SetClipboardData(CF_TEXT, hMem); - CloseClipboard(); + + m_MemAddr.SetValue(address, false, true); + m_HexEditCtrl.SetFocus(); } diff --git a/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.h b/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.h index 3d0b26f01..36e59e88b 100644 --- a/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.h +++ b/Source/Project64/UserInterface/Debugger/Debugger-ViewMemory.h @@ -11,9 +11,78 @@ #pragma once #include "Debugger-AddSymbol.h" +#include +#include + +typedef struct +{ + NMHDR nmh; + int nItem; +} NMMTRCLICK; + +enum +{ + MTCN_RCLICK +}; + +class CMemTabCtrl : + public CWindowImpl +{ +private: + int m_nItemRClick; + +public: + CMemTabCtrl() : + m_nItemRClick(-1) + { + } + + BOOL Attach(HWND hWndNew) + { + return SubclassWindow(hWndNew); + } + +private: + void OnLButtonDblClk(UINT /*nFlags*/, CPoint /*point*/) + { + NMHDR nmh = { m_hWnd, (UINT_PTR)::GetDlgCtrlID(m_hWnd), NM_DBLCLK }; + ::SendMessage(::GetParent(m_hWnd), WM_NOTIFY, NM_DBLCLK, (LPARAM)&nmh); + } + + void OnRButtonDown(UINT /*nFlags*/, CPoint point) + { + TCHITTESTINFO ht = { point, 0}; + int nItem = ::SendMessage(m_hWnd, TCM_HITTEST, 0, (LPARAM)&ht); + if (nItem != -1) + { + m_nItemRClick = nItem; + } + } + + void OnRButtonUp(UINT /*nFlags*/, CPoint point) + { + TCHITTESTINFO ht = { point, 0 }; + int nItem = ::SendMessage(m_hWnd, TCM_HITTEST, 0, (LPARAM)&ht); + if (nItem != -1 && nItem == m_nItemRClick) + { + NMMTRCLICK nmrc = { { m_hWnd, (UINT_PTR)::GetDlgCtrlID(m_hWnd), MTCN_RCLICK }, nItem }; + ::SendMessage(::GetParent(m_hWnd), WM_NOTIFY, MTCN_RCLICK, (LPARAM)&nmrc); + } + m_nItemRClick = -1; + } + + BEGIN_MSG_MAP_EX(CMemTabCtrl) + MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk) + MSG_WM_RBUTTONDOWN(OnRButtonDown) + MSG_WM_RBUTTONUP(OnRButtonUp) + MSG_WM_RBUTTONDBLCLK(OnRButtonDown) + END_MSG_MAP() +}; class CDebugMemoryView : - public CDebugDialog < CDebugMemoryView > + public CDebugDialog, + public CDialogResize, + public CToolTipDialog { public: enum { IDD = IDD_Debugger_Memory }; @@ -21,74 +90,201 @@ public: CDebugMemoryView(CDebuggerUI * debugger); virtual ~CDebugMemoryView(void); - void ShowAddress(DWORD Address, bool VAddr); + void ShowAddress(uint32_t address, bool bVirtual); private: + enum + { + WM_SHOWADDRESS = WM_USER + 1 + }; + + enum + { + COLOR_DEFAULT = RGB(0, 0, 0), + COLOR_READONLY = RGB(0, 100, 0), + BKCOLOR_DEFAULT = RGB(255, 255, 255), + COLOR_CHANGED = RGB(255, 0, 0), + BKCOLOR_HOT = RGB(240, 240, 240), + BKCOLOR_LOCKED = RGB(100, 100, 0), + COLOR_BP = RGB(255, 255, 255), + BKCOLOR_RWBP = RGB(100, 0, 100), + BKCOLOR_RBP = RGB(0, 0, 100), + BKCOLOR_WBP = RGB(100, 0, 0), + BKCOLOR_CPUREAD = RGB(200, 200, 255), + BKCOLOR_CPUWRITE = RGB(255, 200, 200), + BKCOLOR_SYMBOL0 = RGB(208, 240, 208), + BKCOLOR_SYMBOL1 = RGB(176, 208, 176), + BKCOLOR_SAFEEDIT = RGB(255, 230, 255), + COLOR_SAFEEDIT = RGB(255, 0, 255) + }; + + enum + { + MEMSB_HOTADDR, + MEMSB_BLOCK, + MEMSB_BLOCKLEN, + MEMSB_DMAINFO, + MEMSB_SAFEMODE, + MEMSB_NUM_PANES + }; + + enum + { + MEMSB_HOTADDR_W = 160, + MEMSB_BLOCK_W = 120 + MEMSB_HOTADDR_W, + MEMSB_BLOCKLEN_W = 60 + MEMSB_BLOCK_W, + MEMSB_DMAINFO_W = 60 + MEMSB_BLOCKLEN_W, + MEMSB_SAFEMODE_W = -1 + }; + + enum edit_type_t + { + SE_FILL, + //SE_DATA + }; + + typedef struct + { + edit_type_t type; + uint32_t startAddress; + uint32_t endAddress; + uint8_t value; + //uint8_t* data; + } edit_t; + + typedef struct + { + uint32_t vaddr; + uint32_t paddr; + uint32_t size; + const char* caption; + } jump_item_t; + + typedef struct + { + uint32_t address; + bool bVirtual; + int numBytesPerGroup; + } tab_info_t; + + static jump_item_t JumpItems[]; + static int GetJumpItemIndex(uint32_t address, bool bVirtual); + + CHexEditCtrl m_HexEditCtrl; + CEditNumber32 m_MemAddr; + CAddSymbolDlg m_AddSymbolDlg; + CButton m_VirtualCheckbox; + CMemTabCtrl m_TabCtrl; + CStatusBarCtrl m_StatusBar; + CComboBox m_CmbJump; + + std::vector m_TabData; + CBreakpoints* m_Breakpoints; + + int m_WriteTargetColorStride; + int m_ReadTargetColorStride; + int m_SymbolColorStride; + int m_SymbolColorPhase; + uint32_t m_ContextMenuAddress; + uint32_t m_HotAddress; + bool m_bIgnoreAddressInput; + bool m_bVirtualMemory; + + bool m_bSafeEditMode; + std::vector m_SafeEditQueue; + + bool GetByte(uint32_t address, uint8_t* value); + bool SetByte(uint32_t address, uint8_t value); + void SetupJumpMenu(bool bVirtual); + bool GetSafeEditValue(uint32_t address, uint8_t* value); + void ApplySafeEdits(void); + void CopyTextToClipboard(const char* text); + void CopyBytesToClipboard(uint32_t startAddress, uint32_t endAddress, bool bHex, bool bIncludeAddresses = false, bool bRowAddresses = false); + void CopyGameSharkCodeToClipboard(uint32_t startAddress, uint32_t endAddress); + void FillRange(uint32_t startAddress, uint32_t endAddress, uint8_t value); + void FollowPointer(bool bContextMenuAddress = true); + void JumpToSelection(void); + int AddTab(uint32_t address, bool bVirtual, int numBytesPerGroup); + int InsertTab(int nItem, uint32_t address, bool bVirtual, int numBytesPerGroup); + void DeleteTab(int index); + void UpdateCurrentTab(uint32_t address); + void OpenNewTab(uint32_t address, bool bVirtual, int numBytesPerGroup, bool bInsert = false, bool bOpenExisting = false); + void OpenDuplicateTab(void); + void CloseTab(int nItem); + void CloseCurrentTab(void); + void TabSelChanged(void); + + LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnShowAddress(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); + void OnAddrChanged(UINT Code, int id, HWND ctl); + void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar pScrollBar); + void OnExitSizeMove(void); + LRESULT OnDestroy(void); + LRESULT OnHxGetByteInfo(LPNMHDR lpNMHDR); + LRESULT OnHxSetNibble(LPNMHDR lpNMHDR); + LRESULT OnHxSetByte(LPNMHDR lpNMHDR); + LRESULT OnHxRightClick(LPNMHDR lpNMHDR); + LRESULT OnHxEnterPressed(LPNMHDR lpNMHDR); + LRESULT OnHxRedrawStarted(LPNMHDR lpNMHDR); + LRESULT OnHxBaseAddrChanged(LPNMHDR lpNMHDR); + LRESULT OnHxHotAddrChanged(LPNMHDR lpNMHDR); + LRESULT OnHxCopy(LPNMHDR lpNMHDR); + LRESULT OnHxPaste(LPNMHDR lpNMHDR); + LRESULT OnHxCtrlKeyPressed(LPNMHDR lpNMHDR); + LRESULT OnHxFillRange(LPNMHDR lpNMHDR); + LRESULT OnHxInsertModeChanged(LPNMHDR lpNMHDR); + LRESULT OnHxSelectionChanged(LPNMHDR lpNMHDR); + LRESULT OnHxGroupSizeChanged(LPNMHDR lpNMHDR); + LRESULT OnTabSelChange(LPNMHDR lpNMHDR); + LRESULT OnTabDblClick(LPNMHDR lpNMHDR); + LRESULT OnTabRClick(LPNMHDR lpNMHDR); + LRESULT OnStatusBarClick(LPNMHDR lpNMHDR); + void OnJumpComboSelChange(UINT uNotifyCode, int nID, CWindow wndCtl); + BEGIN_MSG_MAP_EX(CDebugMemoryView) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) + MESSAGE_HANDLER(WM_SHOWADDRESS, OnShowAddress) COMMAND_CODE_HANDLER(BN_CLICKED, OnClicked) COMMAND_HANDLER_EX(IDC_ADDR_EDIT, EN_CHANGE, OnAddrChanged) - NOTIFY_HANDLER_EX(IDC_MEM_DETAILS, LCN_MODIFIED, OnMemoryModified) - NOTIFY_HANDLER_EX(IDC_MEM_DETAILS, LCN_RIGHTCLICK, OnMemoryRightClicked) - NOTIFY_HANDLER_EX(IDC_MEM_DETAILS, LCN_HOTITEMCHANGED, OnHotItemChanged) - MESSAGE_HANDLER(WM_ACTIVATE, OnActivate) MSG_WM_EXITSIZEMOVE(OnExitSizeMove) MSG_WM_DESTROY(OnDestroy) MSG_WM_VSCROLL(OnVScroll) + COMMAND_HANDLER_EX(IDC_CMB_JUMP, CBN_SELCHANGE, OnJumpComboSelChange) + NOTIFY_HANDLER_EX(IDC_STATUSBAR, NM_CLICK, OnStatusBarClick) + NOTIFY_HANDLER_EX(IDC_MEMTABS, NM_DBLCLK, OnTabDblClick) + NOTIFY_HANDLER_EX(IDC_MEMTABS, TCN_SELCHANGE, OnTabSelChange) + NOTIFY_HANDLER_EX(IDC_MEMTABS, MTCN_RCLICK, OnTabRClick) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_GETBYTEINFO, OnHxGetByteInfo) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_SETNIBBLE, OnHxSetNibble) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_SETBYTE, OnHxSetByte) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_RCLICK, OnHxRightClick) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_HOTADDRCHANGED, OnHxHotAddrChanged) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_REDRAWSTARTED, OnHxRedrawStarted) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_BASEADDRCHANGED, OnHxBaseAddrChanged) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_HOTADDRCHANGED, OnHxHotAddrChanged) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_PASTE, OnHxPaste) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_CTRLKEYPRESSED, OnHxCtrlKeyPressed) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_FILLRANGE, OnHxFillRange) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_COPY, OnHxCopy) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_INSERTMODECHANGED, OnHxInsertModeChanged) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_ENTERPRESSED, OnHxEnterPressed) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_SELCHANGED, OnHxSelectionChanged) + NOTIFY_HANDLER_EX(IDC_HEXEDIT, HXN_GROUPSIZECHANGED, OnHxGroupSizeChanged) + CHAIN_MSG_MAP(CDialogResize) END_MSG_MAP() - LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); - LRESULT OnClicked(WORD wNotifyCode, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled); - void OnAddrChanged(UINT Code, int id, HWND ctl); - void OnVScroll(int request, short Pos, HWND ctrl); - void OnExitSizeMove(void); - LRESULT OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); - LRESULT OnMemoryModified(LPNMHDR lpNMHDR); - LRESULT OnMemoryRightClicked(LPNMHDR lpNMHDR); - LRESULT OnHotItemChanged(LPNMHDR lpNMHDR); - LRESULT OnDestroy(void); + BEGIN_DLGRESIZE_MAP(CDebugMemoryView) + DLGRESIZE_CONTROL(IDC_CMB_JUMP, DLSZ_MOVE_X) + DLGRESIZE_CONTROL(IDC_STATUSBAR, DLSZ_SIZE_X | DLSZ_MOVE_Y) + DLGRESIZE_CONTROL(IDC_HEXEDIT, DLSZ_SIZE_X | DLSZ_SIZE_Y) + DLGRESIZE_CONTROL(IDC_MEMTABS, DLSZ_SIZE_X) + DLGRESIZE_CONTROL(IDC_SCRL_BAR, DLSZ_MOVE_X | DLSZ_SIZE_Y) + END_DLGRESIZE_MAP() - void Insert_MemoryLineDump(int LineNumber); - void RefreshMemory(bool ResetCompare); + BEGIN_TOOLTIP_MAP() + TOOLTIP(IDC_SYMBOLS_BTN, "Symbols...") + TOOLTIP(IDC_CHK_VADDR, "Checked = Use virtual address space (CPU), Unchecked = Use physical address space (RCP)") + END_TOOLTIP_MAP() +}; - HANDLE m_AutoRefreshThread; - static DWORD WINAPI AutoRefreshProc(void* _self); - - void SelectColors(uint32_t address, bool changed, COLORREF& bgColor, COLORREF& fgColor, COLORREF& fgHiColor); - bool GetItemOffset(LPNMHDR lpNMHDR, uint32_t &offset); - bool GetItemAddress(LPNMHDR lpNMHDR, uint32_t &address); - - void CopyNumber(uint32_t address, int numBytes); - - enum { MemoryToDisplay = 0x100 }; - - static CDebugMemoryView* _this; - static HHOOK hWinMessageHook; - static LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam); - - void InterceptMouseWheel(WPARAM wParam, LPARAM lParam); - - CEditNumber32 m_MemAddr; - CListCtrl * m_MemoryList; - CAddSymbolDlg m_AddSymbolDlg; - CStatic m_SymInfo; - CStatic m_DMAInfo; - - bool m_bAutoRefreshEnabled; - - CBreakpoints* m_Breakpoints; - - int m_SymbolColorStride; - int m_SymbolColorPhase; - DWORD m_CtxMenuAddr; - - DWORD m_DataStartLoc; - bool m_DataVAddrr; - BYTE m_CurrentData[MemoryToDisplay]; - bool m_DataValid[MemoryToDisplay]; - - DWORD m_CompareStartLoc; - bool m_CompareVAddrr; - BYTE m_CompareData[MemoryToDisplay]; - bool m_CompareValid[MemoryToDisplay]; -}; \ No newline at end of file diff --git a/Source/Project64/UserInterface/Debugger/Debugger.cpp b/Source/Project64/UserInterface/Debugger/Debugger.cpp index 37889575e..6882787d4 100644 --- a/Source/Project64/UserInterface/Debugger/Debugger.cpp +++ b/Source/Project64/UserInterface/Debugger/Debugger.cpp @@ -233,10 +233,6 @@ void CDebuggerUI::OpenMemoryDump() void CDebuggerUI::OpenMemoryWindow(void) { - if (g_MMU == NULL) - { - return; - } if (m_MemoryView == NULL) { m_MemoryView = new CDebugMemoryView(this); @@ -462,192 +458,6 @@ CCPULog* CDebuggerUI::CPULog() return m_CPULog; } -// thread safe LW_PAddr -// does not trigger application breakpoint if paddr is invalid -bool CDebuggerUI::DebugLW_PAddr(uint32_t paddr, uint32_t& value) -{ - if (g_MMU == NULL) - { - return false; - } - - if ((paddr < g_MMU->RdramSize()) || // RDRAM - (paddr >= 0x04000000 && paddr <= 0x04001FFF)) // DMEM/IMEM - { - value = *(uint32_t*)(g_MMU->Rdram() + paddr); - return true; - } - else if (paddr >= 0x05000000 && paddr <= 0x050004FF) // 64DD buffer - { - // todo - return false; - } - else if (paddr >= 0x06000000 && paddr <= 0x06FFFFFF) // Cartridge Domain 1 (Address 1) (64DD IPL ROM) - { - uint32_t iplRomOffset = paddr - 0x06000000; - - if (g_DDRom != NULL && iplRomOffset < g_DDRom->GetRomSize()) - { - value = *(uint32_t*)(g_MMU->Rdram() + paddr); - return true; - } - } - else if (paddr >= 0x08000000 && paddr < 0x08FFFFFF) // Cartridge Domain 2 (Address 2) - { - uint32_t saveOffset = paddr & 0x000FFFFF; - - if (g_System->m_SaveUsing == SaveChip_Sram && saveOffset <= 0x7FFF) // sram - { - uint8_t tmp[4] = ""; - CSram *sram = g_MMU->GetSram(); - sram->DmaFromSram(tmp, paddr - 0x08000000, 4); - value = tmp[3] << 24 | tmp[2] << 16 | tmp[1] << 8 | tmp[0]; - return true; - } - else if (g_System->m_SaveUsing == SaveChip_FlashRam && saveOffset == 0) // flash ram status - { - CFlashram* flashRam = g_MMU->GetFlashram(); - value = flashRam->ReadFromFlashStatus(0x08000000); - return true; - } - } - else if (paddr >= 0x10000000 && paddr <= 0x15FFFFFF) // Cartridge ROM - { - uint32_t cartRomOffset = paddr - 0x10000000; - if (g_Rom != NULL && paddr < g_Rom->GetRomSize()) - { - value = *(uint32_t*)(g_Rom->GetRomAddress() + cartRomOffset); - return true; - } - } - else if (paddr >= 0x1FC00000 && paddr <= 0x1FC007BF) // PIF ROM - { - return false; - } - else if (paddr >= 0x1FC007C0 && paddr <= 0x1FC007FF) // PIF RAM - { - uint32_t pifRamOffset = paddr - 0x1FC007C0; - value = *(uint32_t*)(g_MMU->PifRam() + pifRamOffset); - return true; - } - - // note: write-only registers are excluded - switch (paddr) - { - case 0x03F00000: value = g_Reg->RDRAM_CONFIG_REG; return true; - case 0x03F00004: value = g_Reg->RDRAM_DEVICE_ID_REG; return true; - case 0x03F00008: value = g_Reg->RDRAM_DELAY_REG; return true; - case 0x03F0000C: value = g_Reg->RDRAM_MODE_REG; return true; - case 0x03F00010: value = g_Reg->RDRAM_REF_INTERVAL_REG; return true; - case 0x03F00014: value = g_Reg->RDRAM_REF_ROW_REG; return true; - case 0x03F00018: value = g_Reg->RDRAM_RAS_INTERVAL_REG; return true; - case 0x03F0001C: value = g_Reg->RDRAM_MIN_INTERVAL_REG; return true; - case 0x03F00020: value = g_Reg->RDRAM_ADDR_SELECT_REG; return true; - case 0x03F00024: value = g_Reg->RDRAM_DEVICE_MANUF_REG; return true; - case 0x04040010: value = g_Reg->SP_STATUS_REG; return true; - case 0x04040014: value = g_Reg->SP_DMA_FULL_REG; return true; - case 0x04040018: value = g_Reg->SP_DMA_BUSY_REG; return true; - case 0x0404001C: value = g_Reg->SP_SEMAPHORE_REG; return true; - case 0x04080000: value = g_Reg->SP_PC_REG; return true; - case 0x0410000C: value = g_Reg->DPC_STATUS_REG; return true; - case 0x04100010: value = g_Reg->DPC_CLOCK_REG; return true; - case 0x04100014: value = g_Reg->DPC_BUFBUSY_REG; return true; - case 0x04100018: value = g_Reg->DPC_PIPEBUSY_REG; return true; - case 0x0410001C: value = g_Reg->DPC_TMEM_REG; return true; - case 0x04300000: value = g_Reg->MI_MODE_REG; return true; - case 0x04300004: value = g_Reg->MI_VERSION_REG; return true; - case 0x04300008: value = g_Reg->MI_INTR_REG; return true; - case 0x0430000C: value = g_Reg->MI_INTR_MASK_REG; return true; - case 0x04400000: value = g_Reg->VI_STATUS_REG; return true; - case 0x04400004: value = g_Reg->VI_ORIGIN_REG; return true; - case 0x04400008: value = g_Reg->VI_WIDTH_REG; return true; - case 0x0440000C: value = g_Reg->VI_INTR_REG; return true; - case 0x04400010: value = g_Reg->VI_V_CURRENT_LINE_REG; return true; - case 0x04400014: value = g_Reg->VI_BURST_REG; return true; - case 0x04400018: value = g_Reg->VI_V_SYNC_REG; return true; - case 0x0440001C: value = g_Reg->VI_H_SYNC_REG; return true; - case 0x04400020: value = g_Reg->VI_LEAP_REG; return true; - case 0x04400024: value = g_Reg->VI_H_START_REG; return true; - case 0x04400028: value = g_Reg->VI_V_START_REG; return true; - case 0x0440002C: value = g_Reg->VI_V_BURST_REG; return true; - case 0x04400030: value = g_Reg->VI_X_SCALE_REG; return true; - case 0x04400034: value = g_Reg->VI_Y_SCALE_REG; return true; - case 0x04600000: value = g_Reg->PI_DRAM_ADDR_REG; return true; - case 0x04600004: value = g_Reg->PI_CART_ADDR_REG; return true; - case 0x04600008: value = g_Reg->PI_RD_LEN_REG; return true; - case 0x0460000C: value = g_Reg->PI_WR_LEN_REG; return true; - case 0x04600010: value = g_Reg->PI_STATUS_REG; return true; - case 0x04600014: value = g_Reg->PI_DOMAIN1_REG; return true; - case 0x04600018: value = g_Reg->PI_BSD_DOM1_PWD_REG; return true; - case 0x0460001C: value = g_Reg->PI_BSD_DOM1_PGS_REG; return true; - case 0x04600020: value = g_Reg->PI_BSD_DOM1_RLS_REG; return true; - case 0x04600024: value = g_Reg->PI_DOMAIN2_REG; return true; - case 0x04600028: value = g_Reg->PI_BSD_DOM2_PWD_REG; return true; - case 0x0460002C: value = g_Reg->PI_BSD_DOM2_PGS_REG; return true; - case 0x04600030: value = g_Reg->PI_BSD_DOM2_RLS_REG; return true; - case 0x04700000: value = g_Reg->RI_MODE_REG; return true; - case 0x04700004: value = g_Reg->RI_CONFIG_REG; return true; - case 0x04700008: value = g_Reg->RI_CURRENT_LOAD_REG; return true; - case 0x0470000C: value = g_Reg->RI_SELECT_REG; return true; - case 0x04700010: value = g_Reg->RI_REFRESH_REG; return true; - case 0x04700014: value = g_Reg->RI_LATENCY_REG; return true; - case 0x04700018: value = g_Reg->RI_RERROR_REG; return true; - case 0x0470001C: value = g_Reg->RI_WERROR_REG; return true; - case 0x04800018: value = g_Reg->SI_STATUS_REG; return true; - case 0x05000500: value = g_Reg->ASIC_DATA; return true; - case 0x05000504: value = g_Reg->ASIC_MISC_REG; return true; - case 0x05000508: value = g_Reg->ASIC_STATUS; return true; - case 0x0500050C: value = g_Reg->ASIC_CUR_TK; return true; - case 0x05000510: value = g_Reg->ASIC_BM_STATUS; return true; - case 0x05000514: value = g_Reg->ASIC_ERR_SECTOR; return true; - case 0x05000518: value = g_Reg->ASIC_SEQ_STATUS; return true; - case 0x0500051C: value = g_Reg->ASIC_CUR_SECTOR; return true; - case 0x05000520: value = g_Reg->ASIC_HARD_RESET; return true; - case 0x05000524: value = g_Reg->ASIC_C1_S0; return true; - case 0x05000528: value = g_Reg->ASIC_HOST_SECBYTE; return true; - case 0x0500052C: value = g_Reg->ASIC_C1_S2; return true; - case 0x05000530: value = g_Reg->ASIC_SEC_BYTE; return true; - case 0x05000534: value = g_Reg->ASIC_C1_S4; return true; - case 0x05000538: value = g_Reg->ASIC_C1_S6; return true; - case 0x0500053C: value = g_Reg->ASIC_CUR_ADDR; return true; - case 0x05000540: value = g_Reg->ASIC_ID_REG; return true; - case 0x05000544: value = g_Reg->ASIC_TEST_REG; return true; - case 0x05000548: value = g_Reg->ASIC_TEST_PIN_SEL; return true; - case 0x04500004: - if (g_System->bFixedAudio()) - { - value = g_Audio->GetLength(); - } - else - { - CAudioPlugin* audioPlg = g_Plugins->Audio(); - value = (audioPlg->AiReadLength != NULL) ? audioPlg->AiReadLength() : 0; - } - return true; - case 0x0450000C: - value = g_System->bFixedAudio() ? g_Audio->GetStatus() : g_Reg->AI_STATUS_REG; - return true; - } - - return false; -} - -bool CDebuggerUI::DebugLW_VAddr(uint32_t vaddr, uint32_t& value) -{ - if (vaddr <= 0x7FFFFFFF || vaddr >= 0xC0000000) // KUSEG, KSEG2 (TLB) - { - if (g_MMU == NULL) - { - return false; - } - - return g_MMU->LW_VAddr(vaddr, value); - } - - uint32_t paddr = vaddr & 0x1FFFFFFF; - return DebugLW_PAddr(paddr, value); -} - // CDebugger implementation void CDebuggerUI::TLBChanged() diff --git a/Source/Project64/UserInterface/Debugger/MemoryScanner.cpp b/Source/Project64/UserInterface/Debugger/MemoryScanner.cpp index e7957bfc8..6e0207598 100644 --- a/Source/Project64/UserInterface/Debugger/MemoryScanner.cpp +++ b/Source/Project64/UserInterface/Debugger/MemoryScanner.cpp @@ -989,9 +989,9 @@ bool CMemoryScanner::NextScan() int CMemoryScanner::HexDigitVal(char c) { - if (c >= '0' && c < '9') return (c - '0'); - if (c >= 'A' && c < 'F') return (c - 'A') + 0x0A; - if (c >= 'a' && c < 'f') return (c - 'a') + 0x0A; + if (c >= '0' && c <= '9') return (c - '0'); + if (c >= 'A' && c <= 'F') return (c - 'A') + 0x0A; + if (c >= 'a' && c <= 'f') return (c - 'a') + 0x0A; return 0; } diff --git a/Source/Project64/UserInterface/Debugger/debugger.h b/Source/Project64/UserInterface/Debugger/debugger.h index 20e23ca83..89836b177 100644 --- a/Source/Project64/UserInterface/Debugger/debugger.h +++ b/Source/Project64/UserInterface/Debugger/debugger.h @@ -12,6 +12,7 @@ #include #include #include +#include "DebugMMU.h" class CDumpMemory; class CDebugMemoryView; @@ -33,7 +34,8 @@ class CScriptSystem; class CDebuggerUI : public CDebugger, - public CDebugSettings + public CDebugSettings, + public CDebugMMU { public: CDebuggerUI(); @@ -88,8 +90,8 @@ public: static void GameNameChanged(CDebuggerUI * _this); static void SteppingOpsChanged(CDebuggerUI * _this); - bool DebugLW_PAddr(uint32_t vaddr, uint32_t& value); - bool DebugLW_VAddr(uint32_t vaddr, uint32_t& value); + //bool DebugLW_PAddr(uint32_t vaddr, uint32_t& value); + //bool DebugLW_VAddr(uint32_t vaddr, uint32_t& value); protected: void TLBChanged(void); diff --git a/Source/Project64/UserInterface/MainMenu.cpp b/Source/Project64/UserInterface/MainMenu.cpp index de7e3ca79..c66139546 100644 --- a/Source/Project64/UserInterface/MainMenu.cpp +++ b/Source/Project64/UserInterface/MainMenu.cpp @@ -1252,7 +1252,6 @@ void CMainMenu::FillOutMenu(HMENU hMenu) Item.Reset(SUB_MENU, EMPTY_STRING, EMPTY_STDSTR, &DebugR4300Menu, L"&R4300i"); DebugMenu.push_back(Item); Item.Reset(SUB_MENU, EMPTY_STRING, EMPTY_STDSTR, &DebugMemoryMenu, L"Memory"); - Item.SetItemEnabled(CPURunning); DebugMenu.push_back(Item); DebugMenu.push_back(MENU_ITEM(SPLITER)); Item.Reset(SUB_MENU, EMPTY_STRING, EMPTY_STDSTR, &DebugProfileMenu, L"Profile"); diff --git a/Source/Project64/UserInterface/UIResources.rc b/Source/Project64/UserInterface/UIResources.rc index f20c5839f..727d35b84 100644 --- a/Source/Project64/UserInterface/UIResources.rc +++ b/Source/Project64/UserInterface/UIResources.rc @@ -509,24 +509,19 @@ BEGIN COMBOBOX IDC_LANG_SEL,105,77,112,120,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP END -IDD_Debugger_Memory DIALOGEX 0, 0, 435, 204 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +IDD_Debugger_Memory DIALOGEX 0, 0, 321, 199 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "Memory" FONT 8, "MS Shell Dlg", 0, 0, 0x1 BEGIN - EDITTEXT IDC_ADDR_EDIT,35,5,49,12,ES_AUTOHSCROLL - LTEXT "Address:",IDC_STATIC,4,6,29,11 - CONTROL "Is VAddr?",IDC_CHK_VADDR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,111,7,46,8 - PUSHBUTTON "Dump",IDC_DUMP_MEM,319,4,54,13 - PUSHBUTTON "Search",IDC_SEARCH_MEM,377,4,54,13 - SCROLLBAR IDC_SCRL_BAR,421,23,11,154,SBS_VERT - PUSHBUTTON "Refresh",IDC_REFRSH_MEM,161,4,54,13 - CONTROL "Mem Details",IDC_MEM_DETAILS,"ListCtrl",WS_TABSTOP,4,23,416,154 - PUSHBUTTON "...",IDC_SYMBOLS_BTN,86,4,21,13 - LTEXT "",IDC_SYM_INFO,8,198,418,8 - LTEXT "",IDC_DMA_INFO,8,208,415,8 - GROUPBOX "",IDC_STATIC,4,190,428,30 - CONTROL "Auto",IDC_CHK_AUTOREFRESH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,220,7,31,8 + EDITTEXT IDC_ADDR_EDIT,5,20,49,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_SYMBOLS_BTN,58,20,21,13 + CONTROL "Virtual",IDC_CHK_VADDR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,83,20,40,13 + CONTROL "HexEdit",IDC_HEXEDIT,"HexEditCtrl",WS_TABSTOP,0,36,310,147 + SCROLLBAR IDC_SCRL_BAR,310,36,11,147,SBS_VERT + CONTROL "",IDC_MEMTABS,"SysTabControl32",TCS_FOCUSNEVER,0,3,319,13 + COMBOBOX IDC_CMB_JUMP,194,20,123,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_STATUSBAR,"msctls_statusbar32",WS_TABSTOP,0,188,321,11 END IDD_Debugger_Search DIALOGEX 0, 0, 357, 257 @@ -1597,8 +1592,7 @@ BEGIN IDD_Debugger_Memory, DIALOG BEGIN - RIGHTMARGIN, 431 - BOTTOMMARGIN, 203 + BOTTOMMARGIN, 196 END IDD_Debugger_Search, DIALOG @@ -1954,23 +1948,39 @@ IDR_MEM_BP_POPUP MENU BEGIN POPUP "PopupMenu" BEGIN - MENUITEM "Toggle read breakpoint", ID_POPUPMENU_TOGGLERBP - MENUITEM "Toggle write breakpoint", ID_POPUPMENU_TOGGLEWBP - MENUITEM "Clear all breakpoints", ID_POPUPMENU_CLEARALLBPS + MENUITEM "Read breakpoint\tCtrl+R", ID_POPUPMENU_TOGGLERBP + MENUITEM "Write breakpoint\tCtrl+W", ID_POPUPMENU_TOGGLEWBP + MENUITEM "Lock value\tCtrl+E", ID_POPUPMENU_TOGGLELOCK MENUITEM SEPARATOR - MENUITEM "Toggle lock", ID_POPUPMENU_TOGGLELOCK - MENUITEM "Clear locks", ID_POPUPMENU_CLEARLOCKS + MENUITEM "Clear breakpoints\tCtrl+Q", ID_POPUPMENU_CLEARALLBPS + MENUITEM "Clear locks\tCtrl+Q", ID_POPUPMENU_CLEARLOCKS MENUITEM SEPARATOR - MENUITEM "View disassembly...", ID_POPUPMENU_VIEWDISASM + MENUITEM "Jump here\tCtrl+G", ID_POPUPMENU_JUMPHERE + MENUITEM "Follow pointer\tCtrl+Space", ID_POPUPMENU_FOLLOWPOINTER + MENUITEM SEPARATOR + POPUP "Byte group size" + BEGIN + MENUITEM "1\tCtrl+1", ID_POPUPMENU_BYTEGROUPSIZE_1 + MENUITEM "2\tCtrl+2", ID_POPUPMENU_BYTEGROUPSIZE_2 + MENUITEM "4\tCtrl+4", ID_POPUPMENU_BYTEGROUPSIZE_4 + MENUITEM "8\tCtrl+8", ID_POPUPMENU_BYTEGROUPSIZE_8 + END + MENUITEM "Safe mode\tINS", ID_POPUPMENU_SAFEMODE MENUITEM SEPARATOR MENUITEM "Add symbol...", ID_POPUPMENU_ADDSYMBOL + MENUITEM "View disassembly...", ID_POPUPMENU_VIEWDISASM + MENUITEM "Dump...\tCtrl+S", ID_POPUPMENU_DUMP + MENUITEM "Search...\tCtrl+F", ID_POPUPMENU_SEARCH MENUITEM SEPARATOR - POPUP "Copy" + POPUP "Copy special" BEGIN - MENUITEM "Word", ID_POPUPMENU_COPY_WORD - MENUITEM "Halfword", ID_POPUPMENU_COPY_HALFWORD - MENUITEM "Byte", ID_POPUPMENU_COPY_BYTE + MENUITEM "Data with group addresses", ID_POPUPMENU_COPYDATAWITHGROUPADDRESSES + MENUITEM "Data with row addresses", ID_POPUPMENU_COPYDATAWITHROWADDRESSES + MENUITEM "GameShark code", ID_POPUPMENU_COPYGAMESHARKCODE END + MENUITEM "Copy\tCtrl+C", ID_POPUPMENU_COPY + MENUITEM "Paste\tCtrl+V", ID_POPUPMENU_PASTE + MENUITEM "Zero-fill\tDEL", ID_POPUPMENU_ZEROFILL END END diff --git a/Source/Project64/UserInterface/WTLControls/HexEditCtrl.cpp b/Source/Project64/UserInterface/WTLControls/HexEditCtrl.cpp new file mode 100644 index 000000000..e1497bb0d --- /dev/null +++ b/Source/Project64/UserInterface/WTLControls/HexEditCtrl.cpp @@ -0,0 +1,1747 @@ +#include "stdafx.h" +#include "HexEditCtrl.h" +#include + +CHexEditCtrl::CHexEditCtrl(void) : + m_BaseAddress(0x80000000), + m_DrawnBaseAddress(0xFFFFFFFF), + m_SelStartAddress(0), + m_SelEndAddress(0), + m_SelStartCellSide(HX_LEFT), + m_SelEndCellSide(HX_LEFT), + m_bInsertMode(false), + m_CaretAddress(0), + m_bCaretLoNibble(false), + m_bCaretVisible(false), + m_bHaveCaret(false), + m_bShowHotAddress(false), + m_HotAddress(0), + m_Font(NULL), + m_BackBMP(NULL), + m_BackDC(NULL), + m_CharWidth(0), + m_CharHeight(0), + m_FocusedColumn(HX_COL_NONE), + m_hCursorIBeam(NULL), + m_hCursorDefault(NULL), + m_DragScrollDelta(0), + m_AddressColumnRect({0}), + m_HexDataColumnRect({0}), + m_AsciiColumnRect({0}), + m_bDblClicked(false), + m_bLButtonDown(false), + m_bMouseDragging(false), + m_bLayoutChanged(false), + m_OldBytes(NULL), + m_NewBytes(NULL), + m_NumBytesPerGroup(4), + m_NumByteGroupsPerRow(0), + m_NumVisibleRows(0), + m_NumVisibleBytes(0), + m_RealSelStartAddress(0), + m_RealSelEndAddress(0), + m_bHaveRealSel(false) +{ + WNDCLASS wc; + if (!GetClassInfo(GetModuleHandle(NULL), _T("HexEditCtrl"), &wc)) + { + GetWndClassInfo().m_wc.lpfnWndProc = m_pfnSuperWindowProc; + GetWndClassInfo().Register(&m_pfnSuperWindowProc); + } +} + +CHexEditCtrl::~CHexEditCtrl(void) +{ +} + +int CALLBACK CHexEditCtrl::HaveFontCb(CONST LOGFONTA *lplf, CONST TEXTMETRICA *lptm, DWORD FontType, LPARAM lParam) +{ + const char* name = (const char*)lParam; + if (strcmp(lplf->lfFaceName, name) == 0) + { + return 0; + } + return 1; +} + +bool CHexEditCtrl::HaveFont(HDC hdc, const char* name) +{ + if (EnumFonts(hdc, name, HaveFontCb, (LPARAM)name) == 0) + { + return true; + } + return false; +} + +BOOL CHexEditCtrl::Attach(HWND hWnd) +{ + if (m_hWnd != NULL) + { + return FALSE; + } + + if (!CWindowImpl::SubclassWindow(hWnd)) + { + return FALSE; + } + + CRect wndRc; + if (!GetWindowRect(&wndRc)) + { + return FALSE; + } + + HDC hdc = GetDC(); + HBITMAP hOldBMP; + HFONT hOldFont; + + m_BackDC = CreateCompatibleDC(hdc); + m_BackBMP = CreateCompatibleBitmap(hdc, wndRc.Width(), wndRc.Height()); + hOldBMP = (HBITMAP)SelectObject(m_BackDC, m_BackBMP); + DeleteObject(hOldBMP); + + m_hCursorIBeam = LoadCursor(NULL, IDC_IBEAM); + m_hCursorDefault = LoadCursor(NULL, IDC_ARROW); + + float dpiScale = ::GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f; + + if (HaveFont(hdc, "Consolas")) + { + m_Font = CreateFont((int)(14 * dpiScale), 0, 0, 0, + FW_DONTCARE, + FALSE, + FALSE, + FALSE, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FF_DONTCARE | FIXED_PITCH, + "Consolas"); + } + else + { + m_Font = (HFONT)GetStockObject(ANSI_FIXED_FONT); + } + + hOldFont = (HFONT)SelectObject(m_BackDC, m_Font); + DeleteObject(hOldFont); + + TEXTMETRIC tm; + GetTextMetrics(m_BackDC, &tm); + m_CharHeight = tm.tmHeight; + m_CharWidth = tm.tmAveCharWidth; + + UpdateLayoutInfo(); + ReallocByteBuffers(); + + CRect clrRc(0, 0, wndRc.Width(), wndRc.Height()); + HBRUSH hbrush = CreateSolidBrush(BKCOLOR_DEFAULT); + FillRect(m_BackDC, clrRc, hbrush); + DeleteObject(hbrush); + + SetTimer(TIMER_ID_AUTO_REFRESH, 20, NULL); + SetTimer(TIMER_ID_DRAG_SCROLL, 50, NULL); + + ReleaseDC(hdc); + + return TRUE; +} + +HWND CHexEditCtrl::Detach(void) +{ + if (m_hWnd == NULL) + { + return NULL; + } + + KillTimer(TIMER_ID_AUTO_REFRESH); + KillTimer(TIMER_ID_DRAG_SCROLL); + + if (m_BackBMP != NULL) + { + DeleteObject(m_BackBMP); + m_BackBMP = NULL; + } + + if (m_BackDC != NULL) + { + DeleteObject(m_BackDC); + m_BackDC = NULL; + } + + if (m_Font != NULL) + { + DeleteObject(m_Font); + m_Font = NULL; + } + + if (m_NewBytes != NULL) + { + free(m_NewBytes); + m_NewBytes = NULL; + } + + if (m_OldBytes != NULL) + { + free(m_OldBytes); + m_OldBytes = NULL; + } + + return CWindowImpl::UnsubclassWindow(); +} + +void CHexEditCtrl::Draw(void) +{ + Notify(HXN_REDRAWSTARTED); + + int startCellIndex = 0; + uint32_t startAddress = m_BaseAddress; + int numBytesToUpdate = m_NumVisibleBytes; + bool bIgnoreDiff = false; + + if (m_BaseAddress != m_DrawnBaseAddress) + { + m_bShowHotAddress = false; + int64_t addrDelta = (int64_t)m_BaseAddress - (int64_t)m_DrawnBaseAddress; + + // scroll optimization + if ((addrDelta % m_NumBytesPerRow) == 0 && abs(addrDelta) < (m_NumVisibleBytes - m_NumBytesPerRow)) + { + int rowDelta = (int)(addrDelta / m_NumBytesPerRow); + int numBytesScrolled = abs(rowDelta) * m_NumBytesPerRow; + int numBytesToShift = (m_NumVisibleBytes - numBytesScrolled) - m_NumBytesPerRow; + int shiftSrcIndex = 0, shiftDstIndex = 0; + + numBytesToUpdate = numBytesScrolled + m_NumBytesPerRow; + + CRect rcScrollArea; + rcScrollArea.left = m_HexDataColumnRect.left; + rcScrollArea.right = m_AsciiColumnRect.right; + rcScrollArea.top = m_HexDataColumnRect.top; + rcScrollArea.bottom = m_HexDataColumnRect.bottom; + + if (rowDelta > 0) + { + rcScrollArea.bottom -= m_CharHeight; + startCellIndex = m_NumVisibleBytes - numBytesToUpdate; + shiftSrcIndex = 0 + numBytesScrolled; + shiftDstIndex = 0; + } + else if (rowDelta < 0) + { + rcScrollArea.top += m_CharHeight; + startCellIndex = 0; + shiftSrcIndex = 0 + m_NumBytesPerRow; + shiftDstIndex = shiftSrcIndex + numBytesScrolled; + } + + startAddress = m_BaseAddress + startCellIndex; + + memmove(&m_OldBytes[shiftDstIndex], &m_OldBytes[shiftSrcIndex], numBytesToShift * sizeof(HXBYTEINFO)); + memmove(&m_NewBytes[shiftDstIndex], &m_NewBytes[shiftSrcIndex], numBytesToShift * sizeof(HXBYTEINFO)); + + ScrollDC(m_BackDC, 0, -rowDelta * m_CharHeight, &rcScrollArea, &rcScrollArea, NULL, NULL); + InvalidateRect(&rcScrollArea, false); + } + + DrawAddressColumn(); + DrawHeader(); + + m_DrawnBaseAddress = m_BaseAddress; + bIgnoreDiff = true; + } + + if (m_bLayoutChanged) + { + bIgnoreDiff = true; + m_bLayoutChanged = false; + } + + int numOverflowBytes = 0; + + if (startAddress + numBytesToUpdate < startAddress) + { + numOverflowBytes = (startAddress + numBytesToUpdate); + } + + for (int i = 0; i < numOverflowBytes; i++) + { + m_NewBytes[numBytesToUpdate - numOverflowBytes + i].bHidden = true; + } + + NotifyGetByteInfo(startAddress, numBytesToUpdate - numOverflowBytes, bIgnoreDiff, &m_OldBytes[startCellIndex], &m_NewBytes[startCellIndex]); + + std::unordered_map drawnByteRects; + + for (int i = 0; i < numBytesToUpdate; i++) + { + uint32_t address = startAddress + i; + + HXBYTEINFO* oldByte = &m_OldBytes[startCellIndex + i]; + HXBYTEINFO* newByte = &m_NewBytes[startCellIndex + i]; + + if (IsSelected(address)) + { + // override owner-provided colors if selected + if (newByte->bkColor != BKCOLOR_DEFAULT) + { + // blend owner color with selection color if bkcolor isn't default + newByte->bkColor = BlendColor(BKCOLOR_SEL_FOCUSED, newByte->bkColor); + newByte->color = COLOR_SEL_FOCUSED; + } + else + { + newByte->bkColor = BKCOLOR_SEL_FOCUSED; + newByte->color = COLOR_SEL_FOCUSED; + } + } + + if (address == m_HotAddress && m_bShowHotAddress && !m_bMouseDragging) + { + newByte->bkColor = BlendColor(BKCOLOR_HOT, newByte->bkColor); + } + + // redraw cell if value or colors have changed + if (*newByte != *oldByte) + { + CRect rcHex, rcAscii; + GetHexCellPos(startCellIndex + i, &rcHex); + GetAsciiCellPos(startCellIndex + i, &rcAscii); + + // check if a similar HXBYTEINFO has already been drawn + std::unordered_map::const_iterator drawnByte = drawnByteRects.find(*newByte); + + if (drawnByte != drawnByteRects.end()) + { + HXRECTPAIR src = drawnByte->second; + BitBlt(m_BackDC, rcHex.left, rcHex.top, src.rcHex.Width(), src.rcHex.Height(), + m_BackDC, src.rcHex.left, src.rcHex.top, SRCCOPY); + BitBlt(m_BackDC, rcAscii.left, rcAscii.top, src.rcAscii.Width(), src.rcAscii.Height(), + m_BackDC, src.rcAscii.left, src.rcAscii.top, SRCCOPY); + InvalidateRect(&rcHex, false); + InvalidateRect(&rcAscii, false); + } + else if (newByte->bHidden) + { + HXRECTPAIR rectPair; + Text(rcHex.left, rcHex.top, " ", BKCOLOR_DEFAULT, BKCOLOR_DEFAULT, &rectPair.rcHex); + Text(rcAscii.left, rcAscii.top, " ", BKCOLOR_DEFAULT, BKCOLOR_DEFAULT, &rectPair.rcAscii); + drawnByteRects[*newByte] = rectPair; + } + else + { + COLORREF hexBkColor = newByte->bkColor; + COLORREF hexColor = newByte->color; + COLORREF asciiBkColor = newByte->bkColor; + COLORREF asciiColor = newByte->color; + + if (IsSelected(address)) + { + if (m_FocusedColumn == HX_COL_ASCII) + { + hexBkColor = BKCOLOR_SEL_UNFOCUSED; + hexColor = COLOR_SEL_UNFOCUSED; + } + else + { + asciiBkColor = BKCOLOR_SEL_UNFOCUSED; + asciiColor = COLOR_SEL_UNFOCUSED; + } + } + + HXRECTPAIR rectPair; + + if (newByte->bValid) + { + char szHexByte[4], szAsciiByte[2]; + sprintf(szHexByte, "%02X", newByte->value); + sprintf(szAsciiByte, "%c", ByteAscii(newByte->value)); + Text(rcHex.left, rcHex.top, szHexByte, hexBkColor, hexColor, &rectPair.rcHex); + Text(rcAscii.left, rcAscii.top, szAsciiByte, asciiBkColor, asciiColor, &rectPair.rcAscii); + } + else + { + Text(rcHex.left, rcHex.top, "**", hexBkColor, hexColor, &rectPair.rcHex); + Text(rcAscii.left, rcAscii.top, ".", asciiBkColor, asciiColor, &rectPair.rcAscii); + } + + drawnByteRects[*newByte] = rectPair; + } + } + + *oldByte = *newByte; + } + + UpdateCaretUI(false); +} + +void CHexEditCtrl::HitTest(int x, int y, HXHITTEST* pht) +{ + memset(pht, 0, sizeof(HXHITTEST)); + pht->column = HX_COL_NONE; + + CPoint pt(x, y); + + if (PtInRect(&m_AddressColumnRect, pt)) + { + pht->column = HX_COL_ADDRESS; + } + + int headerHeight = m_CharHeight; + + // clamp row + int row = (y - headerHeight) / m_CharHeight; + row = max(0, row); + row = min(m_NumVisibleRows - 1, row); + + uint32_t rowAddress = SatAdd32(m_BaseAddress, row * m_NumBytesPerRow); + + if (x >= m_HexDataColumnRect.left && x < m_HexDataColumnRect.right) + { + if (PtInRect(&m_HexDataColumnRect, pt)) + { + pht->column = HX_COL_HEXDATA; + } + + int groupWidth = (m_NumBytesPerGroup * m_CharWidth * 2) + (m_CharWidth * 1); + int nGroup = (x - m_HexDataColumnRect.left) / groupWidth; + int groupX = m_HexDataColumnRect.left + nGroup * groupWidth; + int groupCharIdx = (x - groupX) / (m_CharWidth); + uint32_t address = SatAdd32(rowAddress, nGroup * m_NumBytesPerGroup + groupCharIdx / 2); + pht->hexAddress = address; + pht->hexCellSide = (groupCharIdx & 1) ? HX_RIGHT : HX_LEFT; // todo fix for wrap + pht->asciiAddress = address; // approximate + pht->asciiCellSide = HX_LEFT; + } + else if (x >= m_AsciiColumnRect.left && x < m_AsciiColumnRect.right) + { + if (PtInRect(&m_AsciiColumnRect, pt)) + { + pht->column = HX_COL_ASCII; + } + + int asciiX = x - m_AsciiColumnRect.left; + int idx = (asciiX / m_CharWidth); + pht->asciiAddress = SatAdd32(rowAddress, idx); + pht->asciiCellSide = ((asciiX % m_CharWidth) > (m_CharWidth / 2)) ? HX_RIGHT : HX_LEFT; + pht->hexAddress = SatAdd32(rowAddress, (m_NumBytesPerRow - 1)); // approximate + pht->hexCellSide = HX_RIGHT; + } + else if (x < m_HexDataColumnRect.left) + { + // approximate + pht->hexAddress = rowAddress; + pht->hexCellSide = HX_LEFT; + pht->asciiAddress = rowAddress; + pht->asciiCellSide = HX_LEFT; + } + else if (x >= m_AsciiColumnRect.right) + { + // approximate + pht->hexAddress = SatAdd32(rowAddress, (m_NumBytesPerRow - 1)); + pht->hexCellSide = HX_RIGHT; + pht->asciiAddress = SatAdd32(rowAddress, (m_NumBytesPerRow - 1)); + pht->asciiCellSide = HX_RIGHT; + } +} + +bool CHexEditCtrl::UpdateCaretUI(bool bEnsureVisible, bool bTop) +{ + if (bEnsureVisible) + { + EnsureCaretAddressVisible(bTop); + } + + if (!m_bHaveCaret) + { + return false; + } + + if (!IsCaretAddressVisible()) + { + HideCaret(); + return false; + } + + ShowCaret(); + int index = m_CaretAddress - m_BaseAddress; + CRect rcCell; + + int xoffs = 0; + + if (m_FocusedColumn == HX_COL_ASCII) + { + if ((int)((m_RealSelEndAddress - m_BaseAddress) % m_NumBytesPerRow) == m_NumBytesPerRow - 1) + { + // left-to-right selection ends on the end of a row + index--; + xoffs = m_CharWidth; + } + + GetAsciiCellPos(index, &rcCell); + SetCaretPos(rcCell.left + xoffs, rcCell.top); + } + else + { + if (GetSelDirection() > 0) + { + if ((int)((m_RealSelEndAddress - m_BaseAddress) % m_NumBytesPerRow) == m_NumBytesPerRow - 1) + { + // left-to-right selection ends on the end of a row + index--; + xoffs = m_CharWidth * 2; + } + else if ((int)((m_RealSelEndAddress - m_BaseAddress) % m_NumBytesPerGroup) == m_NumBytesPerGroup - 1) + { + // left-to-right selection ends on the end of a group + xoffs = -m_CharWidth; + } + } + + GetHexCellPos(index, &rcCell); + SetCaretPos(rcCell.left + (m_bCaretLoNibble ? m_CharWidth : 0) + xoffs, rcCell.top); + } + + return true; +} + +void CHexEditCtrl::Text(int x, int y, const char *text, COLORREF bg, COLORREF fg, CRect *rcOut) +{ + size_t length = strlen(text); + int calcWidth = length * m_CharWidth; + + CRect rc(x, y, 0, 0); + COLORREF orgBg = ::SetBkColor(m_BackDC, bg); + COLORREF orgFg = ::SetTextColor(m_BackDC, fg); + ::DrawText(m_BackDC, text, -1, &rc, DT_TOP | DT_NOPREFIX | DT_CALCRECT); + rc.right = rc.left + calcWidth; // just in case + ::DrawText(m_BackDC, text, -1, &rc, DT_TOP | DT_NOPREFIX); + InvalidateRect(&rc, false); + ::SetBkColor(m_BackDC, orgBg); + ::SetBkColor(m_BackDC, orgFg); + + *rcOut = rc; +} + +void CHexEditCtrl::UpdateRealSelection(void) +{ + uint32_t start = m_SelStartAddress; + uint32_t end = m_SelEndAddress; + bool bHaveSel = true; + + if (start < end) + { + if (m_SelEndCellSide == HX_LEFT) end--; + if (m_SelStartCellSide == HX_RIGHT) start++; + } + else if (end < start) + { + if (start - end == 1) + { + if (m_SelStartCellSide == HX_LEFT && m_SelEndCellSide == HX_RIGHT) + { + bHaveSel = false; + } + } + + if (m_SelStartCellSide == HX_LEFT) start--; + if (m_SelEndCellSide == HX_RIGHT) end++; + + swap(start, end); + } + else if(start == end) + { + if (m_SelStartCellSide == m_SelEndCellSide) + { + bHaveSel = false; + } + } + + if (m_RealSelStartAddress != start || + m_RealSelEndAddress != end || + m_bHaveRealSel != bHaveSel) + { + m_bHaveRealSel = bHaveSel; + m_RealSelStartAddress = start; + m_RealSelEndAddress = end; + Notify(HXN_SELCHANGED); + } +} + +bool CHexEditCtrl::IsSelected(uint32_t address) +{ + return m_bHaveRealSel && (address >= m_RealSelStartAddress && address <= m_RealSelEndAddress); +} + +void CHexEditCtrl::DrawAddressColumn() +{ + int headerHeight = m_CharHeight; + for (int nRow = 0; nRow < m_NumVisibleRows; nRow++) + { + CRect rcAddress; + uint32_t rowAddress = m_BaseAddress + (nRow * m_NumBytesPerRow); + int y = headerHeight + nRow * m_CharHeight; + + if (rowAddress >= m_BaseAddress) + { + Text(0, y, stdstr_f(" %08X ", rowAddress).c_str(), BKCOLOR_ADDR, COLOR_ADDR, &rcAddress); + } + else + { + // wrapped + Text(0, y, " ", BKCOLOR_ADDR, COLOR_ADDR, &rcAddress); + } + } +} + +void CHexEditCtrl::DrawHeader() +{ + CRect rcClient; + GetClientRect(&rcClient); + CRect rcHeader = { 0, 0, rcClient.Width(), m_CharHeight }; + HBRUSH br = CreateSolidBrush(BKCOLOR_ADDR); + FillRect(m_BackDC, &rcHeader, br); + DeleteObject(br); + + int groupWidth = m_NumBytesPerGroup * m_CharWidth * 2 + m_CharWidth; + + for (int nGroup = 0; nGroup < m_NumByteGroupsPerRow; nGroup++) + { + int groupX = m_HexDataColumnRect.left + nGroup * groupWidth; + int offs = nGroup * m_NumBytesPerGroup; + CRect dummy; + Text(groupX, 0, stdstr_f("%02X", offs).c_str(), BKCOLOR_ADDR, COLOR_ADDR, &dummy); + } + + InvalidateRect(&rcHeader, false); +} + +void CHexEditCtrl::GetHexCellPos(int index, CRect* rc) +{ + int nRow = index / m_NumBytesPerRow; + int rowOffs = (index % m_NumBytesPerRow); + int nGroup = rowOffs / m_NumBytesPerGroup; + int byteOffs = rowOffs % m_NumBytesPerGroup; + + int addrColumnWidth = (m_CharWidth * 11); + int byteWidth = (m_CharWidth * 2); + int hexGroupWidth = (byteWidth * m_NumBytesPerGroup) + (m_CharWidth * 1); + + int headerHeight = m_CharHeight; + + rc->left = addrColumnWidth + (nGroup * hexGroupWidth) + (byteOffs * byteWidth); + rc->top = headerHeight + nRow * m_CharHeight; + rc->right = rc->left + m_CharWidth * 2; + rc->bottom = rc->top + m_CharHeight; +} + +void CHexEditCtrl::GetAsciiCellPos(int index, CRect* rc) +{ + int nRow = index / m_NumBytesPerRow; + int rowOffs = (index % m_NumBytesPerRow); + + int addrColumnWidth = (m_CharWidth * 11); + int byteWidth = (m_CharWidth * 2); + int hexGroupWidth = (byteWidth * m_NumBytesPerGroup) + (m_CharWidth * 1); + int hexColumnWidth = (m_NumByteGroupsPerRow * hexGroupWidth); + int asciiColumnX = 0 + addrColumnWidth + hexColumnWidth; + int headerHeight = m_CharHeight; + + rc->left = asciiColumnX + (rowOffs * m_CharWidth); + rc->top = headerHeight + nRow * m_CharHeight; + rc->right = rc->left + m_CharWidth; + rc->bottom = rc->top + m_CharHeight; +} + +char CHexEditCtrl::ByteAscii(uint8_t value) +{ + if (value <= 0x1F) + { + return '.'; + } + + if (value >= 0x20 && value <= 0x7E) + { + return (char)value; + } + + switch (value) + { + case 0x7F: + case 0x81: + case 0x8D: + case 0x8F: + case 0x90: + case 0x9D: + // undefined in windows-1252 + return '.'; + } + + return (char)value; +} + +uint8_t CHexEditCtrl::HexCharValue(char c) +{ + if (!isxdigit(c)) + { + return 0; + } + + if (c >= '0' && c <= '9') return (c - '0'); + if (c >= 'A' && c <= 'F') return (c - 'A') + 0x0A; + if (c >= 'a' && c <= 'f') return (c - 'a') + 0x0A; + + return 0; +} + +void CHexEditCtrl::CaretIncrementNibble(void) +{ + if (!m_bCaretLoNibble) + { + m_bCaretLoNibble = true; + } + else + { + m_bCaretLoNibble = false; + m_CaretAddress++; + } +} + +void CHexEditCtrl::CaretDecrementNibble(void) +{ + if (m_bCaretLoNibble) + { + m_bCaretLoNibble = false; + } + else + { + m_bCaretLoNibble = true; + m_CaretAddress--; + } +} + +void CHexEditCtrl::OnTimer(UINT_PTR nIDEvent) +{ + if (nIDEvent == TIMER_ID_AUTO_REFRESH) + { + Draw(); + } + else if (nIDEvent == TIMER_ID_DRAG_SCROLL) + { + if (m_DragScrollDelta != 0) + { + int numBytesToScroll = m_DragScrollDelta * m_NumBytesPerRow; + int64_t newCaretAddress = (int64_t)m_CaretAddress + numBytesToScroll; + + if (newCaretAddress < 0 && m_BaseAddress == 0) + { + return; + } + + else if (newCaretAddress > UINT_MAX) + { + return; + } + + m_CaretAddress = SatAdd32(m_CaretAddress, numBytesToScroll); + m_SelEndAddress = SatAdd32(m_SelEndAddress, numBytesToScroll); + + UpdateRealSelection(); + UpdateCaretUI(true); + } + } +} + +void CHexEditCtrl::OnPaint(CDCHandle dc) +{ + PAINTSTRUCT ps; + HDC hdc = BeginPaint(&ps); + CRect rc = ps.rcPaint; + + BitBlt(hdc, + rc.left, rc.top, + rc.Width(), rc.Height(), + m_BackDC, + rc.left, rc.top, + SRCCOPY); + + EndPaint(&ps); +} + +void CHexEditCtrl::OnRButtonDown(UINT /*nFlags*/, CPoint point) +{ + SetFocus(); + + HXHITTEST ht; + HitTest(point.x, point.y, &ht); + + if (ht.column == HX_COL_HEXDATA) + { + if (!IsSelected(ht.hexAddress)) + { + m_CaretAddress = ht.hexAddress; + m_bCaretLoNibble = HX_LEFT; + CancelSelection(); + } + } + else if (ht.column == HX_COL_ASCII) + { + if (!IsSelected(ht.asciiAddress)) + { + m_CaretAddress = ht.asciiAddress; + CancelSelection(); + } + } +} + +void CHexEditCtrl::OnRButtonUp(UINT /*nFlags*/, CPoint point) +{ + HXHITTEST ht; + HitTest(point.x, point.y, &ht); + + if (ht.column == HX_COL_HEXDATA) + { + NotifyRightClick(ht.hexAddress); + } + else if (ht.column == HX_COL_ASCII) + { + NotifyRightClick(ht.asciiAddress); + } +} + +void CHexEditCtrl::OnLButtonDown(UINT nFlags, CPoint point) +{ + m_bLButtonDown = true; + + SetFocus(); + + HXHITTEST ht; + HitTest(point.x, point.y, &ht); + + m_FocusedColumn = ht.column; + + if (m_FocusedColumn == HX_COL_HEXDATA) + { + m_CaretAddress = ht.hexAddress; + m_bCaretLoNibble = (ht.hexCellSide == HX_RIGHT); + + if (nFlags & MK_SHIFT) + { + m_SelEndAddress = ht.hexAddress; + m_SelEndCellSide = ht.hexCellSide; + UpdateRealSelection(); + + if (GetSelDirection() > 0) + { + m_CaretAddress = m_RealSelEndAddress + 1; + } + else + { + m_CaretAddress = m_RealSelStartAddress; + } + m_bCaretLoNibble = false; + } + else + { + m_SelStartAddress = ht.hexAddress; + m_SelEndAddress = ht.hexAddress; + m_SelStartCellSide = ht.hexCellSide; + m_SelEndCellSide = ht.hexCellSide; + UpdateRealSelection(); + } + } + else if (m_FocusedColumn == HX_COL_ASCII) + { + m_CaretAddress = ht.asciiAddress; + + if (nFlags & MK_SHIFT) + { + m_SelEndAddress = ht.asciiAddress; + } + else + { + m_CaretAddress = ht.asciiAddress; + m_SelStartCellSide = ht.asciiCellSide; + m_SelEndCellSide = ht.asciiCellSide; + m_SelStartAddress = ht.asciiAddress; + m_SelEndAddress = ht.asciiAddress; + if (ht.asciiCellSide) + { + m_CaretAddress++; + } + } + + UpdateRealSelection(); + } + + UpdateCaretUI(false); + Draw(); + + SetCapture(); +} + +void CHexEditCtrl::OnLButtonDblClk(UINT /*nFlags*/, CPoint point) +{ + m_bDblClicked = true; + + HXHITTEST ht; + HitTest(point.x, point.y, &ht); + + if (m_FocusedColumn == HX_COL_HEXDATA) + { + // select word + uint32_t offset = (ht.hexAddress - m_BaseAddress); + uint32_t wordOffset = offset - (offset % m_NumBytesPerGroup); + uint32_t wordAddress = m_BaseAddress + wordOffset; + m_SelStartAddress = wordAddress; + m_SelEndAddress = wordAddress + (m_NumBytesPerGroup - 1); + m_SelStartCellSide = HX_LEFT; + m_SelEndCellSide = HX_RIGHT; + m_CaretAddress = m_SelEndAddress + 1; + m_bCaretLoNibble = false; + UpdateRealSelection(); + UpdateCaretUI(false); + } + if (m_FocusedColumn == HX_COL_ASCII) + { + // select row + uint32_t offset = (ht.asciiAddress - m_BaseAddress); + uint32_t rowOffset = (ht.asciiAddress - m_BaseAddress) - (offset % m_NumBytesPerRow); + uint32_t rowAddress = m_BaseAddress + rowOffset; + m_SelStartAddress = rowAddress; + m_SelEndAddress = rowAddress + (m_NumBytesPerRow - 1); + m_SelStartCellSide = HX_LEFT; + m_SelEndCellSide = HX_RIGHT; + m_CaretAddress = m_SelEndAddress + 1; + UpdateRealSelection(); + UpdateCaretUI(false); + } +} + +void CHexEditCtrl::OnLButtonUp(UINT /*nFlags*/, CPoint point) +{ + m_bLButtonDown = false; + m_bMouseDragging = false; + + if (m_DragScrollDelta != 0) + { + m_bDblClicked = false; + m_DragScrollDelta = 0; + ReleaseCapture(); + return; + } + + HXHITTEST ht; + HitTest(point.x, point.y, &ht); + + if (m_bDblClicked) + { + m_bDblClicked = false; + return; + } + + ReleaseCapture(); +} + +void CHexEditCtrl::OnMouseMove(UINT /*nFlags*/, CPoint point) +{ + if (m_bLButtonDown) + { + m_bMouseDragging = true; + } + + HXHITTEST ht; + HitTest(point.x, point.y, &ht); + + if (ht.column == HX_COL_NONE || ht.column == HX_COL_ADDRESS) + { + m_bShowHotAddress = false; + } + else + { + m_bShowHotAddress = true; + + if (ht.column == HX_COL_HEXDATA) + { + if (m_HotAddress != ht.hexAddress) + { + m_HotAddress = ht.hexAddress; + Notify(HXN_HOTADDRCHANGED); + } + } + else if (ht.column == HX_COL_ASCII) + { + if (m_HotAddress != ht.asciiAddress) + { + m_HotAddress = ht.asciiAddress; + Notify(HXN_HOTADDRCHANGED); + } + } + } + + if (!m_bLButtonDown) + { + return; + } + + m_DragScrollDelta = 0; + + if (point.y > m_HexDataColumnRect.bottom) + { + m_DragScrollDelta = 1 + (point.y - m_HexDataColumnRect.bottom) / m_CharHeight; + } + else if (point.y < m_HexDataColumnRect.top) + { + m_DragScrollDelta = -1 + (point.y - m_HexDataColumnRect.top) / m_CharHeight; + } + + if (m_FocusedColumn == HX_COL_HEXDATA) + { + m_CaretAddress = ht.hexAddress; + m_SelEndAddress = ht.hexAddress; + m_SelEndCellSide = ht.hexCellSide; + m_bCaretLoNibble = (ht.hexCellSide == HX_RIGHT); + + if (m_SelEndAddress - m_SelStartAddress == 1 && + m_SelStartCellSide == HX_RIGHT && + m_SelEndCellSide == HX_LEFT) + { + m_SelStartCellSide = HX_LEFT; + } + + if (GetSelDirection() != 0 && m_SelEndCellSide == HX_RIGHT) + { + m_bCaretLoNibble = false; + m_CaretAddress++; + } + } + else if (m_FocusedColumn == HX_COL_ASCII) + { + m_CaretAddress = ht.asciiAddress; + m_SelEndAddress = ht.asciiAddress; + m_SelEndCellSide = ht.asciiCellSide; + + if (GetSelDirection() != 0 && m_SelEndCellSide == HX_RIGHT) + { + m_CaretAddress++; + } + } + + UpdateRealSelection(); +} + +BOOL CHexEditCtrl::OnMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/) +{ + m_BaseAddress = SatAdd32(m_BaseAddress, -(zDelta / 120) * m_NumBytesPerRow); + Notify(HXN_BASEADDRCHANGED); + return FALSE; +} + +void CHexEditCtrl::OnSetFocus(CWindow /*wndOld*/) +{ + ::CreateCaret(m_hWnd, NULL, 2, m_CharHeight); + m_bHaveCaret = true; + UpdateCaretUI(false); +} + +void CHexEditCtrl::OnKillFocus(CWindow /*wndFocus*/) +{ + m_bCaretVisible = false; + m_bHaveCaret = false; + ::DestroyCaret(); +} + +UINT CHexEditCtrl::OnGetDlgCode(LPMSG /*lpMsg*/) +{ + return DLGC_WANTALLKEYS; +} + +void CHexEditCtrl::OnChar(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/) +{ + if (::GetKeyState(VK_CONTROL) & 0x8000) + { + return; + } + + if (nChar == VK_BACK || nChar == VK_TAB || nChar == VK_RETURN) + { + return; + } + + if (m_FocusedColumn == HX_COL_HEXDATA) + { + if (isxdigit(nChar)) + { + int selDirection = GetSelDirection(); + + if (selDirection < 0) + { + m_CaretAddress = m_SelEndAddress; + m_bCaretLoNibble = false; + } + else if (selDirection > 0) + { + m_CaretAddress = m_SelStartAddress; + m_bCaretLoNibble = false; + } + + NotifySetNibble(m_CaretAddress, m_bCaretLoNibble, HexCharValue((char)nChar)); + + CancelSelection(); + CaretIncrementNibble(); + UpdateCaretUI(true); + } + } + else if (m_FocusedColumn == HX_COL_ASCII) + { + int selDirection = GetSelDirection(); + if (selDirection < 0) + { + m_CaretAddress = m_SelEndAddress; + } + else if(selDirection > 0) + { + m_CaretAddress = m_SelStartAddress; + } + + NotifySetByte(m_CaretAddress, (uint8_t)nChar); + + CancelSelection(); + m_CaretAddress++; + UpdateCaretUI(true); + } +} + +void CHexEditCtrl::Paste(bool bAdvanceCaret) +{ + uint32_t targetAddress = m_bHaveRealSel ? m_RealSelStartAddress : m_CaretAddress; + int retLength = NotifyPaste(targetAddress); + + if (retLength != 0) + { + if (bAdvanceCaret) + { + m_CaretAddress = targetAddress + retLength; + UpdateCaretUI(true); + } + CancelSelection(); + } +} + +void CHexEditCtrl::Copy(void) +{ + if (m_bHaveRealSel) + { + Notify(HXN_COPY); + } +} + +void CHexEditCtrl::SetBaseAddress(uint32_t address) +{ + if (m_BaseAddress != address) + { + m_BaseAddress = address; + Draw(); + } +} + +uint32_t CHexEditCtrl::GetCaretAddress(void) +{ + return m_CaretAddress; +} + +uint32_t CHexEditCtrl::GetHotAddress(void) +{ + return m_HotAddress; +} + +uint32_t CHexEditCtrl::GetBaseAddress(void) +{ + return m_BaseAddress; +} + +int CHexEditCtrl::GetNumBytesPerRow(void) +{ + return m_NumBytesPerRow; +} + +int CHexEditCtrl::GetNumVisibleBytes(void) +{ + return m_NumVisibleBytes; +} + +int CHexEditCtrl::GetNumBytesPerGroup(void) +{ + return m_NumBytesPerGroup; +} + +bool CHexEditCtrl::GetSelectionRange(uint32_t* startAddress, uint32_t* endAddress) +{ + *startAddress = m_RealSelStartAddress; + *endAddress = m_RealSelEndAddress; + return m_bHaveRealSel; +} + +bool CHexEditCtrl::GetInsertMode(void) +{ + return m_bInsertMode; +} + +HXCOLUMN CHexEditCtrl::GetFocusedColumn(void) +{ + return m_FocusedColumn; +} + +void CHexEditCtrl::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/) +{ + if (nChar != VK_CONTROL && (GetKeyState(VK_CONTROL) & 0x8000)) + { + NotifyCtrlKeyPressed(nChar); + + if (nChar == 'V') + { + Paste(); + } + else if (nChar == 'B') + { + Paste(false); + } + else if (nChar == 'C') + { + Copy(); + } + else if (nChar == 'X') + { + Copy(); + if (m_bHaveRealSel) + { + NotifyFillRange(m_RealSelStartAddress, m_RealSelEndAddress, 0); + m_CaretAddress = m_RealSelStartAddress; + m_bCaretLoNibble = false; + CancelSelection(); + } + } + else if (nChar == 'A') + { + SelectAllVisible(); + } + } + + + if (nChar == VK_DOWN) + { + m_CaretAddress = SatAdd32(m_CaretAddress, m_NumBytesPerRow); + + if (GetKeyState(VK_SHIFT) & 0x8000) + { + m_SelEndAddress += m_NumBytesPerRow; + if (m_bCaretLoNibble) + { + m_SelStartCellSide = HX_LEFT; + m_SelEndCellSide = HX_LEFT; + m_bCaretLoNibble = false; + } + UpdateRealSelection(); + } + else + { + CancelSelection(); + } + + UpdateCaretUI(true); + } + else if (nChar == VK_UP) + { + m_CaretAddress -= m_NumBytesPerRow; + + if (GetKeyState(VK_SHIFT) & 0x8000) + { + m_SelEndAddress -= m_NumBytesPerRow; + if (m_bCaretLoNibble) + { + m_SelStartCellSide = HX_LEFT; + m_SelEndCellSide = HX_LEFT; + m_bCaretLoNibble = false; + } + UpdateRealSelection(); + } + else + { + CancelSelection(); + } + + UpdateCaretUI(true); + } + else if (nChar == VK_RIGHT) + { + if (m_FocusedColumn == HX_COL_HEXDATA) + { + if (GetKeyState(VK_SHIFT) & 0x8000) + { + if (m_SelStartAddress == m_SelEndAddress && + m_SelStartCellSide == HX_RIGHT && + m_SelEndCellSide == HX_RIGHT) + { + m_SelStartCellSide = HX_LEFT; + m_SelEndCellSide = HX_RIGHT; + } + else + { + m_SelEndAddress++; + } + + m_bCaretLoNibble = false; + m_CaretAddress++; + UpdateRealSelection(); + } + else if (GetKeyState(VK_CONTROL) & 0x8000) + { + CaretIncrementNibble(); + CancelSelection(); + } + else + { + m_bCaretLoNibble = false; + m_CaretAddress++; + m_SelStartAddress = m_CaretAddress; + m_SelEndAddress = m_CaretAddress; + m_SelStartCellSide = HX_LEFT; + m_SelEndCellSide = HX_LEFT; + CancelSelection(); + } + } + else if (m_FocusedColumn == HX_COL_ASCII) + { + m_CaretAddress++; + + if (GetKeyState(VK_SHIFT)) + { + m_SelEndCellSide = HX_LEFT; + m_SelEndAddress = m_CaretAddress; + UpdateRealSelection(); + } + else + { + CancelSelection(); + } + } + + UpdateCaretUI(true); + } + else if (nChar == VK_LEFT) + { + if (m_FocusedColumn == HX_COL_HEXDATA) + { + if(GetKeyState(VK_SHIFT) & 0x8000) + { + m_SelEndCellSide = HX_LEFT; + m_SelEndAddress--; + if (m_bCaretLoNibble) + { + m_SelStartCellSide = HX_LEFT; + } + m_CaretAddress--; + m_bCaretLoNibble = false; + UpdateRealSelection(); + } + else if (GetKeyState(VK_CONTROL) & 0x8000) + { + CaretDecrementNibble(); + CancelSelection(); + } + else + { + if (m_bCaretLoNibble) + { + CaretDecrementNibble(); + } + else + { + m_CaretAddress--; + } + + CancelSelection(); + } + } + else if (m_FocusedColumn == HX_COL_ASCII) + { + m_CaretAddress--; + + if (GetKeyState(VK_SHIFT)) + { + m_SelEndCellSide = HX_LEFT; + m_SelEndAddress = m_CaretAddress; + UpdateRealSelection(); + } + else + { + CancelSelection(); + } + } + + UpdateCaretUI(true); + } + else if (nChar == VK_NEXT || nChar == VK_PRIOR) // page down, page up + { + int delta = (nChar == VK_NEXT) ? m_NumVisibleBytes : -m_NumVisibleBytes; + + if (IsCaretAddressVisible()) + { + m_BaseAddress += delta; + m_CaretAddress += delta; + Notify(HXN_BASEADDRCHANGED); + } + else + { + m_CaretAddress += delta; + UpdateCaretUI(true, true); + } + + CancelSelection(); + } + else if (nChar == VK_INSERT) + { + m_bInsertMode = !m_bInsertMode; + Notify(HXN_INSERTMODECHANGED); + } + else if (nChar == VK_RETURN) + { + Notify(HXN_ENTERPRESSED); + } + else if (nChar == VK_HOME) + { + UpdateCaretUI(true); + } + else if (nChar == VK_BACK) + { + if (m_bHaveRealSel) + { + NotifyFillRange(m_RealSelStartAddress, m_RealSelEndAddress, 0); + CancelSelection(); + m_CaretAddress = m_RealSelStartAddress; + UpdateCaretUI(true); + } + else + { + if (m_FocusedColumn == HX_COL_HEXDATA) + { + uint32_t address = m_CaretAddress + (m_bCaretLoNibble ? 0 : -1); + NotifySetNibble(address, !m_bCaretLoNibble, 0); + CaretDecrementNibble(); + UpdateCaretUI(true); + } + else if (m_FocusedColumn == HX_COL_ASCII) + { + NotifySetByte(m_CaretAddress - 1, 0); + m_CaretAddress--; + UpdateCaretUI(true); + } + } + } + else if (nChar == VK_DELETE) + { + if (m_FocusedColumn == HX_COL_HEXDATA || m_FocusedColumn == HX_COL_ASCII) + { + if (!m_bHaveRealSel) + { + NotifySetByte(m_CaretAddress, 0); + } + else + { + NotifyFillRange(m_RealSelStartAddress, m_RealSelEndAddress, 0); + } + } + } +} + +int CHexEditCtrl::GetSelDirection(void) +{ + if (m_SelStartAddress < m_SelEndAddress) return 1; // right + if (m_SelStartAddress > m_SelEndAddress) return -1; // left + if (m_SelStartCellSide == m_SelEndCellSide) return 0; // no selection + if (m_SelStartCellSide == HX_LEFT && m_SelEndCellSide == HX_RIGHT) return 1; // right (single byte) + if (m_SelStartCellSide == HX_RIGHT && m_SelEndCellSide == HX_LEFT) return -1; // left (single byte) + return 0; +} + +void CHexEditCtrl::CancelSelection(void) +{ + m_SelStartAddress = m_CaretAddress; + m_SelEndAddress = m_CaretAddress; + m_SelStartCellSide = m_bCaretLoNibble ? HX_RIGHT : HX_LEFT; + m_SelEndCellSide = m_bCaretLoNibble ? HX_RIGHT : HX_LEFT; + UpdateRealSelection(); +} + +void CHexEditCtrl::SelectAllVisible(void) +{ + uint32_t lastVisibleByteAddress = (m_BaseAddress + m_NumVisibleBytes) - 1; + m_SelStartAddress = m_BaseAddress; + m_SelStartCellSide = HX_LEFT; + m_SelEndAddress = lastVisibleByteAddress; + m_SelEndCellSide = HX_RIGHT; + m_CaretAddress = lastVisibleByteAddress + 1; + m_bCaretLoNibble = false; + UpdateRealSelection(); +} + +bool CHexEditCtrl::IsCaretAddressVisible(void) +{ + return m_CaretAddress >= m_BaseAddress && m_CaretAddress <= SatAdd32(m_BaseAddress, m_NumVisibleBytes); +} + +uint32_t CHexEditCtrl::LineAddress(uint32_t address) +{ + return address - ((address - (m_BaseAddress % m_NumBytesPerRow)) % m_NumBytesPerRow); +} + +void CHexEditCtrl::EnsureCaretAddressVisible(bool bTop) +{ + uint32_t oldBaseAddress = m_BaseAddress; + uint32_t caretLineAddress = LineAddress(m_CaretAddress); + uint32_t lastVisibleLineAddress = m_BaseAddress + (m_NumVisibleBytes - m_NumBytesPerRow); + + if (bTop || caretLineAddress < m_BaseAddress) + { + m_BaseAddress = caretLineAddress; + } + else if (caretLineAddress >= lastVisibleLineAddress + m_NumBytesPerRow) + { + m_BaseAddress = SatAdd32(m_BaseAddress, caretLineAddress - lastVisibleLineAddress); + } + + if (oldBaseAddress != m_BaseAddress) + { + Notify(HXN_BASEADDRCHANGED); + } +} + +LRESULT CHexEditCtrl::Notify(UINT code) +{ + UINT_PTR nID = ::GetDlgCtrlID(m_hWnd); + NMHDR nmh = { m_hWnd, nID, code }; + return ::SendMessage(GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nmh); +} + +LRESULT CHexEditCtrl::NotifySetByte(uint32_t address, uint8_t value) +{ + UINT_PTR nID = ::GetDlgCtrlID(m_hWnd); + NMHXSETBYTE nmsb = { { m_hWnd, nID, HXN_SETBYTE }, m_bInsertMode, address, value }; + return ::SendMessage(GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nmsb); +} + +LRESULT CHexEditCtrl::NotifySetNibble(uint32_t address, bool bLoNibble, uint8_t value) +{ + UINT_PTR nID = ::GetDlgCtrlID(m_hWnd); + NMHXSETNIBBLE nmsn = { { m_hWnd, nID, HXN_SETNIBBLE }, m_bInsertMode, address, bLoNibble, value }; + return ::SendMessage(GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nmsn); +} + +LRESULT CHexEditCtrl::NotifyFillRange(uint32_t startAddress, uint32_t endAddress, uint8_t value) +{ + UINT_PTR nID = ::GetDlgCtrlID(m_hWnd); + NMHXFILLRANGE nmfr = { { m_hWnd, nID, HXN_FILLRANGE }, m_bInsertMode, startAddress, endAddress, value }; + return ::SendMessage(GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nmfr); +} + +LRESULT CHexEditCtrl::NotifyCtrlKeyPressed(int nChar) +{ + UINT_PTR nID = ::GetDlgCtrlID(m_hWnd); + NMHXCTRLKEYPRESSED nmck = { { m_hWnd, nID, HXN_CTRLKEYPRESSED }, nChar }; + return ::SendMessage(GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nmck); +} + +LRESULT CHexEditCtrl::NotifyPaste(uint32_t address) +{ + UINT_PTR nID = ::GetDlgCtrlID(m_hWnd); + NMHXPASTE nmp = { { m_hWnd, nID, HXN_PASTE }, address, m_FocusedColumn }; + return ::SendMessage(GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nmp); +} + +LRESULT CHexEditCtrl::NotifyRightClick(uint32_t address) +{ + UINT_PTR nID = ::GetDlgCtrlID(m_hWnd); + NMHXRCLICK nmrc = { { m_hWnd, nID, HXN_RCLICK }, address }; + return ::SendMessage(GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nmrc); +} + +LRESULT CHexEditCtrl::NotifyGetByteInfo(uint32_t address, size_t numBytes, bool bIgnoreDiff, HXBYTEINFO* oldBytes, HXBYTEINFO* newBytes) +{ + UINT_PTR nID = ::GetDlgCtrlID(m_hWnd); + NMHXGETBYTEINFO nmgbi = { { m_hWnd, nID, HXN_GETBYTEINFO }, address, numBytes, bIgnoreDiff, oldBytes, newBytes }; + return ::SendMessage(GetParent(), WM_NOTIFY, nmgbi.nmh.idFrom, (LPARAM)&nmgbi); +} + +BOOL CHexEditCtrl::OnSetCursor(CWindow /*wnd*/, UINT /*nHitTest*/, UINT /*message*/) +{ + CPoint point(::GetMessagePos()); + ScreenToClient(&point); + + HXHITTEST ht; + HitTest(point.x, point.y, &ht); + + if (ht.column == HX_COL_HEXDATA || ht.column == HX_COL_ASCII) + { + SetCursor(m_hCursorIBeam); + } + else + { + SetCursor(m_hCursorDefault); + } + return FALSE; +} + +void CHexEditCtrl::ShowCaret(void) +{ + if (!m_bCaretVisible) + { + ::ShowCaret(m_hWnd); + m_bCaretVisible = true; + } +} + +void CHexEditCtrl::HideCaret(void) +{ + if (m_bCaretVisible) + { + ::HideCaret(m_hWnd); + m_bCaretVisible = false; + } +} + +uint32_t CHexEditCtrl::SatAdd32(uint32_t a, int b) +{ + int64_t c = (int64_t)a + b; + if (c > UINT_MAX) + { + return UINT_MAX; + } + if (c < 0) + { + return 0; + } + return (uint32_t)c; +} + +uint32_t CHexEditCtrl::SatAdd32(uint32_t a, uint32_t b) +{ + uint32_t c = a + b; + if (c < a) + { + return (uint32_t)-1; + } + return c; +} + +COLORREF CHexEditCtrl::BlendColor(COLORREF c1, COLORREF c2) +{ + int r1 = GetRValue(c1); + int g1 = GetGValue(c1); + int b1 = GetBValue(c1); + int r2 = GetRValue(c2); + int g2 = GetGValue(c2); + int b2 = GetBValue(c2); + return RGB((r1+r2*2)/3, (g1+g2*2)/3, (b1+b2*2)/3); +} + +void CHexEditCtrl::UpdateLayoutInfo(void) +{ + CRect clientRect; + GetClientRect(&clientRect); + + int addressColumnWidth = 11 * m_CharWidth; + int byteWidth = m_CharWidth * 2; + int byteGroupWidth = (m_NumBytesPerGroup * byteWidth) + (m_CharWidth * 1); + int asciiGroupWidth = (m_NumBytesPerGroup * m_CharWidth); + int headerHeight = m_CharHeight; + + m_NumByteGroupsPerRow = (clientRect.Width() - addressColumnWidth) / (byteGroupWidth + asciiGroupWidth); + m_NumBytesPerRow = m_NumByteGroupsPerRow * m_NumBytesPerGroup; + m_NumVisibleRows = (clientRect.Height() - headerHeight) / m_CharHeight; + m_NumVisibleBytes = m_NumVisibleRows * m_NumBytesPerRow; + + int hexDataColumnWidth = m_NumByteGroupsPerRow * byteGroupWidth; + int asciiColumnWidth = m_NumBytesPerRow * m_CharWidth; + + int addressColumnLeft = 0; + int addressColumnRight = addressColumnLeft + addressColumnWidth; + int hexDataColumnLeft = addressColumnRight; + int hexDataColumnRight = hexDataColumnLeft + hexDataColumnWidth; + int asciiColumnLeft = hexDataColumnRight; + int asciiColumnRight = asciiColumnLeft + asciiColumnWidth; + + int columnsTop = 0 + headerHeight; + int columnsBottom = columnsTop + m_NumVisibleRows * m_CharHeight; + + m_AddressColumnRect = { addressColumnLeft, columnsTop, addressColumnRight, columnsBottom }; + m_HexDataColumnRect = { hexDataColumnLeft, columnsTop, hexDataColumnRight, columnsBottom }; + m_AsciiColumnRect = { asciiColumnLeft, columnsTop, asciiColumnRight, columnsBottom }; + + m_bLayoutChanged = true; +} + +void CHexEditCtrl::ReallocByteBuffers(void) +{ + m_NewBytes = (HXBYTEINFO*)realloc(m_NewBytes, m_NumVisibleBytes * sizeof(HXBYTEINFO)); + m_OldBytes = (HXBYTEINFO*)realloc(m_OldBytes, m_NumVisibleBytes * sizeof(HXBYTEINFO)); + + for (int i = 0; i < m_NumVisibleBytes; i++) + { + m_NewBytes[i] = { 0 }; + m_OldBytes[i] = { 0 }; + } +} + +void CHexEditCtrl::OnWindowPosChanged(LPWINDOWPOS /*lpWndPos*/) +{ + int oldNumRows = m_NumVisibleRows; + int oldNumGroups = m_NumByteGroupsPerRow; + + UpdateLayoutInfo(); + + if (oldNumRows != m_NumVisibleRows || oldNumGroups != m_NumByteGroupsPerRow) + { + ReallocByteBuffers(); + + CRect rc; + GetClientRect(&rc); + m_BackBMP = CreateCompatibleBitmap(m_BackDC, rc.Width(), rc.Height()); + HBITMAP hOldBMP = (HBITMAP)SelectObject(m_BackDC, m_BackBMP); + DeleteObject(hOldBMP); + + CRect clrRc(0, 0, rc.Width(), rc.Height()); + HBRUSH hbrush = CreateSolidBrush(BKCOLOR_DEFAULT); + FillRect(m_BackDC, clrRc, hbrush); + DeleteObject(hbrush); + + Draw(); + DrawAddressColumn(); + DrawHeader(); + } +} + +void CHexEditCtrl::SetByteGroupSize(int nBytes) +{ + m_NumBytesPerGroup = nBytes; + Notify(HXN_GROUPSIZECHANGED); + + UpdateLayoutInfo(); + ReallocByteBuffers(); + + CRect rc; + GetClientRect(&rc); + CRect clrRc(0, 0, rc.Width(), rc.Height()); + HBRUSH hbrush = CreateSolidBrush(BKCOLOR_DEFAULT); + FillRect(m_BackDC, clrRc, hbrush); + DeleteObject(hbrush); + + Draw(); + DrawAddressColumn(); + DrawHeader(); + + int addressColumnWidth = 11 * m_CharWidth; + int headerHeight = m_CharHeight; + CRect rcInv = { addressColumnWidth, headerHeight, rc.Width(), rc.Height() }; + InvalidateRect(&rcInv, true); +} diff --git a/Source/Project64/UserInterface/WTLControls/HexEditCtrl.h b/Source/Project64/UserInterface/WTLControls/HexEditCtrl.h new file mode 100644 index 000000000..4870a2620 --- /dev/null +++ b/Source/Project64/UserInterface/WTLControls/HexEditCtrl.h @@ -0,0 +1,309 @@ +#pragma once +#include + +enum +{ + HXN_REDRAWSTARTED, + HXN_GETBYTEINFO, + HXN_SETBYTE, + HXN_SETNIBBLE, + HXN_FILLRANGE, + HXN_RCLICK, + HXN_INSERTMODECHANGED, + HXN_HOTADDRCHANGED, + HXN_BASEADDRCHANGED, + HXN_GROUPSIZECHANGED, + HXN_SELCHANGED, + HXN_CTRLKEYPRESSED, + HXN_ENTERPRESSED, + HXN_COPY, + HXN_PASTE, +}; + +enum HXCOLUMN +{ + HX_COL_NONE = -1, + HX_COL_ADDRESS, + HX_COL_HEXDATA, + HX_COL_ASCII +}; + +typedef struct HXBYTEINFO_S +{ + bool bHidden; + bool bValid; + uint8_t value; + COLORREF color; + COLORREF bkColor; + + bool operator==(const HXBYTEINFO_S& b) const + { + return memcmp(this, &b, sizeof(HXBYTEINFO_S)) == 0; + } + + bool operator!=(const HXBYTEINFO_S& b) const + { + return memcmp(this, &b, sizeof(HXBYTEINFO_S)) != 0; + } +} HXBYTEINFO; + +template<> +struct std::hash +{ + std::size_t operator()(const HXBYTEINFO& k) const + { + return (size_t)(k.bValid * 0xFFFFFFFF) ^ (k.value * 0x1010101) ^ k.color ^ k.bkColor; + } +}; + +typedef struct +{ + NMHDR nmh; + uint32_t address; + size_t numBytes; + bool bIgnoreDiff; + HXBYTEINFO* oldBytes; + HXBYTEINFO* newBytes; +} NMHXGETBYTEINFO; + +typedef struct +{ + NMHDR nmh; + bool bInsert; + uint32_t address; + uint8_t value; +} NMHXSETBYTE; + +typedef struct +{ + NMHDR nmh; + bool bInsert; + uint32_t address; + bool bLoNibble; + uint8_t value; +} NMHXSETNIBBLE; + +typedef struct +{ + NMHDR nmh; + bool bInsert; + uint32_t startAddress; + uint32_t endAddress; + uint8_t value; +} NMHXFILLRANGE; + +typedef struct +{ + NMHDR nmh; + uint32_t address; + uint32_t length; + uint8_t* data; +} NMHXSETBYTES; + +typedef struct +{ + NMHDR nmh; + uint32_t address; +} NMHXRCLICK; + +typedef struct +{ + NMHDR nmh; + uint32_t address; + HXCOLUMN column; +} NMHXPASTE; + +typedef struct +{ + NMHDR nmh; + int nChar; +} NMHXCTRLKEYPRESSED; + +class CHexEditCtrl : + public CWindowImpl +{ +public: + CHexEditCtrl(void); + ~CHexEditCtrl(void); + DECLARE_WND_CLASS(_T("HexEditCtrl")) + BOOL Attach(HWND hWnd); + HWND Detach(void); + + static char ByteAscii(uint8_t value); + static uint8_t HexCharValue(char c); + static int CALLBACK HaveFontCb(CONST LOGFONTA *lplf, CONST TEXTMETRICA *lptm, DWORD FontType, LPARAM lParam); + static bool HaveFont(HDC hdc, const char* name); + + void Draw(void); + + void Copy(void); + void Paste(bool bAdvanceCaret = true); + + void SetBaseAddress(uint32_t address); + void SetByteGroupSize(int nBytes); + + uint32_t GetBaseAddress(void); + uint32_t GetCaretAddress(void); + uint32_t GetHotAddress(void); + int GetNumBytesPerGroup(void); + int GetNumBytesPerRow(void); + int GetNumVisibleBytes(void); + bool GetSelectionRange(uint32_t* startAddress, uint32_t* endAddress); + HXCOLUMN GetFocusedColumn(void); + bool GetInsertMode(void); + +private: + enum HXCELLSIDE + { + HX_LEFT, + HX_RIGHT + }; + + enum + { + TIMER_ID_AUTO_REFRESH, + TIMER_ID_DRAG_SCROLL + }; + + enum + { + BKCOLOR_DEFAULT = RGB(255, 255, 255), + BKCOLOR_ADDR = RGB(220, 220, 220), + COLOR_ADDR = RGB(40, 40, 40), + BKCOLOR_SEL_FOCUSED = RGB(51, 153, 255), + COLOR_SEL_FOCUSED = RGB(255, 255, 255), + BKCOLOR_SEL_UNFOCUSED = RGB(200, 200, 200), + COLOR_SEL_UNFOCUSED = RGB(0, 0, 0), + BKCOLOR_HOT = RGB(140, 140, 140) + }; + + typedef struct + { + HXCOLUMN column; + uint32_t asciiAddress; + HXCELLSIDE asciiCellSide; + uint32_t hexAddress; + HXCELLSIDE hexCellSide; + } HXHITTEST; + + typedef struct + { + CRect rcHex; + CRect rcAscii; + } HXRECTPAIR; + + uint32_t m_BaseAddress; + uint32_t m_DrawnBaseAddress; + uint32_t m_SelStartAddress; + HXCELLSIDE m_SelStartCellSide; + uint32_t m_SelEndAddress; + HXCELLSIDE m_SelEndCellSide; + uint32_t m_RealSelStartAddress; + uint32_t m_RealSelEndAddress; + bool m_bHaveRealSel; + bool m_bInsertMode; + bool m_bHaveCaret; + bool m_bCaretVisible; + uint32_t m_CaretAddress; + bool m_bCaretLoNibble; + bool m_bShowHotAddress; + uint32_t m_HotAddress; + HXCOLUMN m_FocusedColumn; + HFONT m_Font; + HBITMAP m_BackBMP; + HDC m_BackDC; + HCURSOR m_hCursorIBeam; + HCURSOR m_hCursorDefault; + int m_DragScrollDelta; + bool m_bDblClicked; + bool m_bLButtonDown; + bool m_bMouseDragging; + bool m_bLayoutChanged; + int m_CharWidth; + int m_CharHeight; + CRect m_AddressColumnRect; + CRect m_HexDataColumnRect; + CRect m_AsciiColumnRect; + int m_NumBytesPerGroup; + int m_NumByteGroupsPerRow; + int m_NumBytesPerRow; + int m_NumVisibleRows; + int m_NumVisibleBytes; + HXBYTEINFO* m_NewBytes; + HXBYTEINFO* m_OldBytes; + + static COLORREF BlendColor(COLORREF c1, COLORREF c2); + static uint32_t SatAdd32(uint32_t a, uint32_t b); + static uint32_t SatAdd32(uint32_t a, int b); + + void DrawAddressColumn(void); + void DrawHeader(void); + void Text(int x, int y, const char *text, COLORREF bg, COLORREF fg, CRect* rcOut); + bool IsSelected(uint32_t address); + int GetSelDirection(void); + void CancelSelection(void); + void SelectAllVisible(void); + void UpdateRealSelection(void); + + uint32_t LineAddress(uint32_t address); + + void GetHexCellPos(int index, CRect* rc); + void GetAsciiCellPos(int index, CRect* rc); + void HitTest(int x, int y, HXHITTEST* pht); + + void ShowCaret(void); + void HideCaret(void); + void CaretIncrementNibble(void); + void CaretDecrementNibble(void); + bool UpdateCaretUI(bool bEnsureVisible, bool bTop = false); + void EnsureCaretAddressVisible(bool bTop = false); + bool IsCaretAddressVisible(void); + + void UpdateLayoutInfo(void); + void ReallocByteBuffers(void); + + LRESULT Notify(UINT code); + LRESULT NotifyGetByteInfo(uint32_t address, size_t numBytes, bool bIgnoreDiff, HXBYTEINFO* oldBytes, HXBYTEINFO* newBytes); + LRESULT NotifySetByte(uint32_t address, uint8_t value); + LRESULT NotifySetNibble(uint32_t address, bool bLoNibble, uint8_t value); + LRESULT NotifyFillRange(uint32_t startAddress, uint32_t endAddress, uint8_t value); + LRESULT NotifyCtrlKeyPressed(int nChar); + LRESULT NotifyPaste(uint32_t address); + LRESULT NotifyRightClick(uint32_t address); + + void OnLButtonDown(UINT nFlags, CPoint point); + void OnLButtonDblClk(UINT nFlags, CPoint point); + void OnLButtonUp(UINT nFlags, CPoint point); + void OnRButtonDown(UINT nFlags, CPoint point); + void OnRButtonUp(UINT nFlags, CPoint point); + void OnMouseMove(UINT nFlags, CPoint point); + BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); + void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); + void OnSetFocus(CWindow wndOld); + void OnKillFocus(CWindow wndFocus); + void OnTimer(UINT_PTR nIDEvent); + UINT OnGetDlgCode(LPMSG lpMsg); + void OnPaint(CDCHandle dc); + BOOL OnSetCursor(CWindow wnd, UINT nHitTest, UINT message); + void OnWindowPosChanged(LPWINDOWPOS lpWndPos); + + BEGIN_MSG_MAP_EX(CHexEditCtrl) + MSG_WM_RBUTTONDOWN(OnRButtonDown) + MSG_WM_LBUTTONDOWN(OnLButtonDown) + MSG_WM_LBUTTONUP(OnLButtonUp) + MSG_WM_RBUTTONUP(OnRButtonUp) + MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk) + MSG_WM_MOUSEMOVE(OnMouseMove) + MSG_WM_MOUSEWHEEL(OnMouseWheel) + MSG_WM_KEYDOWN(OnKeyDown) + MSG_WM_CHAR(OnChar) + MSG_WM_SETFOCUS(OnSetFocus) + MSG_WM_KILLFOCUS(OnKillFocus) + MSG_WM_TIMER(OnTimer) + MSG_WM_GETDLGCODE(OnGetDlgCode) + MSG_WM_PAINT(OnPaint) + MSG_WM_SETCURSOR(OnSetCursor) + MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged) + END_MSG_MAP() +}; diff --git a/Source/Project64/UserInterface/resource.h b/Source/Project64/UserInterface/resource.h index 08b0cd062..3fc3dd405 100644 --- a/Source/Project64/UserInterface/resource.h +++ b/Source/Project64/UserInterface/resource.h @@ -91,10 +91,8 @@ #define RSP_ABOUT 1006 #define IDC_ASSIGN 1006 #define IDC_BTN_CHOOSE_FILE 1006 -#define IDC_DUMP_MEM 1006 #define IDC_INFO_FILENAME 1007 #define IDC_BTN_RDRAM 1007 -#define IDC_REFRSH_MEM 1007 #define IDC_INFO_ROMNAME 1008 #define IDC_RESET_PAGE 1008 #define IDC_INFO_CARTID 1009 @@ -194,7 +192,6 @@ #define IDC_DIRECT_READ 1057 #define IDC_DMA_READ 1058 #define IDC_CONT_PAK 1059 -#define IDC_SEARCH_MEM 1059 #define IDC_BTN_ROM 1061 #define IDC_TEXTURE_OTHER 1062 #define IDC_TEXTURE_DIR 1063 @@ -202,7 +199,6 @@ #define IDC_TEXTURE_DEFAULT 1064 #define IDC_LST_RESULTS 1064 #define IDC_SELECT_TEXTURE_DIR 1065 -#define IDC_MEM_DETAILS 1065 #define IDC_PLUGIN_OTHER 1066 #define IDC_PLUGIN_DIR 1067 #define IDC_PLUGIN_DEFAULT 1068 @@ -508,9 +504,7 @@ #define IDC_COP0_17_EDIT 1332 #define IDC_COP0_18_EDIT 1333 #define IDC_FILTER_STATIC 1339 -#define IDC_SYM_INFO 1348 #define IDC_BLOCK_INFO 1350 -#define IDC_DMA_INFO 1351 #define IDC_BACK_BTN 1352 #define IDC_FORWARD_BTN 1353 #define IDC_PC_EDIT 1354 @@ -604,7 +598,6 @@ #define IDC_DD44_EDIT 1443 #define IDC_DD48_EDIT 1444 #define IDC_ROM_FIXEDAUDIO 1445 -#define IDC_CHK_AUTOREFRESH 1446 #define IDC_SHOW_FILE_EXTENSIONS 1447 #define IDC_ENHANCEMENTLIST 1450 #define IDC_OVERCLOCK 1451 @@ -718,6 +711,10 @@ #define IDC_CHK_FP_CO 1566 #define IDC_CHK_FP_CU 1567 #define IDC_CHK_FP_CI 1568 +#define IDC_HEXEDIT 1569 +#define IDC_MEMTABS 1571 +#define IDC_STATUSBAR 1572 +#define IDC_CMB_JUMP 1573 #define ID_POPUPMENU_PLAYGAMEWITHDISK 40008 #define ID_POPUPMENU_ADDSYMBOL 40013 #define ID_POPUPMENU_VIEWDISASM 40017 @@ -757,9 +754,21 @@ #define ID_RESULTS_ADDALLTOWATCHLIST 40080 #define ID_WATCHLIST_CHANGE_ADDRESS 40082 #define ID_WATCHLIST_CHANGE_ADDRESSBY 40084 -#define ID_POPUPMENU_COPY_WORD 40089 -#define ID_POPUPMENU_COPY_HALFWORD 40090 -#define ID_POPUPMENU_COPY_BYTE 40091 +#define ID_POPUPMENU_PASTE 40092 +#define ID_POPUPMENU_COPY 40093 +#define ID_POPUPMENU_FOLLOWPOINTER 40095 +#define ID_POPUPMENU_ZEROFILL 40097 +#define ID_POPUPMENU_BYTEGROUPSIZE_1 40103 +#define ID_POPUPMENU_BYTEGROUPSIZE_2 40104 +#define ID_POPUPMENU_BYTEGROUPSIZE_4 40105 +#define ID_POPUPMENU_BYTEGROUPSIZE_8 40106 +#define ID_POPUPMENU_SAFEMODE 40107 +#define ID_POPUPMENU_DUMP 40108 +#define ID_POPUPMENU_SEARCH 40109 +#define ID_POPUPMENU_JUMPHERE 40113 +#define ID_POPUPMENU_COPYGAMESHARKCODE 40118 +#define ID_POPUPMENU_COPYDATAWITHROWADDRESSES 40119 +#define ID_POPUPMENU_COPYDATAWITHGROUPADDRESSES 40120 #define ID_POPUPMENU_ROMDIRECTORY 40137 #define ID_POPUPMENU_REFRESHROMLIST 40138 #define ID_POPUPMENU_PLAYGAME 40152 @@ -777,8 +786,8 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 219 -#define _APS_NEXT_COMMAND_VALUE 40092 -#define _APS_NEXT_CONTROL_VALUE 1569 +#define _APS_NEXT_COMMAND_VALUE 40121 +#define _APS_NEXT_CONTROL_VALUE 1574 #define _APS_NEXT_SYMED_VALUE 102 #endif #endif