hex-edit.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. #if defined(Hiro_HexEdit)
  2. namespace hiro {
  3. auto pHexEdit::construct() -> void {
  4. hwnd = CreateWindowEx(
  5. WS_EX_CLIENTEDGE, L"EDIT", L"",
  6. WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY | ES_MULTILINE | ES_WANTRETURN,
  7. 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0
  8. );
  9. scrollBar = CreateWindowEx(
  10. 0, L"SCROLLBAR", L"",
  11. WS_VISIBLE | WS_CHILD | SBS_VERT,
  12. 0, 0, 0, 0, hwnd, nullptr, GetModuleHandle(0), 0
  13. );
  14. SetWindowLongPtr(scrollBar, GWLP_USERDATA, (LONG_PTR)&reference);
  15. pWidget::construct();
  16. setAddress(state().address);
  17. setBackgroundColor(state().backgroundColor);
  18. setLength(state().length);
  19. update();
  20. PostMessage(hwnd, EM_SETSEL, 10, 10);
  21. }
  22. auto pHexEdit::destruct() -> void {
  23. DestroyWindow(hwnd);
  24. }
  25. auto pHexEdit::setAddress(unsigned address) -> void {
  26. SetScrollPos(scrollBar, SB_CTL, address / state().columns, true);
  27. update();
  28. }
  29. auto pHexEdit::setBackgroundColor(Color color) -> void {
  30. if(backgroundBrush) DeleteObject(backgroundBrush);
  31. backgroundBrush = CreateSolidBrush(color ? CreateRGB(color) : GetSysColor(COLOR_WINDOW));
  32. }
  33. auto pHexEdit::setColumns(unsigned columns) -> void {
  34. update();
  35. }
  36. auto pHexEdit::setForegroundColor(Color color) -> void {
  37. }
  38. auto pHexEdit::setLength(unsigned length) -> void {
  39. SetScrollRange(scrollBar, SB_CTL, 0, rowsScrollable(), true);
  40. EnableWindow(scrollBar, rowsScrollable() > 0);
  41. update();
  42. }
  43. auto pHexEdit::setRows(unsigned rows) -> void {
  44. update();
  45. }
  46. auto pHexEdit::update() -> void {
  47. if(!state().onRead) {
  48. SetWindowText(hwnd, L"");
  49. return;
  50. }
  51. unsigned cursorPosition = Edit_GetSel(hwnd);
  52. string output;
  53. unsigned address = state().address;
  54. for(auto row : range(state().rows)) {
  55. output.append(hex(address, 8L));
  56. output.append(" ");
  57. string hexdata;
  58. string ansidata = " ";
  59. for(auto column : range(state().columns)) {
  60. if(address < state().length) {
  61. uint8_t data = self().doRead(address++);
  62. hexdata.append(hex(data, 2L));
  63. hexdata.append(" ");
  64. ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
  65. } else {
  66. hexdata.append(" ");
  67. ansidata.append(" ");
  68. }
  69. }
  70. output.append(hexdata);
  71. output.append(ansidata);
  72. if(address >= state().length) break;
  73. if(row != state().rows - 1) output.append("\r\n");
  74. }
  75. SetWindowText(hwnd, utf16_t(output));
  76. Edit_SetSel(hwnd, LOWORD(cursorPosition), HIWORD(cursorPosition));
  77. }
  78. auto pHexEdit::keyPress(unsigned scancode) -> bool {
  79. if(!state().onRead) return false;
  80. signed position = LOWORD(Edit_GetSel(hwnd));
  81. signed lineWidth = 10 + (state().columns * 3) + 1 + state().columns + 2;
  82. signed cursorY = position / lineWidth;
  83. signed cursorX = position % lineWidth;
  84. if(scancode == VK_HOME) {
  85. signed offset = cursorY * lineWidth + 10;
  86. Edit_SetSel(hwnd, offset, offset);
  87. return true;
  88. }
  89. if(scancode == VK_END) {
  90. signed offset = cursorY * lineWidth + 57;
  91. Edit_SetSel(hwnd, offset, offset);
  92. return true;
  93. }
  94. if(scancode == VK_UP) {
  95. if(cursorY > 0) return false;
  96. scrollTo(scrollPosition() - 1);
  97. return true;
  98. }
  99. if(scancode == VK_DOWN) {
  100. if(cursorY >= rows() - 1) return true;
  101. if(cursorY < state().rows - 1) return false;
  102. scrollTo(scrollPosition() + 1);
  103. return true;
  104. }
  105. if(scancode == VK_PRIOR) {
  106. scrollTo(scrollPosition() - state().rows);
  107. return true;
  108. }
  109. if(scancode == VK_NEXT) {
  110. scrollTo(scrollPosition() + state().rows);
  111. return true;
  112. }
  113. //convert scancode to hex nibble
  114. if(scancode >= '0' && scancode <= '9') scancode = scancode - '0';
  115. else if(scancode >= 'A' && scancode <= 'F') scancode = scancode - 'A' + 10;
  116. else if(scancode >= 'a' && scancode <= 'f') scancode = scancode - 'a' + 10;
  117. else return false;
  118. if(cursorX >= 10) {
  119. //not on an address
  120. cursorX -= 10;
  121. if((cursorX % 3) != 2) {
  122. //not on a space
  123. bool cursorNibble = (cursorX % 3) == 1; //0 = high, 1 = low
  124. cursorX /= 3;
  125. if(cursorX < state().columns) {
  126. //not in ANSI region
  127. unsigned address = state().address + (cursorY * state().columns + cursorX);
  128. if(address >= state().length) return false; //do not edit past end of data
  129. uint8_t data = self().doRead(address);
  130. //write modified value
  131. if(cursorNibble == 1) {
  132. data = (data & 0xf0) | (scancode << 0);
  133. } else {
  134. data = (data & 0x0f) | (scancode << 4);
  135. }
  136. self().doWrite(address, data);
  137. //auto-advance cursor to next nibble or byte
  138. position++;
  139. if(cursorNibble && cursorX != state().columns - 1) position++;
  140. Edit_SetSel(hwnd, position, position);
  141. //refresh output to reflect modified data
  142. update();
  143. }
  144. }
  145. }
  146. return true;
  147. }
  148. auto pHexEdit::rows() -> int {
  149. return (max(1u, state().length) + state().columns - 1) / state().columns;
  150. }
  151. auto pHexEdit::rowsScrollable() -> int {
  152. return max(0u, rows() - state().rows);
  153. }
  154. auto pHexEdit::scrollPosition() -> int {
  155. return state().address / state().columns;
  156. }
  157. auto pHexEdit::scrollTo(signed position) -> void {
  158. if(position > rowsScrollable()) position = rowsScrollable();
  159. if(position < 0) position = 0;
  160. if(position == scrollPosition()) return;
  161. self().setAddress(position * state().columns);
  162. }
  163. auto pHexEdit::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
  164. if(msg == WM_KEYDOWN) {
  165. if(keyPress(wparam)) return 0;
  166. }
  167. if(msg == WM_MOUSEWHEEL) {
  168. int offset = -((int16_t)HIWORD(wparam) / WHEEL_DELTA);
  169. scrollTo(scrollPosition() + offset);
  170. return true;
  171. }
  172. if(msg == WM_SIZE) {
  173. RECT rc;
  174. GetClientRect(hwnd, &rc);
  175. SetWindowPos(scrollBar, HWND_TOP, rc.right - 18, 0, 18, rc.bottom, SWP_SHOWWINDOW);
  176. }
  177. if(msg == WM_VSCROLL) {
  178. SCROLLINFO info{sizeof(SCROLLINFO)};
  179. info.fMask = SIF_ALL;
  180. GetScrollInfo((HWND)lparam, SB_CTL, &info);
  181. switch(LOWORD(wparam)) {
  182. case SB_LEFT: info.nPos = info.nMin; break;
  183. case SB_RIGHT: info.nPos = info.nMax; break;
  184. case SB_LINELEFT: info.nPos--; break;
  185. case SB_LINERIGHT: info.nPos++; break;
  186. case SB_PAGELEFT: info.nPos -= info.nMax >> 3; break;
  187. case SB_PAGERIGHT: info.nPos += info.nMax >> 3; break;
  188. case SB_THUMBTRACK: info.nPos = info.nTrackPos; break;
  189. }
  190. info.fMask = SIF_POS;
  191. SetScrollInfo((HWND)lparam, SB_CTL, &info, TRUE);
  192. GetScrollInfo((HWND)lparam, SB_CTL, &info); //get clamped position
  193. scrollTo(info.nPos);
  194. return true;
  195. }
  196. return pWidget::windowProc(hwnd, msg, wparam, lparam);
  197. }
  198. }
  199. #endif