diff --git a/HexCtrl/res/HexCtrl.rc b/HexCtrl/res/HexCtrl.rc index 07761f90..d42767cf 100644 --- a/HexCtrl/res/HexCtrl.rc +++ b/HexCtrl/res/HexCtrl.rc @@ -51,30 +51,31 @@ END // Dialog // -IDD_HEXCTRL_SEARCH DIALOGEX 0, 0, 351, 188 +IDD_HEXCTRL_SEARCH DIALOGEX 0, 0, 357, 187 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_LAYERED CAPTION "Search and Replace..." FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - COMBOBOX IDC_HEXCTRL_SEARCH_COMBO_SEARCH,71,16,170,30,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_HEXCTRL_SEARCH_COMBO_REPLACE,71,32,170,30,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "<< Search",IDC_HEXCTRL_SEARCH_BTN_SEARCH_B,248,16,41,14 - DEFPUSHBUTTON "Search >>",IDC_HEXCTRL_SEARCH_BTN_SEARCH_F,291,16,41,14 - PUSHBUTTON "Replace",IDC_HEXCTRL_SEARCH_BTN_REPLACE,248,46,41,14 - PUSHBUTTON "Replace All",IDC_HEXCTRL_SEARCH_BTN_REPLACE_ALL,291,47,41,14 + COMBOBOX IDC_HEXCTRL_SEARCH_COMBO_SEARCH,71,16,179,30,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_HEXCTRL_SEARCH_COMBO_REPLACE,71,32,179,30,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "<< Search",IDC_HEXCTRL_SEARCH_BTN_SEARCH_B,258,16,41,14 + DEFPUSHBUTTON "Search >>",IDC_HEXCTRL_SEARCH_BTN_SEARCH_F,301,16,41,14 + PUSHBUTTON "Replace",IDC_HEXCTRL_SEARCH_BTN_REPLACE,258,46,41,14 + PUSHBUTTON "Replace All",IDC_HEXCTRL_SEARCH_BTN_REPLACE_ALL,301,47,41,14 LTEXT "",IDC_HEXCTRL_SEARCH_STATIC_TEXTBOTTOM,10,176,280,8 LTEXT "Find:",IDC_STATIC,51,19,17,8 LTEXT "Replace with:",IDC_STATIC,24,35,44,8 - CONTROL "Selection",IDC_HEXCTRL_SEARCH_CHECK_SELECTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,48,43,10 + CONTROL "In selection",IDC_HEXCTRL_SEARCH_CHECK_SELECTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,203,48,47,8 COMBOBOX IDC_HEXCTRL_SEARCH_COMBO_MODE,71,48,82,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Search mode:",IDC_STATIC,23,51,44,8 - CONTROL "",IDC_HEXCTRL_SEARCH_LIST_MAIN,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP,7,81,337,93 - PUSHBUTTON "Find All",IDC_HEXCTRL_SEARCH_BTN_FINDALL,248,31,84,14 + LTEXT "Search mode:",IDC_STATIC,23,50,44,8 + CONTROL "",IDC_HEXCTRL_SEARCH_LIST_MAIN,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP,7,81,343,93 + PUSHBUTTON "Find All",IDC_HEXCTRL_SEARCH_BTN_FINDALL,258,31,84,14 EDITTEXT IDC_HEXCTRL_SEARCH_EDIT_START,71,64,82,12,ES_AUTOHSCROLL LTEXT "Start search at:",IDC_STATIC,17,66,52,8 - EDITTEXT IDC_HEXCTRL_SEARCH_EDIT_STEP,182,64,59,12,ES_AUTOHSCROLL + EDITTEXT IDC_HEXCTRL_SEARCH_EDIT_STEP,182,64,68,12,ES_AUTOHSCROLL,WS_EX_RIGHT LTEXT "Step:",IDC_STATIC,160,66,18,8 + CONTROL "Wildcard",IDC_HEXCTRL_SEARCH_CHECK_WILDCARD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,159,48,38,8 END IDD_HEXCTRL_ABOUT DIALOGEX 0, 0, 187, 65 @@ -257,9 +258,9 @@ BEGIN IDD_HEXCTRL_SEARCH, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 344 + RIGHTMARGIN, 350 TOPMARGIN, 7 - BOTTOMMARGIN, 187 + BOTTOMMARGIN, 186 END IDD_HEXCTRL_ABOUT, DIALOG diff --git a/HexCtrl/res/HexCtrlRes.h b/HexCtrl/res/HexCtrlRes.h index e827a5a1..5fbf7f32 100644 --- a/HexCtrl/res/HexCtrlRes.h +++ b/HexCtrl/res/HexCtrlRes.h @@ -90,6 +90,8 @@ #define IDC_HEXCTRL_GOTO_STATIC_OFFRANGE 9268 #define IDC_HEXCTRL_GOTO_STATIC_PAGETOTAL 9269 #define IDC_HEXCTRL_GOTO_STATIC_PAGERANGE 9270 +#define IDC_CHECK1 9271 +#define IDC_HEXCTRL_SEARCH_CHECK_WILDCARD 9271 #define IDM_HEXCTRL_DLG_SEARCH 32769 #define IDM_HEXCTRL_MODIFY_UNDO 32770 #define IDM_HEXCTRL_MODIFY_REDO 32771 @@ -143,7 +145,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 9271 #define _APS_NEXT_COMMAND_VALUE 32815 -#define _APS_NEXT_CONTROL_VALUE 9271 +#define _APS_NEXT_CONTROL_VALUE 9272 #define _APS_NEXT_SYMED_VALUE 9333 #endif #endif diff --git a/HexCtrl/src/CHexCtrl.cpp b/HexCtrl/src/CHexCtrl.cpp index d481d6be..83eef3a6 100644 --- a/HexCtrl/src/CHexCtrl.cpp +++ b/HexCtrl/src/CHexCtrl.cpp @@ -4203,10 +4203,6 @@ void CHexCtrl::OnChar(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/) if (!m_fMutable || !IsCurTextArea() || (GetKeyState(VK_CONTROL) < 0)) return; - SMODIFY hms; - hms.vecSpan.emplace_back(HEXSPANSTRUCT { m_ullCaretPos, 1 }); - hms.ullDataSize = 1; - BYTE chByte = nChar & 0xFF; WCHAR warrCurrLocaleID[KL_NAMELENGTH]; GetKeyboardLayoutNameW(warrCurrLocaleID); //Current langID as wstring. @@ -4225,6 +4221,9 @@ void CHexCtrl::OnChar(UINT nChar, UINT /*nRepCnt*/, UINT /*nFlags*/) } } + SMODIFY hms; + hms.vecSpan.emplace_back(HEXSPANSTRUCT { m_ullCaretPos, 1 }); + hms.ullDataSize = 1; hms.pData = reinterpret_cast(&chByte); Modify(hms); CaretMoveRight(); @@ -4276,6 +4275,7 @@ void CHexCtrl::OnDestroy() m_pDlgEncoding->DestroyWindow(); m_pDlgOpers->DestroyWindow(); m_pDlgSearch->DestroyWindow(); + m_pDlgGoTo->DestroyWindow(); m_pScrollV->DestroyWindow(); m_pScrollH->DestroyWindow(); @@ -4374,34 +4374,32 @@ void CHexCtrl::OnKeyDown(UINT nChar, UINT /*nRepCnt*/, UINT nFlags) if (auto optCmd = GetCommand(static_cast(nChar), GetAsyncKeyState(VK_CONTROL) < 0, GetAsyncKeyState(VK_SHIFT) < 0, GetAsyncKeyState(VK_MENU) < 0); optCmd) ExecuteCmd(optCmd.value()); - else if (m_fMutable) + else if (IsMutable() && !IsCurTextArea()) //If caret is in Hex area, just one part (High/Low) of byte must be changed. { - SMODIFY hms; - hms.vecSpan.emplace_back(HEXSPANSTRUCT { m_ullCaretPos, 1 }); - hms.ullDataSize = 1; - BYTE chByte = nChar & 0xFF; - if (!IsCurTextArea()) //If cursor is not in Ascii area then just one part (High/Low) of byte must be changed. - { - if (chByte >= 0x30 && chByte <= 0x39) //Digits. - chByte -= 0x30; - else if (chByte >= 0x41 && chByte <= 0x46) //Hex letters uppercase. - chByte -= 0x37; - else if (chByte >= 0x61 && chByte <= 0x66) //Hex letters lowercase. - chByte -= 0x57; - else - return; + //Normalizing all input in Hex area, reducing it to 0-15 (0x0-F) digit range. + //Allowing only [0-9][A-F][NUM0-NUM9]. + if (chByte >= 0x30 && chByte <= 0x39) //Digits [0-9]. + chByte -= 0x30; + else if (chByte >= 0x41 && chByte <= 0x46) //Hex letters [A-F]. + chByte -= 0x37; + else if (chByte >= 0x60 && chByte <= 0x69) //VK_NUMPAD0 - VK_NUMPAD9 [NUM0-NUM9]. + chByte -= 0x60; + else + return; - auto chByteCurr = GetData(m_ullCaretPos); - if (m_fCursorHigh) - chByte = (chByte << 4) | (chByteCurr & 0x0F); - else - chByte = (chByte & 0x0F) | (chByteCurr & 0xF0); + auto chByteCurr = GetData(m_ullCaretPos); + if (m_fCursorHigh) + chByte = (chByte << 4) | (chByteCurr & 0x0F); + else + chByte = (chByte & 0x0F) | (chByteCurr & 0xF0); - hms.pData = reinterpret_cast(&chByte); - Modify(hms); - CaretMoveRight(); - } + SMODIFY hms; + hms.vecSpan.emplace_back(HEXSPANSTRUCT { m_ullCaretPos, 1 }); + hms.ullDataSize = 1; + hms.pData = reinterpret_cast(&chByte); + Modify(hms); + CaretMoveRight(); } m_optRMouseClick.reset(); //Reset right mouse click. diff --git a/HexCtrl/src/Dialogs/CHexDlgSearch.cpp b/HexCtrl/src/Dialogs/CHexDlgSearch.cpp index 3cdd3bb4..4d3c47f7 100644 --- a/HexCtrl/src/Dialogs/CHexDlgSearch.cpp +++ b/HexCtrl/src/Dialogs/CHexDlgSearch.cpp @@ -42,6 +42,7 @@ void CHexDlgSearch::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_HEXCTRL_SEARCH_CHECK_SELECTION, m_stCheckSel); + DDX_Control(pDX, IDC_HEXCTRL_SEARCH_CHECK_WILDCARD, m_stCheckWcard); DDX_Control(pDX, IDC_HEXCTRL_SEARCH_COMBO_SEARCH, m_stComboSearch); DDX_Control(pDX, IDC_HEXCTRL_SEARCH_COMBO_REPLACE, m_stComboReplace); DDX_Control(pDX, IDC_HEXCTRL_SEARCH_COMBO_MODE, m_stComboMode); @@ -66,24 +67,24 @@ BOOL CHexDlgSearch::OnInitDialog() m_stBrushDefault.CreateSolidBrush(m_clrBkTextArea); - auto iIndex = m_stComboMode.AddString(L"Hex"); + auto iIndex = m_stComboMode.AddString(L"Hex Bytes"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_HEX)); m_stComboMode.SetCurSel(iIndex); - iIndex = m_stComboMode.AddString(L"Text (ASCII)"); + iIndex = m_stComboMode.AddString(L"ASCII Text"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_ASCII)); - iIndex = m_stComboMode.AddString(L"Text (UTF-16)"); + iIndex = m_stComboMode.AddString(L"UTF-16 Text"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_WCHAR)); - iIndex = m_stComboMode.AddString(L"Number (BYTE)"); + iIndex = m_stComboMode.AddString(L"Byte (std::(u)int8_t)"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_BYTE)); - iIndex = m_stComboMode.AddString(L"Number (WORD)"); + iIndex = m_stComboMode.AddString(L"Short (std::(u)int16_t)"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_WORD)); - iIndex = m_stComboMode.AddString(L"Number (DWORD)"); + iIndex = m_stComboMode.AddString(L"Int (std::(u)int32_t)"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_DWORD)); - iIndex = m_stComboMode.AddString(L"Number (QWORD)"); + iIndex = m_stComboMode.AddString(L"Int64 (std::(u)int64_t)"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_QWORD)); - iIndex = m_stComboMode.AddString(L"Number (Float)"); + iIndex = m_stComboMode.AddString(L"Float"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_FLOAT)); - iIndex = m_stComboMode.AddString(L"Number (Double)"); + iIndex = m_stComboMode.AddString(L"Double"); m_stComboMode.SetItemData(iIndex, static_cast(EMode::SEARCH_DOUBLE)); m_pListMain->CreateDialogCtrl(IDC_HEXCTRL_SEARCH_LIST_MAIN, this); @@ -99,6 +100,28 @@ BOOL CHexDlgSearch::OnInitDialog() SetEditStep(m_ullStep); + auto hwndTip = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, WS_POPUP | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hWnd, nullptr, nullptr, nullptr); + if (hwndTip == nullptr) + return FALSE; + + TTTOOLINFOW stToolInfo { }; //Wildcard check box tooltip. + stToolInfo.cbSize = sizeof(TTTOOLINFOW); + stToolInfo.hwnd = m_hWnd; + stToolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + stToolInfo.uId = reinterpret_cast(GetDlgItem(IDC_HEXCTRL_SEARCH_CHECK_WILDCARD)->m_hWnd); + static std::wstring wstrToolText { }; //Tooltip text. + wstrToolText += L"Use '"; + wstrToolText += static_cast(m_uWildcard); + wstrToolText += L"' character to match any symbol, or any byte if in `Hex Bytes` search mode.\r\n"; + wstrToolText += L"Example:\r\n"; + wstrToolText += L" Hex Bytes : `11?33` will find 112233, 113333, 114433, 119733, etc...\r\n"; + wstrToolText += L" ASCII Text: `sa??le` will find `sample`, 'saAAle', 'saxale', 'saZZle', etc...\r\n"; + stToolInfo.lpszText = wstrToolText.data(); + ::SendMessageW(hwndTip, TTM_ADDTOOL, 0, reinterpret_cast(&stToolInfo)); + ::SendMessageW(hwndTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, static_cast(LOWORD(0x7FFF))); + ::SendMessageW(hwndTip, TTM_SETMAXTIPWIDTH, 0, static_cast(1000)); + return TRUE; } @@ -200,6 +223,7 @@ BOOL CHexDlgSearch::OnCommand(WPARAM wParam, LPARAM lParam) break; case EMenuID::IDM_SEARCH_CLEARALL: ClearList(); + m_fSecondMatch = false; //To be able to search from the zero offset. break; default: fHere = false; @@ -368,13 +392,14 @@ BOOL CHexDlgSearch::ShowWindow(int nCmdShow) void CHexDlgSearch::PrepareSearch() { auto pHexCtrl = GetHexCtrl(); - auto ullDataSize = pHexCtrl->GetDataSize(); + const auto ullDataSize = pHexCtrl->GetDataSize(); //"Search" text. CStringW wstrTextSearch; m_stComboSearch.GetWindowTextW(wstrTextSearch); if (wstrTextSearch.IsEmpty()) return; + if (wstrTextSearch.Compare(m_wstrTextSearch.data()) != 0) { ResetSearch(); @@ -412,6 +437,7 @@ void CHexDlgSearch::PrepareSearch() ComboReplaceFill(warrReplace); } m_stComboSearch.SetFocus(); + m_fWildcard = m_stCheckWcard.GetCheck() == BST_CHECKED; bool fSuccess { false }; switch (GetSearchMode()) @@ -509,7 +535,7 @@ bool CHexDlgSearch::PrepareHex() { m_strSearch = wstr2str(m_wstrTextSearch); m_strReplace = wstr2str(m_wstrTextReplace); - if (!str2hex(m_strSearch, m_strSearch)) + if (!str2hex(m_strSearch, m_strSearch, m_fWildcard, static_cast(m_uWildcard))) { m_iWrap = 1; MessageBoxW(m_wstrWrongInput.data(), L"Error", MB_OK | MB_ICONERROR | MB_TOPMOST); @@ -544,10 +570,18 @@ bool CHexDlgSearch::PrepareASCII() bool CHexDlgSearch::PrepareWCHAR() { - m_nSizeSearch = m_wstrTextSearch.size() * sizeof(wchar_t); - m_nSizeReplace = m_wstrTextReplace.size() * sizeof(wchar_t); - m_pSearchData = reinterpret_cast(m_wstrTextSearch.data()); - m_pReplaceData = reinterpret_cast(m_wstrTextReplace.data()); + m_wstrSearch = m_wstrTextSearch; + m_wstrReplace = m_wstrTextReplace; + if (m_fWildcard) + { + //Constructing one wchar_t consisting with two m_uWildcard symbols. + const wchar_t wDblWildcard = static_cast(m_uWildcard) << 8 | static_cast(m_uWildcard); + std::replace(m_wstrSearch.begin(), m_wstrSearch.end(), static_cast(m_uWildcard), wDblWildcard); + } + m_nSizeSearch = m_wstrSearch.size() * sizeof(wchar_t); + m_nSizeReplace = m_wstrReplace.size() * sizeof(wchar_t); + m_pSearchData = reinterpret_cast(m_wstrSearch.data()); + m_pReplaceData = reinterpret_cast(m_wstrReplace.data()); return true; } @@ -555,14 +589,14 @@ bool CHexDlgSearch::PrepareWCHAR() bool CHexDlgSearch::PrepareBYTE() { BYTE bData { }; - BYTE bDataRet { }; - if (!wstr2num(m_wstrTextSearch, bData) || (m_fReplace && !wstr2num(m_wstrTextReplace, bDataRet))) + BYTE bDataRep { }; + if (!wstr2num(m_wstrTextSearch, bData) || (m_fReplace && !wstr2num(m_wstrTextReplace, bDataRep))) { MessageBoxW(m_wstrWrongInput.data(), L"Error", MB_OK | MB_ICONERROR | MB_TOPMOST); return false; } m_strSearch = bData; - m_strReplace = bDataRet; + m_strReplace = bDataRep; m_nSizeSearch = m_strSearch.size(); m_nSizeReplace = m_strReplace.size(); m_pSearchData = reinterpret_cast(m_strSearch.data()); @@ -574,15 +608,15 @@ bool CHexDlgSearch::PrepareBYTE() bool CHexDlgSearch::PrepareWORD() { WORD wData { }; - WORD wDataRet { }; - if (!wstr2num(m_wstrTextSearch, wData) || (m_fReplace && !wstr2num(m_wstrTextReplace, wDataRet))) + WORD wDataRep { }; + if (!wstr2num(m_wstrTextSearch, wData) || (m_fReplace && !wstr2num(m_wstrTextReplace, wDataRep))) { MessageBoxW(m_wstrWrongInput.data(), L"Error", MB_OK | MB_ICONERROR | MB_TOPMOST); return false; } m_strSearch.assign(reinterpret_cast(&wData), sizeof(WORD)); - m_strReplace.assign(reinterpret_cast(&wDataRet), sizeof(WORD)); + m_strReplace.assign(reinterpret_cast(&wDataRep), sizeof(WORD)); m_nSizeSearch = m_strSearch.size(); m_nSizeReplace = m_strReplace.size(); m_pSearchData = reinterpret_cast(m_strSearch.data()); @@ -594,15 +628,15 @@ bool CHexDlgSearch::PrepareWORD() bool CHexDlgSearch::PrepareDWORD() { DWORD dwData { }; - DWORD dwDataRet { }; - if (!wstr2num(m_wstrTextSearch, dwData) || (m_fReplace && !wstr2num(m_wstrTextReplace, dwDataRet))) + DWORD dwDataRep { }; + if (!wstr2num(m_wstrTextSearch, dwData) || (m_fReplace && !wstr2num(m_wstrTextReplace, dwDataRep))) { MessageBoxW(m_wstrWrongInput.data(), L"Error", MB_OK | MB_ICONERROR | MB_TOPMOST); return false; } m_strSearch.assign(reinterpret_cast(&dwData), sizeof(DWORD)); - m_strReplace.assign(reinterpret_cast(&dwDataRet), sizeof(DWORD)); + m_strReplace.assign(reinterpret_cast(&dwDataRep), sizeof(DWORD)); m_nSizeSearch = m_strSearch.size(); m_nSizeReplace = m_strReplace.size(); m_pSearchData = reinterpret_cast(m_strSearch.data()); @@ -614,15 +648,15 @@ bool CHexDlgSearch::PrepareDWORD() bool CHexDlgSearch::PrepareQWORD() { QWORD qwData { }; - QWORD qwDataRet { }; - if (!wstr2num(m_wstrTextSearch, qwData) || (m_fReplace && !wstr2num(m_wstrTextReplace, qwDataRet))) + QWORD qwDataRep { }; + if (!wstr2num(m_wstrTextSearch, qwData) || (m_fReplace && !wstr2num(m_wstrTextReplace, qwDataRep))) { MessageBoxW(m_wstrWrongInput.data(), L"Error", MB_OK | MB_ICONERROR | MB_TOPMOST); return false; } m_strSearch.assign(reinterpret_cast(&qwData), sizeof(QWORD)); - m_strReplace.assign(reinterpret_cast(&qwDataRet), sizeof(QWORD)); + m_strReplace.assign(reinterpret_cast(&qwDataRep), sizeof(QWORD)); m_nSizeSearch = m_strSearch.size(); m_nSizeReplace = m_strReplace.size(); m_pSearchData = reinterpret_cast(m_strSearch.data()); @@ -634,15 +668,15 @@ bool CHexDlgSearch::PrepareQWORD() bool CHexDlgSearch::PrepareFloat() { float flData { }; - float flDataRet { }; - if (!wstr2num(m_wstrTextSearch, flData) || (m_fReplace && !wstr2num(m_wstrTextReplace, flDataRet))) + float flDataRep { }; + if (!wstr2num(m_wstrTextSearch, flData) || (m_fReplace && !wstr2num(m_wstrTextReplace, flDataRep))) { MessageBoxW(m_wstrWrongInput.data(), L"Error", MB_OK | MB_ICONERROR | MB_TOPMOST); return false; } m_strSearch.assign(reinterpret_cast(&flData), sizeof(float)); - m_strReplace.assign(reinterpret_cast(&flDataRet), sizeof(float)); + m_strReplace.assign(reinterpret_cast(&flDataRep), sizeof(float)); m_nSizeSearch = m_strSearch.size(); m_nSizeReplace = m_strReplace.size(); m_pSearchData = reinterpret_cast(m_strSearch.data()); @@ -654,15 +688,15 @@ bool CHexDlgSearch::PrepareFloat() bool CHexDlgSearch::PrepareDouble() { double ddData { }; - double ddDataRet { }; - if (!wstr2num(m_wstrTextSearch, ddData) || (m_fReplace && !wstr2num(m_wstrTextReplace, ddDataRet))) + double ddDataRep { }; + if (!wstr2num(m_wstrTextSearch, ddData) || (m_fReplace && !wstr2num(m_wstrTextReplace, ddDataRep))) { MessageBoxW(m_wstrWrongInput.data(), L"Error", MB_OK | MB_ICONERROR | MB_TOPMOST); return false; } m_strSearch.assign(reinterpret_cast(&ddData), sizeof(double)); - m_strReplace.assign(reinterpret_cast(&ddDataRet), sizeof(double)); + m_strReplace.assign(reinterpret_cast(&ddDataRep), sizeof(double)); m_nSizeSearch = m_strSearch.size(); m_nSizeReplace = m_strReplace.size(); m_pSearchData = reinterpret_cast(m_strSearch.data()); @@ -833,6 +867,7 @@ void CHexDlgSearch::Search() { swprintf_s(wstrInfo.data(), wstrInfo.size(), L"%lu occurrence(s) replaced.", m_dwReplaced); m_dwReplaced = 0; + pHexCtrl->Redraw(); //Redraw in case of Replace all. } else { @@ -892,10 +927,7 @@ bool CHexDlgSearch::Find(ULONGLONG& ullStart, ULONGLONG ullEnd, std::byte* pSear if (ullMemToAcquire > ullSize + nSizeSearch) ullMemToAcquire = ullSize + nSizeSearch; ullSizeChunk = ullMemToAcquire - nSizeSearch; - if (ullSize > ullSizeChunk) - ullChunks = (ullSize % ullSizeChunk) ? ullSize / ullSizeChunk + 1 : ullSize / ullSizeChunk; - else - ullChunks = 1; + ullChunks = ullSize > ullSizeChunk ? ullSize / ullSizeChunk + ((ullSize % ullSizeChunk) ? 1 : 0) : 1; } break; } @@ -906,7 +938,7 @@ bool CHexDlgSearch::Find(ULONGLONG& ullStart, ULONGLONG ullEnd, std::byte* pSear if (fForward) { ullOffsetSearch = ullStart; - for (ULONGLONG iterChunk = 0; iterChunk < ullChunks; ++iterChunk) + for (auto iterChunk = 0ULL; iterChunk < ullChunks; ++iterChunk) { if (ullOffsetSearch + ullMemToAcquire > ullEndSentinel) { @@ -917,7 +949,7 @@ bool CHexDlgSearch::Find(ULONGLONG& ullStart, ULONGLONG ullEnd, std::byte* pSear ullOffsetSearch += ullSizeChunk; auto pData = pHexCtrl->GetData({ ullOffsetSearch, ullMemToAcquire }); - for (ULONGLONG i = 0; i <= ullSizeChunk; i += m_ullStep) + for (auto i = 0ULL; i <= ullSizeChunk; i += m_ullStep) { if (memcmp(pData + i, pSearch, nSizeSearch) == 0) { @@ -933,7 +965,7 @@ bool CHexDlgSearch::Find(ULONGLONG& ullStart, ULONGLONG ullEnd, std::byte* pSear else { ullOffsetSearch = ullStart - ullSizeChunk; - for (ULONGLONG iterChunk = ullChunks; iterChunk > 0; --iterChunk) + for (auto iterChunk = ullChunks; iterChunk > 0; --iterChunk) { auto pData = pHexCtrl->GetData({ ullOffsetSearch, ullMemToAcquire }); for (auto i = static_cast(ullSizeChunk); i >= 0; i -= m_ullStep) //i might be negative. @@ -1035,6 +1067,26 @@ void CHexDlgSearch::SetEditStep(ULONGLONG ullStep) m_stEditStep.SetWindowTextW(buff); } +int CHexDlgSearch::memcmp(const std::byte* pBuf1, const std::byte* pBuf2, size_t nSize)const +{ + if (m_fWildcard) + { + for (auto i { 0ULL }; i < nSize; ++i, ++pBuf1, ++pBuf2) + { + if (*pBuf2 == m_uWildcard) //Checking for wildcard match. + continue; + + if (*pBuf1 < *pBuf2) + return -1; + if (*pBuf1 > * pBuf2) + return 1; + } + return 0; + } + + return std::memcmp(pBuf1, pBuf2, nSize); +} + void CHexDlgSearch::ResetSearch() { m_ullOffsetCurr = { }; diff --git a/HexCtrl/src/Dialogs/CHexDlgSearch.h b/HexCtrl/src/Dialogs/CHexDlgSearch.h index 81cf8560..b759c9f1 100644 --- a/HexCtrl/src/Dialogs/CHexDlgSearch.h +++ b/HexCtrl/src/Dialogs/CHexDlgSearch.h @@ -77,6 +77,7 @@ namespace HEXCTRL::INTERNAL void ComboReplaceFill(LPCWSTR pwsz); void SetEditStartAt(ULONGLONG ullOffset); //Start search offset edit set. void SetEditStep(ULONGLONG ullStep); + [[nodiscard]] int memcmp(const std::byte* pBuf1, const std::byte* pBuf2, size_t nSize)const; DECLARE_MESSAGE_MAP() private: CHexCtrl* m_pHexCtrl { }; @@ -86,18 +87,19 @@ namespace HEXCTRL::INTERNAL CComboBox m_stComboSearch; //Combo box "Search". CComboBox m_stComboReplace; //Combo box "Replace". CComboBox m_stComboMode; //Combo box "Search mode". - CButton m_stCheckSel; //Check box "Selection". + CButton m_stCheckSel; //Check box "In selection". + CButton m_stCheckWcard; //Check box "Wildcard". CEdit m_stEditStart; //Edit "Start search at". CEdit m_stEditStep; //Edit "Step". const COLORREF m_clrSearchFailed { RGB(200, 0, 0) }; const COLORREF m_clrSearchFound { RGB(0, 200, 0) }; const COLORREF m_clrBkTextArea { GetSysColor(COLOR_MENU) }; CBrush m_stBrushDefault; - std::wstring m_wstrTextSearch { }; //String to search for. - std::wstring m_wstrTextReplace { }; //Search "Replace with..." wstring. + std::wstring m_wstrTextSearch { }; //Text from "Search" box. + std::wstring m_wstrTextReplace { }; //Text from "Replace with..." box. ULONGLONG m_ullOffsetStart { }; //Search start boundary. ULONGLONG m_ullOffsetEnd { }; //Search end boundary. - ULONGLONG m_ullOffsetCurr { }; //Current offset search should start from. + ULONGLONG m_ullOffsetCurr { }; //Current offset a search should start from. ULONGLONG m_ullEndSentinel { }; //Maximum offset search can't cross. ULONGLONG m_ullStep { 1 }; //Search step (default is 1 byte). DWORD m_dwCount { }; //How many, or what index number. @@ -111,12 +113,16 @@ namespace HEXCTRL::INTERNAL bool m_fReplace { false }; //Find or Find and Replace with...? bool m_fAll { false }; //Find/Replace one by one, or all? bool m_fSelection { false }; //Search in selection. + bool m_fWildcard { false }; //Use wildcard. std::byte* m_pSearchData { }; //Pointer to the data for search. std::byte* m_pReplaceData { }; //Pointer to the data to replace with. size_t m_nSizeSearch { }; size_t m_nSizeReplace { }; - std::string m_strSearch; - std::string m_strReplace; + std::string m_strSearch; //Actual string to search after all conversions. + std::string m_strReplace; //Actual string to replace. + std::wstring m_wstrSearch; //Actual wstring to search. + std::wstring m_wstrReplace; //Actual wstring to replace. + const std::byte m_uWildcard { '?' }; //Wildcard symbol. std::wstring_view m_wstrWrongInput { L"Wrong input data!" }; HEXSPANSTRUCT m_stSelSpan { }; //Previous selection. CMenu m_stMenuList; diff --git a/HexCtrl/src/Helper.cpp b/HexCtrl/src/Helper.cpp index a1ca07b7..3f532d61 100644 --- a/HexCtrl/src/Helper.cpp +++ b/HexCtrl/src/Helper.cpp @@ -87,11 +87,10 @@ namespace HEXCTRL::INTERNAL else { const auto llData = std::wcstoll(wstr.data(), &pEndPtr, iBase); - if ((llData == 0 && (pEndPtr == wstr.data() || *pEndPtr != '\0')) + if ((llData == 0 && (pEndPtr == wstr.data() || *pEndPtr != L'\0')) || ((llData == LLONG_MAX || llData == LLONG_MIN) && errno == ERANGE) || (llData > static_cast(std::numeric_limits::max())) - || (llData < static_cast(std::numeric_limits::min())) - ) + || (llData < static_cast(std::numeric_limits>::min()))) return false; tData = static_cast(llData); } @@ -129,7 +128,7 @@ namespace HEXCTRL::INTERNAL if ((llData == 0 && (pEndPtr == str.data() || *pEndPtr != '\0')) || ((llData == LLONG_MAX || llData == LLONG_MIN) && errno == ERANGE) || (llData > static_cast(std::numeric_limits::max())) - || (llData < static_cast(std::numeric_limits::min())) + || (llData < static_cast(std::numeric_limits>::min())) ) return false; tData = static_cast(llData); @@ -140,20 +139,26 @@ namespace HEXCTRL::INTERNAL //Explicit instantiations of templated func in .cpp. template bool str2num(const std::string& str, UCHAR& t, int iBase); - bool str2hex(const std::string& str, std::string& strToHex) + bool str2hex(const std::string& str, std::string& strToHex, bool fWc, unsigned char uWc) { - if (str.size() % 2 > 0) //Only even amount of chars allowed. - return false; - - const auto nIterations = str.size() / 2; std::string strTmp; - strTmp.reserve(nIterations); - - for (size_t i = 0; i < nIterations; ++i) + for (auto iterBegin = str.begin(); iterBegin != str.end();) { - //Extract two current chars and pass it to str2num as string_view. - if (unsigned char chNumber; str2num(str.substr(i * 2, (i + 2 <= str.size()) ? 2 : 1), chNumber, 16)) + if (fWc && *iterBegin == uWc) //Skip wildcard. + { + ++iterBegin; + strTmp += uWc; + continue; + } + + //Extract two current chars and pass it to str2num as string. + const size_t nOffsetCurr = iterBegin - str.begin(); + const auto nSize = nOffsetCurr + 2 <= str.size() ? 2 : 1; + if (unsigned char chNumber; str2num(str.substr(nOffsetCurr, nSize), chNumber, 16)) + { + iterBegin += nSize; strTmp += chNumber; + } else return false; } diff --git a/HexCtrl/src/Helper.h b/HexCtrl/src/Helper.h index b890e532..a16da913 100644 --- a/HexCtrl/src/Helper.h +++ b/HexCtrl/src/Helper.h @@ -37,7 +37,8 @@ namespace HEXCTRL::INTERNAL bool str2num(const std::string& str, T& tData, int iBase = 0); //Converts every two numeric chars to one respective hex character: "56"->V(0x56), "7A"->z(0x7A) - bool str2hex(const std::string& str, std::string& strToHex); + //fWc means that wildcards are allowed, uWc - the wildcard. + bool str2hex(const std::string& str, std::string& strToHex, bool fWc = false, unsigned char uWc = '?'); //Wide to Multibyte string convertion. std::string wstr2str(std::wstring_view wstr, UINT uCodePage = CP_UTF8); diff --git a/HexCtrl/src/ListEx/src/CListEx.cpp b/HexCtrl/src/ListEx/src/CListEx.cpp index 84a5f4d4..534811c8 100644 --- a/HexCtrl/src/ListEx/src/CListEx.cpp +++ b/HexCtrl/src/ListEx/src/CListEx.cpp @@ -8,6 +8,7 @@ #include "stdafx.h" #include "CListEx.h" #include "strsafe.h" +#include #include using namespace HEXCTRL::LISTEX; @@ -1074,24 +1075,21 @@ void CListEx::OnMouseMove(UINT /*nFlags*/, CPoint pt) ListView_SubItemHitTest(m_hWnd, &hi); bool fLink { false }; //Cursor at link's rect area. - for (const auto& iter : ParseItemText(hi.iItem, hi.iSubItem)) + auto vecText = ParseItemText(hi.iItem, hi.iSubItem); + if (const auto iterFind = std::find_if(vecText.begin(), vecText.end(), [&](SITEMTEXT& iter) + { return iter.fLink && iter.rect.PtInRect(pt); }); iterFind != vecText.end()) { - if (iter.fLink && iter.rect.PtInRect(pt)) + fLink = true; + if (m_fLinkTooltip && !m_fLDownAtLink && m_rcLinkCurr != iterFind->rect) { - fLink = true; + TtLinkHide(); + m_rcLinkCurr = iterFind->rect; + m_stCurrLink.iItem = hi.iItem; + m_stCurrLink.iSubItem = hi.iSubItem; + m_wstrTtText = iterFind->fTitle ? iterFind->wstrTitle : iterFind->wstrLink; + m_stTInfoLink.lpszText = m_wstrTtText.data(); - if (m_fLinkTooltip && !m_fLDownAtLink && m_rcLinkCurr != iter.rect) - { - TtLinkHide(); - m_rcLinkCurr = iter.rect; - m_stCurrLink.iItem = hi.iItem; - m_stCurrLink.iSubItem = hi.iSubItem; - m_wstrTtText = iter.fTitle ? iter.wstrTitle : iter.wstrLink; - m_stTInfoLink.lpszText = m_wstrTtText.data(); - - SetTimer(ID_TIMER_TT_LINK_ACTIVATE, 400, nullptr); //Activate (show) tooltip after delay. - } - break; + SetTimer(ID_TIMER_TT_LINK_ACTIVATE, 400, nullptr); //Activate (show) tooltip after delay. } } SetCursor(fLink ? m_cursorHand : m_cursorDefault); @@ -1154,15 +1152,13 @@ void CListEx::OnLButtonDown(UINT nFlags, CPoint pt) return; bool fLinkDown { false }; - for (const auto& iter : ParseItemText(hi.iItem, hi.iSubItem)) + auto vecText = ParseItemText(hi.iItem, hi.iSubItem); + if (const auto iterFind = std::find_if(vecText.begin(), vecText.end(), [&](SITEMTEXT& iter) + { return iter.fLink && iter.rect.PtInRect(pt); }); iterFind != vecText.end()) { - if (iter.fLink && iter.rect.PtInRect(pt)) - { - m_fLDownAtLink = true; - m_rcLinkCurr = iter.rect; - fLinkDown = true; - break; - } + m_fLDownAtLink = true; + m_rcLinkCurr = iterFind->rect; + fLinkDown = true; } if (!fLinkDown) @@ -1183,23 +1179,19 @@ void CListEx::OnLButtonUp(UINT nFlags, CPoint pt) return; } - for (const auto& iter : ParseItemText(hi.iItem, hi.iSubItem)) + auto vecText = ParseItemText(hi.iItem, hi.iSubItem); + if (const auto iterFind = std::find_if(vecText.begin(), vecText.end(), [&](SITEMTEXT& iter) + { return iter.fLink && iter.rect == m_rcLinkCurr; }); iterFind != vecText.end()) { - if (iter.fLink && iter.rect == m_rcLinkCurr) - { - m_rcLinkCurr.SetRectEmpty(); - fLinkUp = true; - - UINT uCtrlId = static_cast(GetDlgCtrlID()); - NMITEMACTIVATE nmii { { m_hWnd, uCtrlId, LISTEX_MSG_LINKCLICK } }; - nmii.iItem = hi.iItem; - nmii.iSubItem = hi.iSubItem; - nmii.ptAction = pt; - nmii.lParam = reinterpret_cast(iter.wstrLink.data()); - GetParent()->SendMessageW(WM_NOTIFY, static_cast(uCtrlId), reinterpret_cast(&nmii)); - - break; - } + m_rcLinkCurr.SetRectEmpty(); + fLinkUp = true; + UINT uCtrlId = static_cast(GetDlgCtrlID()); + NMITEMACTIVATE nmii { { m_hWnd, uCtrlId, LISTEX_MSG_LINKCLICK } }; + nmii.iItem = hi.iItem; + nmii.iSubItem = hi.iSubItem; + nmii.ptAction = pt; + nmii.lParam = reinterpret_cast(iterFind->wstrLink.data()); + GetParent()->SendMessageW(WM_NOTIFY, static_cast(uCtrlId), reinterpret_cast(&nmii)); } } diff --git a/HexCtrl/src/version.h b/HexCtrl/src/version.h index d42a8379..7f227185 100644 --- a/HexCtrl/src/version.h +++ b/HexCtrl/src/version.h @@ -2,6 +2,6 @@ #define PRODUCT_DESC "Hex Control for MFC/Win32" #define COPYRIGHT_NAME "(C) Jovibor 2019-2020" #define MAJOR_VERSION 2 -#define MINOR_VERSION 18 -#define MAINTENANCE_VERSION 1 +#define MINOR_VERSION 19 +#define MAINTENANCE_VERSION 0 #define REVISION_VERSION 0