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