Process Hacker
hexedit.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * hex editor control
4  *
5  * Copyright (C) 2010-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phgui.h>
24 #include <hexedit.h>
25 #include <hexeditp.h>
26 
27 // Code originally from http://www.codeguru.com/Cpp/controls/editctrl/article.php/c539
28 
30  VOID
31  )
32 {
33  WNDCLASSEX c = { sizeof(c) };
34 
35  c.style = CS_GLOBALCLASS;
36  c.lpfnWndProc = PhpHexEditWndProc;
37  c.cbClsExtra = 0;
38  c.cbWndExtra = sizeof(PVOID);
39  c.hInstance = PhLibImageBase;
40  c.hIcon = NULL;
41  c.hCursor = LoadCursor(NULL, IDC_ARROW);
42  c.hbrBackground = NULL;
43  c.lpszMenuName = NULL;
44  c.lpszClassName = PH_HEXEDIT_CLASSNAME;
45  c.hIconSm = NULL;
46 
47  if (!RegisterClassEx(&c))
48  return FALSE;
49 
50  return TRUE;
51 }
52 
54  _Out_ PPHP_HEXEDIT_CONTEXT *Context
55  )
56 {
57  PPHP_HEXEDIT_CONTEXT context;
58 
59  context = PhAllocate(sizeof(PHP_HEXEDIT_CONTEXT));
60  memset(context, 0, sizeof(PHP_HEXEDIT_CONTEXT)); // important, set NullWidth to 0
61 
62  context->Data = NULL;
63  context->Length = 0;
64  context->TopIndex = 0;
65  context->BytesPerRow = 16;
66  context->LinesPerPage = 1;
67 
68  context->ShowHex = TRUE;
69  context->ShowAscii = TRUE;
70  context->ShowAddress = TRUE;
71  context->AddressIsWide = TRUE;
72  context->AllowLengthChange = FALSE;
73 
74  context->AddressOffset = 0;
75  context->HexOffset = 0;
76  context->AsciiOffset = 0;
77 
78  context->Update = TRUE;
79  context->NoAddressChange = FALSE;
80  context->CurrentMode = EDIT_NONE;
81 
82  context->EditPosition.x = 0;
83  context->EditPosition.y = 0;
84  context->CurrentAddress = 0;
85  context->HalfPage = TRUE;
86 
87  context->SelStart = -1;
88  context->SelEnd = -1;
89 
90  *Context = context;
91 }
92 
94  _In_ _Post_invalid_ PPHP_HEXEDIT_CONTEXT Context
95  )
96 {
97  if (!Context->UserBuffer && Context->Data) PhFree(Context->Data);
98  if (Context->CharBuffer) PhFree(Context->CharBuffer);
99  if (Context->Font) DeleteObject(Context->Font);
100  PhFree(Context);
101 }
102 
103 LRESULT CALLBACK PhpHexEditWndProc(
104  _In_ HWND hwnd,
105  _In_ UINT uMsg,
106  _In_ WPARAM wParam,
107  _In_ LPARAM lParam
108  )
109 {
110  PPHP_HEXEDIT_CONTEXT context;
111 
112  context = (PPHP_HEXEDIT_CONTEXT)GetWindowLongPtr(hwnd, 0);
113 
114  if (uMsg == WM_CREATE)
115  {
116  PhpCreateHexEditContext(&context);
117  SetWindowLongPtr(hwnd, 0, (LONG_PTR)context);
118  }
119 
120  if (!context)
121  return DefWindowProc(hwnd, uMsg, wParam, lParam);
122 
123  switch (uMsg)
124  {
125  case WM_CREATE:
126  {
127  context->Font = CreateFont(-12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Courier New");
128  }
129  break;
130  case WM_DESTROY:
131  {
132  PhpFreeHexEditContext(context);
133  }
134  break;
135  case WM_PAINT:
136  {
137  PAINTSTRUCT paintStruct;
138  HDC hdc;
139 
140  if (hdc = BeginPaint(hwnd, &paintStruct))
141  {
142  PhpHexEditOnPaint(hwnd, context, &paintStruct, hdc);
143  EndPaint(hwnd, &paintStruct);
144  }
145  }
146  break;
147  case WM_SETFOCUS:
148  {
149  if (context->Data && !PhpHexEditHasSelected(context))
150  {
151  if (context->EditPosition.x == 0 && context->ShowAddress)
152  PhpHexEditCreateAddressCaret(hwnd, context);
153  else
154  PhpHexEditCreateEditCaret(hwnd, context);
155 
156  SetCaretPos(context->EditPosition.x, context->EditPosition.y);
157  ShowCaret(hwnd);
158  }
159  }
160  break;
161  case WM_KILLFOCUS:
162  {
163  DestroyCaret();
164  }
165  break;
166  case WM_VSCROLL:
167  {
168  SHORT scrollRequest = LOWORD(wParam);
169  LONG currentPosition;
170  LONG originalTopIndex;
171  SCROLLINFO scrollInfo = { sizeof(scrollInfo) };
172 
173  originalTopIndex = context->TopIndex;
174 
175  scrollInfo.fMask = SIF_TRACKPOS;
176  GetScrollInfo(hwnd, SB_VERT, &scrollInfo);
177  currentPosition = scrollInfo.nTrackPos;
178 
179  if (context->Data)
180  {
181  LONG mult;
182 
183  mult = context->LinesPerPage * context->BytesPerRow;
184 
185  switch (scrollRequest)
186  {
187  case SB_LINEDOWN:
188  if (context->TopIndex < context->Length - mult)
189  {
190  context->TopIndex += context->BytesPerRow;
191  REDRAW_WINDOW(hwnd);
192  }
193  break;
194  case SB_LINEUP:
195  if (context->TopIndex >= context->BytesPerRow)
196  {
197  context->TopIndex -= context->BytesPerRow;
198  REDRAW_WINDOW(hwnd);
199  }
200  break;
201  case SB_PAGEDOWN:
202  if (context->TopIndex < context->Length - mult)
203  {
204  context->TopIndex += mult;
205 
206  if (context->TopIndex > context->Length - mult)
207  context->TopIndex = context->Length - mult;
208 
209  REDRAW_WINDOW(hwnd);
210  }
211  break;
212  case SB_PAGEUP:
213  if (context->TopIndex > 0)
214  {
215  context->TopIndex -= mult;
216 
217  if (context->TopIndex < 0)
218  context->TopIndex = 0;
219 
220  REDRAW_WINDOW(hwnd);
221  }
222  break;
223  case SB_THUMBTRACK:
224  context->TopIndex = currentPosition * context->BytesPerRow;
225  REDRAW_WINDOW(hwnd);
226  break;
227  }
228 
229  SetScrollPos(hwnd, SB_VERT, context->TopIndex / context->BytesPerRow, TRUE);
230 
231  if (!context->NoAddressChange && FALSE) // this behaviour sucks, so just leave it out
232  context->CurrentAddress += context->TopIndex - originalTopIndex;
233 
234  PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress);
235  }
236  }
237  break;
238  case WM_MOUSEWHEEL:
239  {
240  SHORT wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
241 
242  if (context->Data)
243  {
244  ULONG wheelScrollLines;
245 
246  if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScrollLines, 0))
247  wheelScrollLines = 3;
248 
249  context->TopIndex += context->BytesPerRow * (LONG)wheelScrollLines * -wheelDelta / WHEEL_DELTA;
250 
251  if (context->TopIndex < 0)
252  context->TopIndex = 0;
253 
254  if (context->Length >= context->LinesPerPage * context->BytesPerRow)
255  {
256  if (context->TopIndex > context->Length - context->LinesPerPage * context->BytesPerRow)
257  context->TopIndex = context->Length - context->LinesPerPage * context->BytesPerRow;
258  }
259 
260  REDRAW_WINDOW(hwnd);
261 
262  SetScrollPos(hwnd, SB_VERT, context->TopIndex / context->BytesPerRow, TRUE);
263 
264  PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress);
265  }
266  }
267  break;
268  case WM_GETDLGCODE:
269  if (wParam != VK_ESCAPE)
270  return DLGC_WANTALLKEYS;
271  break;
272  case WM_ERASEBKGND:
273  return 1;
274  case WM_LBUTTONDOWN:
275  {
276  ULONG flags = (ULONG)wParam;
277  POINT cursorPos;
278 
279  cursorPos.x = (LONG)(SHORT)LOWORD(lParam);
280  cursorPos.y = (LONG)(SHORT)HIWORD(lParam);
281 
282  SetFocus(hwnd);
283 
284  if (context->Data)
285  {
286  POINT point;
287 
288  if (wParam & MK_SHIFT)
289  context->SelStart = context->CurrentAddress;
290 
291  PhpHexEditCalculatePosition(hwnd, context, cursorPos.x, cursorPos.y, &point);
292 
293  if (point.x > -1)
294  {
295  context->EditPosition = point;
296 
297  point.x *= context->NullWidth;
298  point.y *= context->LineHeight;
299 
300  if (point.x == 0 && context->ShowAddress)
301  PhpHexEditCreateAddressCaret(hwnd, context);
302  else
303  PhpHexEditCreateEditCaret(hwnd, context);
304 
305  SetCaretPos(point.x, point.y);
306 
307  if (flags & MK_SHIFT)
308  {
309  context->SelEnd = context->CurrentAddress;
310 
311  if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW)
312  context->SelEnd++;
313 
314  REDRAW_WINDOW(hwnd);
315  }
316  }
317 
318  if (!(flags & MK_SHIFT))
319  {
320  if (DragDetect(hwnd, cursorPos))
321  {
322  context->SelStart = context->CurrentAddress;
323  context->SelEnd = context->SelStart;
324  SetCapture(hwnd);
325  context->HasCapture = TRUE;
326  }
327  else
328  {
329  BOOLEAN selected;
330 
331  selected = context->SelStart != -1;
332  context->SelStart = -1;
333  context->SelEnd = -1;
334 
335  if (selected)
336  REDRAW_WINDOW(hwnd);
337  }
338  }
339 
340  if (!PhpHexEditHasSelected(context))
341  ShowCaret(hwnd);
342  }
343  }
344  break;
345  case WM_LBUTTONUP:
346  {
347  if (context->HasCapture && PhpHexEditHasSelected(context))
348  ReleaseCapture();
349 
350  context->HasCapture = FALSE;
351  }
352  break;
353  case WM_MOUSEMOVE:
354  {
355  ULONG flags = (ULONG)wParam;
356  POINT cursorPos;
357 
358  cursorPos.x = (LONG)(SHORT)LOWORD(lParam);
359  cursorPos.y = (LONG)(SHORT)HIWORD(lParam);
360 
361  if (
362  context->Data &&
363  context->HasCapture &&
364  context->SelStart != -1
365  )
366  {
367  RECT rect;
368  POINT point;
369  ULONG oldSelEnd;
370 
371  // User is dragging.
372 
373  GetClientRect(hwnd, &rect);
374 
375  if (!PtInRect(&rect, cursorPos))
376  {
377  if (cursorPos.y < 0)
378  {
379  SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
380  cursorPos.y = 0;
381  }
382  else if (cursorPos.y > rect.bottom)
383  {
384  SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
385  cursorPos.y = rect.bottom - 1;
386  }
387  }
388 
389  oldSelEnd = context->SelEnd;
390  PhpHexEditCalculatePosition(hwnd, context, cursorPos.x, cursorPos.y, &point);
391 
392  if (point.x > -1)
393  {
394  context->SelEnd = context->CurrentAddress;
395 
396  if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW)
397  context->SelEnd++;
398  }
399 
400  if (PhpHexEditHasSelected(context))
401  DestroyCaret();
402 
403  if (context->SelEnd != oldSelEnd)
404  REDRAW_WINDOW(hwnd);
405  }
406  }
407  break;
408  case WM_CHAR:
409  {
410  ULONG c = (ULONG)wParam;
411 
412  if (!context->Data)
413  goto DefaultHandler;
414  if (c == '\t')
415  goto DefaultHandler;
416 
417  if (GetKeyState(VK_CONTROL) < 0)
418  {
419  switch (c)
420  {
421  case 0x3:
422  if (PhpHexEditHasSelected(context))
423  PhpHexEditCopyEdit(hwnd, context);
424  goto DefaultHandler;
425  case 0x16:
426  PhpHexEditPasteEdit(hwnd, context);
427  goto DefaultHandler;
428  case 0x18:
429  if (PhpHexEditHasSelected(context))
430  PhpHexEditCutEdit(hwnd, context);
431  goto DefaultHandler;
432  case 0x1a:
433  PhpHexEditUndoEdit(hwnd, context);
434  goto DefaultHandler;
435  }
436  }
437 
438  // Disallow editing beyond the end of the data.
439  if (context->CurrentAddress >= context->Length)
440  goto DefaultHandler;
441 
442  if (c == 0x8)
443  {
444  if (context->CurrentAddress != 0)
445  {
446  context->CurrentAddress--;
447  PhpHexEditSelDelete(hwnd, context, context->CurrentAddress, context->CurrentAddress + 1);
448  PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress);
449  REDRAW_WINDOW(hwnd);
450  }
451 
452  goto DefaultHandler;
453  }
454 
455  PhpHexEditSetSel(hwnd, context, -1, -1);
456 
457  switch (context->CurrentMode)
458  {
459  case EDIT_NONE:
460  goto DefaultHandler;
461  case EDIT_HIGH:
462  case EDIT_LOW:
463  if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
464  {
465  ULONG b = c - '0';
466 
467  if (b > 9)
468  b = 10 + c - 'a';
469 
470  if (context->CurrentMode == EDIT_HIGH)
471  {
472  context->Data[context->CurrentAddress] =
473  (UCHAR)((context->Data[context->CurrentAddress] & 0x0f) | (b << 4));
474  }
475  else
476  {
477  context->Data[context->CurrentAddress] =
478  (UCHAR)((context->Data[context->CurrentAddress] & 0xf0) | b);
479  }
480 
481  PhpHexEditMove(hwnd, context, 1, 0);
482  }
483  break;
484  case EDIT_ASCII:
485  context->Data[context->CurrentAddress] = (UCHAR)c;
486  PhpHexEditMove(hwnd, context, 1, 0);
487  break;
488  }
489 
490  REDRAW_WINDOW(hwnd);
491  }
492  break;
493  case WM_KEYDOWN:
494  {
495  ULONG vk = (ULONG)wParam;
496  BOOLEAN shift = GetKeyState(VK_SHIFT) < 0;
497  BOOLEAN oldNoAddressChange = context->NoAddressChange;
498  BOOLEAN noScrollIntoView = FALSE;
499 
500  context->NoAddressChange = TRUE;
501 
502  switch (vk)
503  {
504  case VK_DOWN:
505  if (context->CurrentMode != EDIT_NONE)
506  {
507  if (shift)
508  {
509  if (!PhpHexEditHasSelected(context))
510  context->SelStart = context->CurrentAddress;
511 
512  PhpHexEditMove(hwnd, context, 0, 1);
513  context->SelEnd = context->CurrentAddress;
514 
515  if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW)
516  context->SelEnd++;
517 
518  REDRAW_WINDOW(hwnd);
519  break;
520  }
521  else
522  {
523  PhpHexEditSetSel(hwnd, context, -1, -1);
524  }
525 
526  PhpHexEditMove(hwnd, context, 0, 1);
527  noScrollIntoView = TRUE;
528  }
529  else
530  {
531  PhpHexEditMove(hwnd, context, 0, 1);
532  }
533  break;
534  case VK_UP:
535  if (context->CurrentMode != EDIT_NONE)
536  {
537  if (shift)
538  {
539  if (!PhpHexEditHasSelected(context))
540  context->SelStart = context->CurrentAddress;
541 
542  PhpHexEditMove(hwnd, context, 0, -1);
543  context->SelEnd = context->CurrentAddress;
544 
545  REDRAW_WINDOW(hwnd);
546  break;
547  }
548  else
549  {
550  PhpHexEditSetSel(hwnd, context, -1, -1);
551  }
552 
553  PhpHexEditMove(hwnd, context, 0, -1);
554  noScrollIntoView = TRUE;
555  }
556  else
557  {
558  PhpHexEditMove(hwnd, context, 0, -1);
559  }
560  break;
561  case VK_LEFT:
562  if (context->CurrentMode != EDIT_NONE)
563  {
564  if (shift)
565  {
566  if (!PhpHexEditHasSelected(context))
567  context->SelStart = context->CurrentAddress;
568 
569  PhpHexEditMove(hwnd, context, -1, 0);
570  context->SelEnd = context->CurrentAddress;
571 
572  REDRAW_WINDOW(hwnd);
573  break;
574  }
575  else
576  {
577  PhpHexEditSetSel(hwnd, context, -1, -1);
578  }
579 
580  PhpHexEditMove(hwnd, context, -1, 0);
581  noScrollIntoView = TRUE;
582  }
583  break;
584  case VK_RIGHT:
585  if (context->CurrentMode != EDIT_NONE)
586  {
587  if (shift)
588  {
589  if (!PhpHexEditHasSelected(context))
590  context->SelStart = context->CurrentAddress;
591 
592  PhpHexEditMove(hwnd, context, 1, 0);
593  context->SelEnd = context->CurrentAddress;
594 
595  if (context->CurrentMode == EDIT_HIGH || context->CurrentMode == EDIT_LOW)
596  context->SelEnd++;
597 
598  REDRAW_WINDOW(hwnd);
599  break;
600  }
601  else
602  {
603  PhpHexEditSetSel(hwnd, context, -1, -1);
604  }
605 
606  PhpHexEditMove(hwnd, context, 1, 0);
607  noScrollIntoView = TRUE;
608  }
609  break;
610  case VK_PRIOR:
611  if (shift)
612  {
613  if (!PhpHexEditHasSelected(context))
614  context->SelStart = context->CurrentAddress;
615 
616  SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
617  PhpHexEditMove(hwnd, context, 0, 0);
618  context->SelEnd = context->CurrentAddress;
619 
620  REDRAW_WINDOW(hwnd);
621  break;
622  }
623  else
624  {
625  PhpHexEditSetSel(hwnd, context, -1, -1);
626  }
627 
628  SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
629  PhpHexEditMove(hwnd, context, 0, 0);
630  noScrollIntoView = TRUE;
631  break;
632  case VK_NEXT:
633  if (shift)
634  {
635  if (!PhpHexEditHasSelected(context))
636  context->SelStart = context->CurrentAddress;
637 
638  SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
639  PhpHexEditMove(hwnd, context, 0, 0);
640  context->SelEnd = context->CurrentAddress;
641 
642  REDRAW_WINDOW(hwnd);
643  break;
644  }
645  else
646  {
647  PhpHexEditSetSel(hwnd, context, -1, -1);
648  }
649 
650  SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
651  PhpHexEditMove(hwnd, context, 0, 0);
652  noScrollIntoView = TRUE;
653  break;
654  case VK_HOME:
655  if (shift)
656  {
657  if (!PhpHexEditHasSelected(context))
658  context->SelStart = context->CurrentAddress;
659 
660  if (GetKeyState(VK_CONTROL) < 0)
661  {
662  SendMessage(hwnd, WM_VSCROLL, SB_THUMBTRACK, 0);
663  }
664  else
665  {
666  // Round down.
667  context->CurrentAddress /= context->BytesPerRow;
668  context->CurrentAddress *= context->BytesPerRow;
669  }
670 
671  PhpHexEditMove(hwnd, context, 0, 0);
672  context->SelEnd = context->CurrentAddress;
673 
674  REDRAW_WINDOW(hwnd);
675  break;
676  }
677  else
678  {
679  PhpHexEditSetSel(hwnd, context, -1, -1);
680  }
681 
682  if (GetKeyState(VK_CONTROL) < 0)
683  {
684  SendMessage(hwnd, WM_VSCROLL, SB_THUMBTRACK, 0);
685  context->CurrentAddress = 0;
686  }
687  else
688  {
689  // Round down.
690  context->CurrentAddress /= context->BytesPerRow;
691  context->CurrentAddress *= context->BytesPerRow;
692  }
693 
694  PhpHexEditMove(hwnd, context, 0, 0);
695  noScrollIntoView = TRUE;
696 
697  break;
698  case VK_END:
699  if (shift)
700  {
701  if (!PhpHexEditHasSelected(context))
702  context->SelStart = context->CurrentAddress;
703 
704  if (GetKeyState(VK_CONTROL) < 0)
705  {
706  context->CurrentAddress = context->Length - 1;
707  SendMessage(hwnd, WM_VSCROLL,
708  MAKEWPARAM(SB_THUMBTRACK, ((context->Length + (context->BytesPerRow / 2)) / context->BytesPerRow) - context->LinesPerPage),
709  0);
710  }
711  else
712  {
713  context->CurrentAddress /= context->BytesPerRow;
714  context->CurrentAddress *= context->BytesPerRow;
715  context->CurrentAddress += context->BytesPerRow - 1;
716 
717  if (context->CurrentAddress > context->Length)
718  context->CurrentAddress = context->Length - 1;
719  }
720 
721  PhpHexEditMove(hwnd, context, 0, 0);
722  context->SelEnd = context->CurrentAddress;
723 
724  REDRAW_WINDOW(hwnd);
725  break;
726  }
727  else
728  {
729  PhpHexEditSetSel(hwnd, context, -1, -1);
730  }
731 
732  if (GetKeyState(VK_CONTROL) < 0)
733  {
734  context->CurrentAddress = context->Length - 1;
735 
736  if (context->HalfPage)
737  {
738  SendMessage(hwnd, WM_VSCROLL, 0, 0);
739  }
740  else
741  {
742  SendMessage(hwnd, WM_VSCROLL,
743  MAKEWPARAM(SB_THUMBTRACK, ((context->Length + (context->BytesPerRow / 2)) / context->BytesPerRow) - context->LinesPerPage),
744  0);
745  }
746  }
747  else
748  {
749  context->CurrentAddress /= context->BytesPerRow;
750  context->CurrentAddress *= context->BytesPerRow;
751  context->CurrentAddress += context->BytesPerRow - 1;
752 
753  if (context->CurrentAddress > context->Length)
754  context->CurrentAddress = context->Length - 1;
755  }
756 
757  PhpHexEditMove(hwnd, context, 0, 0);
758  noScrollIntoView = TRUE;
759 
760  break;
761  case VK_INSERT:
762  PhpHexEditSelInsert(hwnd, context, context->CurrentAddress,
763  max(1, context->SelEnd - context->SelStart));
764  REDRAW_WINDOW(hwnd);
765  break;
766  case VK_DELETE:
767  if (PhpHexEditHasSelected(context))
768  {
769  PhpHexEditClearEdit(hwnd, context);
770  }
771  else
772  {
773  PhpHexEditSelDelete(hwnd, context, context->CurrentAddress, context->CurrentAddress + 1);
774  REDRAW_WINDOW(hwnd);
775  }
776  break;
777  case '\t':
778  switch (context->CurrentMode)
779  {
780  case EDIT_NONE:
781  context->CurrentMode = EDIT_HIGH;
782  break;
783  case EDIT_HIGH:
784  case EDIT_LOW:
785  context->CurrentMode = EDIT_ASCII;
786  break;
787  case EDIT_ASCII:
788  context->CurrentMode = EDIT_HIGH;
789  break;
790  }
791 
792  PhpHexEditMove(hwnd, context, 0, 0);
793 
794  break;
795  }
796 
797  // Scroll into view if not in view.
798  if (
799  !noScrollIntoView &&
800  (context->CurrentAddress < context->TopIndex ||
801  context->CurrentAddress >= context->TopIndex + context->LinesPerPage * context->BytesPerRow)
802  )
803  {
804  PhpHexEditScrollTo(hwnd, context, context->CurrentAddress);
805  }
806 
807  context->NoAddressChange = oldNoAddressChange;
808  }
809  break;
810  case HEM_SETBUFFER:
811  {
812  PhpHexEditSetBuffer(hwnd, context, (PUCHAR)lParam, (ULONG)wParam);
813  }
814  return TRUE;
815  case HEM_SETDATA:
816  {
817  PhpHexEditSetData(hwnd, context, (PUCHAR)lParam, (ULONG)wParam);
818  }
819  return TRUE;
820  case HEM_GETBUFFER:
821  {
822  PULONG length = (PULONG)wParam;
823 
824  if (length)
825  *length = context->Length;
826 
827  return (LPARAM)context->Data;
828  }
829  case HEM_SETSEL:
830  {
831  LONG selStart = (LONG)wParam;
832  LONG selEnd = (LONG)lParam;
833 
834  if (selStart <= 0)
835  return FALSE;
836  if (selEnd > context->Length)
837  return FALSE;
838 
839  PhpHexEditScrollTo(hwnd, context, selStart);
840  PhpHexEditSetSel(hwnd, context, selStart, selEnd);
841  PhpHexEditRepositionCaret(hwnd, context, selStart);
842  REDRAW_WINDOW(hwnd);
843  }
844  return TRUE;
845  case HEM_SETEDITMODE:
846  {
847  context->CurrentMode = (LONG)wParam;
848  REDRAW_WINDOW(hwnd);
849  }
850  return TRUE;
851  case HEM_SETBYTESPERROW:
852  {
853  LONG bytesPerRow = (LONG)wParam;
854 
855  if (bytesPerRow >= 4)
856  {
857  context->BytesPerRow = bytesPerRow;
858  PhpHexEditUpdateMetrics(hwnd, context, NULL);
859  PhpHexEditUpdateScrollbars(hwnd, context);
860  PhpHexEditScrollTo(hwnd, context, context->CurrentAddress);
861  PhpHexEditRepositionCaret(hwnd, context, context->CurrentAddress);
862  REDRAW_WINDOW(hwnd);
863  }
864  }
865  return TRUE;
866  }
867 
868 DefaultHandler:
869  return DefWindowProc(hwnd, uMsg, wParam, lParam);
870 }
871 
872 FORCEINLINE VOID PhpPrintHex(
873  _In_ HDC hdc,
874  _In_ PPHP_HEXEDIT_CONTEXT Context,
875  _Inout_ PWCHAR Buffer,
876  _In_ UCHAR Byte,
877  _Inout_ PLONG X,
878  _Inout_ PLONG Y,
879  _Inout_ PULONG N
880  )
881 {
882  PWCHAR p = Buffer;
883 
884  TO_HEX(p, Byte);
885  *p++ = ' ';
886  TextOut(hdc, *X, *Y, Buffer, 3);
887  *X += Context->NullWidth * 3;
888  (*N)++;
889 
890  if (*N == Context->BytesPerRow)
891  {
892  *N = 0;
893  *X = Context->HexOffset;
894  *Y += Context->LineHeight;
895  }
896 }
897 
898 FORCEINLINE VOID PhpPrintAscii(
899  _In_ HDC hdc,
900  _In_ PPHP_HEXEDIT_CONTEXT Context,
901  _In_ UCHAR Byte,
902  _Inout_ PLONG X,
903  _Inout_ PLONG Y,
904  _Inout_ PULONG N
905  )
906 {
907  WCHAR c;
908 
909  c = IS_PRINTABLE(Byte) ? Byte : '.';
910  TextOut(hdc, *X, *Y, &c, 1);
911  *X += Context->NullWidth;
912  (*N)++;
913 
914  if (*N == Context->BytesPerRow)
915  {
916  *N = 0;
917  *X = Context->AsciiOffset;
918  *Y += Context->LineHeight;
919  }
920 }
921 
922 FORCEINLINE COLORREF GetLighterHighlightColor(
923  VOID
924  )
925 {
926  COLORREF color;
927  UCHAR r;
928  UCHAR g;
929  UCHAR b;
930 
931  color = GetSysColor(COLOR_HIGHLIGHT);
932  r = (UCHAR)color;
933  g = (UCHAR)(color >> 8);
934  b = (UCHAR)(color >> 16);
935 
936  if (r <= 255 - 64)
937  r += 64;
938  else
939  r = 255;
940 
941  if (g <= 255 - 64)
942  g += 64;
943  else
944  g = 255;
945 
946  if (b <= 255 - 64)
947  b += 64;
948  else
949  b = 255;
950 
951  return RGB(r, g, b);
952 }
953 
955  _In_ HWND hwnd,
956  _In_ PPHP_HEXEDIT_CONTEXT Context,
957  _In_opt_ HDC hdc
958  )
959 {
960  BOOLEAN freeHdc = FALSE;
961  RECT clientRect;
962  SIZE size;
963 
964  if (!hdc)
965  {
966  hdc = CreateCompatibleDC(hdc);
967  SelectObject(hdc, Context->Font);
968  freeHdc = TRUE;
969  }
970 
971  GetClientRect(hwnd, &clientRect);
972  GetCharWidth(hdc, '0', '0', &Context->NullWidth);
973  GetTextExtentPoint32(hdc, L"0", 1, &size);
974  Context->LineHeight = size.cy;
975 
976  Context->HexOffset = Context->ShowAddress ? (Context->AddressIsWide ? Context->NullWidth * 9 : Context->NullWidth * 5) : 0;
977  Context->AsciiOffset = Context->HexOffset + (Context->ShowHex ? (Context->BytesPerRow * 3 * Context->NullWidth) : 0);
978 
979  Context->LinesPerPage = clientRect.bottom / Context->LineHeight;
980  Context->HalfPage = FALSE;
981 
982  if (Context->LinesPerPage * Context->BytesPerRow > Context->Length)
983  {
984  Context->LinesPerPage = (Context->Length + Context->BytesPerRow / 2) / Context->BytesPerRow;
985 
986  if (Context->Length % Context->BytesPerRow != 0)
987  {
988  Context->HalfPage = TRUE;
989  Context->LinesPerPage++;
990  }
991  }
992 
993  if (freeHdc && hdc)
994  DeleteDC(hdc);
995 }
996 
998  _In_ HWND hwnd,
999  _In_ PPHP_HEXEDIT_CONTEXT Context,
1000  _In_ PAINTSTRUCT *PaintStruct,
1001  _In_ HDC hdc
1002  )
1003 {
1004  RECT clientRect;
1005  HDC bufferDc;
1006  HBITMAP bufferBitmap;
1007  HBITMAP oldBufferBitmap;
1008  LONG height;
1009  LONG x;
1010  LONG y;
1011  LONG i;
1012  ULONG requiredBufferLength;
1013  PWCHAR buffer;
1014 
1015  GetClientRect(hwnd, &clientRect);
1016 
1017  bufferDc = CreateCompatibleDC(hdc);
1018  bufferBitmap = CreateCompatibleBitmap(hdc, clientRect.right, clientRect.bottom);
1019  oldBufferBitmap = SelectObject(bufferDc, bufferBitmap);
1020 
1021  SetDCBrushColor(bufferDc, GetSysColor(COLOR_WINDOW));
1022  FillRect(bufferDc, &clientRect, GetStockObject(DC_BRUSH));
1023  SelectObject(bufferDc, Context->Font);
1024  SetBoundsRect(bufferDc, &clientRect, DCB_DISABLE);
1025 
1026  requiredBufferLength = (max(8, Context->BytesPerRow * 3) + 1) * sizeof(WCHAR);
1027 
1028  if (Context->CharBufferLength < requiredBufferLength)
1029  {
1030  if (Context->CharBuffer)
1031  PhFree(Context->CharBuffer);
1032 
1033  Context->CharBuffer = PhAllocate(requiredBufferLength);
1034  Context->CharBufferLength = requiredBufferLength;
1035  buffer = Context->CharBuffer;
1036  }
1037 
1038  buffer = Context->CharBuffer;
1039 
1040  if (Context->Data)
1041  {
1042  // Get character dimensions.
1043  if (Context->Update)
1044  {
1045  PhpHexEditUpdateMetrics(hwnd, Context, bufferDc);
1046  Context->Update = FALSE;
1047  PhpHexEditUpdateScrollbars(hwnd, Context);
1048  }
1049 
1050  height = (clientRect.bottom + Context->LineHeight - 1) / Context->LineHeight * Context->LineHeight; // round up to height
1051 
1052  if (Context->ShowAddress)
1053  {
1054  PH_FORMAT format;
1055  ULONG w;
1056  RECT rect;
1057 
1058  PhInitFormatX(&format, 0);
1059  format.Type |= FormatPadZeros;
1060  format.Width = Context->AddressIsWide ? 8 : 4;
1061 
1062  w = Context->AddressIsWide ? 8 : 4;
1063 
1064  rect = clientRect;
1065  rect.left = Context->AddressOffset;
1066  rect.top = 0;
1067 
1068  for (i = Context->TopIndex; i < Context->Length && rect.top < height; i += Context->BytesPerRow)
1069  {
1070  format.u.Int32 = i;
1071  PhFormatToBuffer(&format, 1, buffer, requiredBufferLength, NULL);
1072  DrawText(bufferDc, buffer, w, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP);
1073  rect.top += Context->LineHeight;
1074  }
1075  }
1076 
1077  if (Context->ShowHex)
1078  {
1079  RECT rect;
1080  LONG n = 0;
1081 
1082  x = Context->HexOffset;
1083  y = 0;
1084  rect = clientRect;
1085  rect.left = x;
1086  rect.top = 0;
1087 
1088  if (Context->SelStart != -1)
1089  {
1090  COLORREF highlightColor;
1091  LONG selStart;
1092  LONG selEnd;
1093 
1094  if (Context->CurrentMode == EDIT_HIGH || Context->CurrentMode == EDIT_LOW)
1095  highlightColor = GetSysColor(COLOR_HIGHLIGHT);
1096  else
1097  highlightColor = GetLighterHighlightColor();
1098 
1099  selStart = Context->SelStart;
1100  selEnd = Context->SelEnd;
1101 
1102  if (selStart > selEnd)
1103  {
1104  ULONG t;
1105 
1106  t = selEnd;
1107  selEnd = selStart;
1108  selStart = t;
1109  }
1110 
1111  if (selStart >= Context->Length)
1112  selStart = Context->Length - 1;
1113  if (selEnd > Context->Length)
1114  selEnd = Context->Length;
1115 
1116  // Bytes before the selection
1117 
1118  for (i = Context->TopIndex; i < selStart && y < height; i++)
1119  {
1120  PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n);
1121  }
1122 
1123  // Bytes in the selection
1124 
1125  SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1126  SetBkColor(bufferDc, highlightColor);
1127 
1128  for (; i < selEnd && i < Context->Length && y < height; i++)
1129  {
1130  PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n);
1131  }
1132 
1133  // Bytes after the selection
1134 
1135  SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT));
1136  SetBkColor(bufferDc, GetSysColor(COLOR_WINDOW));
1137 
1138  for (; i < Context->Length && y < height; i++)
1139  {
1140  PhpPrintHex(bufferDc, Context, buffer, Context->Data[i], &x, &y, &n);
1141  }
1142  }
1143  else
1144  {
1145  i = Context->TopIndex;
1146 
1147  while (i < Context->Length && rect.top < height)
1148  {
1149  PWCHAR p = buffer;
1150 
1151  for (n = 0; n < Context->BytesPerRow && i < Context->Length; n++)
1152  {
1153  TO_HEX(p, Context->Data[i]);
1154  *p++ = ' ';
1155  i++;
1156  }
1157 
1158  while (n < Context->BytesPerRow)
1159  {
1160  p[0] = ' ';
1161  p[1] = ' ';
1162  p[2] = ' ';
1163  p += 3;
1164  n++;
1165  }
1166 
1167  DrawText(bufferDc, buffer, Context->BytesPerRow * 3, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP);
1168  rect.top += Context->LineHeight;
1169  }
1170  }
1171  }
1172 
1173  if (Context->ShowAscii)
1174  {
1175  RECT rect;
1176  LONG n = 0;
1177 
1178  x = Context->AsciiOffset;
1179  y = 0;
1180  rect = clientRect;
1181  rect.left = x;
1182  rect.top = 0;
1183 
1184  if (Context->SelStart != -1)
1185  {
1186  COLORREF highlightColor;
1187  LONG selStart;
1188  LONG selEnd;
1189 
1190  if (Context->CurrentMode == EDIT_ASCII)
1191  highlightColor = GetSysColor(COLOR_HIGHLIGHT);
1192  else
1193  highlightColor = GetLighterHighlightColor();
1194 
1195  selStart = Context->SelStart;
1196  selEnd = Context->SelEnd;
1197 
1198  if (selStart > selEnd)
1199  {
1200  LONG t;
1201 
1202  t = selEnd;
1203  selEnd = selStart;
1204  selStart = t;
1205  }
1206 
1207  if (selStart >= Context->Length)
1208  selStart = Context->Length - 1;
1209  if (selEnd > Context->Length)
1210  selEnd = Context->Length;
1211 
1212  // Bytes before the selection
1213 
1214  for (i = Context->TopIndex; i < selStart && y < height; i++)
1215  {
1216  PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n);
1217  }
1218 
1219  // Bytes in the selection
1220 
1221  SetTextColor(bufferDc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1222  SetBkColor(bufferDc, highlightColor);
1223 
1224  for (; i < selEnd && i < Context->Length && y < height; i++)
1225  {
1226  PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n);
1227  }
1228 
1229  // Bytes after the selection
1230 
1231  SetTextColor(bufferDc, GetSysColor(COLOR_WINDOWTEXT));
1232  SetBkColor(bufferDc, GetSysColor(COLOR_WINDOW));
1233 
1234  for (; i < Context->Length && y < height; i++)
1235  {
1236  PhpPrintAscii(bufferDc, Context, Context->Data[i], &x, &y, &n);
1237  }
1238  }
1239  else
1240  {
1241  i = Context->TopIndex;
1242 
1243  while (i < Context->Length && rect.top < height)
1244  {
1245  PWCHAR p = buffer;
1246 
1247  for (n = 0; n < Context->BytesPerRow && i < Context->Length; n++)
1248  {
1249  *p++ = IS_PRINTABLE(Context->Data[i]) ? Context->Data[i] : '.'; // 1
1250  i++;
1251  }
1252 
1253  DrawText(bufferDc, buffer, n, &rect, DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP);
1254  rect.top += Context->LineHeight;
1255  }
1256  }
1257  }
1258  }
1259 
1260  BitBlt(hdc, 0, 0, clientRect.right, clientRect.bottom, bufferDc, 0, 0, SRCCOPY);
1261  SelectObject(bufferDc, oldBufferBitmap);
1262  DeleteObject(bufferBitmap);
1263  DeleteDC(bufferDc);
1264 }
1265 
1267  _In_ HWND hwnd,
1268  _In_ PPHP_HEXEDIT_CONTEXT Context
1269  )
1270 {
1271  SCROLLINFO si = { sizeof(si) };
1272 
1273  si.fMask = SIF_ALL;
1274  si.nMin = 0;
1275  si.nMax = (Context->Length / Context->BytesPerRow) - 1;
1276  si.nPage = Context->LinesPerPage;
1277  si.nPos = Context->TopIndex / Context->BytesPerRow;
1278  SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
1279 
1280  if (si.nMax > (LONG)si.nPage)
1281  EnableScrollBar(hwnd, SB_VERT, ESB_ENABLE_BOTH);
1282 
1283  // No horizontal scrollbar please.
1284  /*si.nMin = 0;
1285  si.nMax = ((Context->ShowAddress ? (Context->AddressIsWide ? 8 : 4) : 0) +
1286  (Context->ShowHex ? Context->BytesPerRow * 3 : 0) +
1287  (Context->ShowAscii ? Context->BytesPerRow : 0)) * Context->NullWidth;
1288  si.nPage = 1;
1289  si.nPos = 0;
1290  SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);*/
1291 }
1292 
1294  _In_ HWND hwnd,
1295  _In_ PPHP_HEXEDIT_CONTEXT Context
1296  )
1297 {
1298  DestroyCaret();
1299  CreateCaret(hwnd, NULL, Context->NullWidth * (Context->AddressIsWide ? 8 : 4), Context->LineHeight);
1300 }
1301 
1303  _In_ HWND hwnd,
1304  _In_ PPHP_HEXEDIT_CONTEXT Context
1305  )
1306 {
1307  DestroyCaret();
1308  CreateCaret(hwnd, NULL, Context->NullWidth, Context->LineHeight);
1309 }
1310 
1312  _In_ HWND hwnd,
1313  _In_ PPHP_HEXEDIT_CONTEXT Context,
1314  _In_ LONG Position
1315  )
1316 {
1317  ULONG x;
1318  ULONG y;
1319  RECT rect;
1320 
1321  x = (Position - Context->TopIndex) % Context->BytesPerRow;
1322  y = (Position - Context->TopIndex) / Context->BytesPerRow;
1323 
1324  switch (Context->CurrentMode)
1325  {
1326  case EDIT_NONE:
1327  PhpHexEditCreateAddressCaret(hwnd, Context);
1328  x = 0;
1329  break;
1330  case EDIT_HIGH:
1331  PhpHexEditCreateEditCaret(hwnd, Context);
1332  x *= Context->NullWidth * 3;
1333  x += Context->HexOffset;
1334  break;
1335  case EDIT_LOW:
1336  PhpHexEditCreateEditCaret(hwnd, Context);
1337  x *= Context->NullWidth * 3;
1338  x += Context->NullWidth;
1339  x += Context->HexOffset;
1340  break;
1341  case EDIT_ASCII:
1342  PhpHexEditCreateEditCaret(hwnd, Context);
1343  x *= Context->NullWidth;
1344  x += Context->AsciiOffset;
1345  break;
1346  }
1347 
1348  Context->EditPosition.x = x;
1349  Context->EditPosition.y = y * Context->LineHeight;
1350 
1351  GetClientRect(hwnd, &rect);
1352 
1353  if (PtInRect(&rect, Context->EditPosition))
1354  {
1355  SetCaretPos(Context->EditPosition.x, Context->EditPosition.y);
1356  ShowCaret(hwnd);
1357  }
1358 }
1359 
1361  _In_ HWND hwnd,
1362  _In_ PPHP_HEXEDIT_CONTEXT Context,
1363  _In_ LONG X,
1364  _In_ LONG Y,
1365  _Out_ POINT *Point
1366  )
1367 {
1368  LONG xp;
1369 
1370  Y /= Context->LineHeight;
1371 
1372  if (Y < 0 || Y >= Context->LinesPerPage)
1373  {
1374  Point->x = -1;
1375  Point->y = -1;
1376  return;
1377  }
1378 
1379  if (Y * Context->BytesPerRow >= Context->Length)
1380  {
1381  Point->x = -1;
1382  Point->y = -1;
1383  return;
1384  }
1385 
1386  X += Context->NullWidth;
1387  X /= Context->NullWidth;
1388 
1389  if (Context->ShowAddress && X <= (Context->AddressIsWide ? 8 : 4))
1390  {
1391  Context->CurrentAddress = Context->TopIndex + Context->BytesPerRow * Y;
1392  Context->CurrentMode = EDIT_NONE;
1393 
1394  Point->x = 0;
1395  Point->y = Y;
1396  return;
1397  }
1398 
1399  xp = Context->HexOffset / Context->NullWidth + Context->BytesPerRow * 3;
1400 
1401  if (Context->ShowHex && X < xp)
1402  {
1403  if (X % 3)
1404  X--;
1405 
1406  Context->CurrentAddress = Context->TopIndex +
1407  Context->BytesPerRow * Y +
1408  (X - (Context->HexOffset / Context->NullWidth)) / 3;
1409  Context->CurrentMode = ((X % 3) & 1) ? EDIT_LOW : EDIT_HIGH;
1410 
1411  Point->x = X;
1412  Point->y = Y;
1413  return;
1414  }
1415 
1416  X--; // fix selection problem
1417 
1418  xp = Context->AsciiOffset / Context->NullWidth + Context->BytesPerRow;
1419 
1420  if (Context->ShowAscii && X * Context->NullWidth >= Context->AsciiOffset && X <= xp)
1421  {
1422  Context->CurrentAddress = Context->TopIndex +
1423  Context->BytesPerRow * Y +
1424  (X - (Context->AsciiOffset / Context->NullWidth));
1425  Context->CurrentMode = EDIT_ASCII;
1426 
1427  Point->x = X;
1428  Point->y = Y;
1429  return;
1430  }
1431 
1432  Point->x = -1;
1433  Point->y = -1;
1434 }
1435 
1437  _In_ HWND hwnd,
1438  _In_ PPHP_HEXEDIT_CONTEXT Context,
1439  _In_ LONG X,
1440  _In_ LONG Y
1441  )
1442 {
1443  switch (Context->CurrentMode)
1444  {
1445  case EDIT_NONE:
1446  Context->CurrentAddress += Y * Context->BytesPerRow;
1447  break;
1448  case EDIT_HIGH:
1449  if (X != 0)
1450  Context->CurrentMode = EDIT_LOW;
1451  if (X == -1)
1452  Context->CurrentAddress--;
1453  Context->CurrentAddress += Y * Context->BytesPerRow;
1454  break;
1455  case EDIT_LOW:
1456  if (X != 0)
1457  Context->CurrentMode = EDIT_HIGH;
1458  if (X == 1)
1459  Context->CurrentAddress++;
1460  Context->CurrentAddress += Y * Context->BytesPerRow;
1461  break;
1462  case EDIT_ASCII:
1463  Context->CurrentAddress += X;
1464  Context->CurrentAddress += Y * Context->BytesPerRow;
1465  break;
1466  }
1467 
1468  if (Context->CurrentAddress < 0)
1469  Context->CurrentAddress = 0;
1470 
1471  if (Context->CurrentAddress >= Context->Length)
1472  {
1473  Context->CurrentAddress -= X;
1474  Context->CurrentAddress -= Y * Context->BytesPerRow;
1475  }
1476 
1477  Context->NoAddressChange = TRUE;
1478 
1479  if (Context->CurrentAddress < Context->TopIndex)
1480  SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
1481  if (Context->CurrentAddress >= Context->TopIndex + Context->LinesPerPage * Context->BytesPerRow)
1482  SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
1483 
1484  Context->NoAddressChange = FALSE;
1485  PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress);
1486 }
1487 
1489  _In_ HWND hwnd,
1490  _In_ PPHP_HEXEDIT_CONTEXT Context,
1491  _In_ LONG S,
1492  _In_ LONG E
1493  )
1494 {
1495  DestroyCaret();
1496  Context->SelStart = S;
1497  Context->SelEnd = E;
1498  REDRAW_WINDOW(hwnd);
1499 
1500  if (S != -1 && E != -1)
1501  {
1502  Context->CurrentAddress = S;
1503  }
1504  else
1505  {
1506  if (Context->EditPosition.x == 0 && Context->ShowAddress)
1507  PhpHexEditCreateAddressCaret(hwnd, Context);
1508  else
1509  PhpHexEditCreateEditCaret(hwnd, Context);
1510 
1511  SetCaretPos(Context->EditPosition.x, Context->EditPosition.y);
1512  ShowCaret(hwnd);
1513  }
1514 }
1515 
1517  _In_ HWND hwnd,
1518  _In_ PPHP_HEXEDIT_CONTEXT Context,
1519  _In_ LONG Position
1520  )
1521 {
1522  if (Position < Context->TopIndex || Position > Context->TopIndex + Context->LinesPerPage * Context->BytesPerRow)
1523  {
1524  Context->TopIndex = Position / Context->BytesPerRow * Context->BytesPerRow; // round down
1525  Context->TopIndex -= Context->LinesPerPage / 3 * Context->BytesPerRow;
1526 
1527  if (Context->TopIndex < 0)
1528  Context->TopIndex = 0;
1529 
1530  if (Context->Length >= Context->LinesPerPage * Context->BytesPerRow)
1531  {
1532  if (Context->TopIndex > Context->Length - Context->LinesPerPage * Context->BytesPerRow)
1533  Context->TopIndex = Context->Length - Context->LinesPerPage * Context->BytesPerRow;
1534  }
1535 
1536  PhpHexEditUpdateScrollbars(hwnd, Context);
1537  REDRAW_WINDOW(hwnd);
1538  }
1539 }
1540 
1542  _In_ HWND hwnd,
1543  _In_ PPHP_HEXEDIT_CONTEXT Context
1544  )
1545 {
1546  if (Context->AllowLengthChange)
1547  {
1548  Context->CurrentAddress = Context->SelStart;
1549  PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd);
1550  PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress);
1551  REDRAW_WINDOW(hwnd);
1552  }
1553 }
1554 
1556  _In_ HWND hwnd,
1557  _In_ PPHP_HEXEDIT_CONTEXT Context
1558  )
1559 {
1560  if (OpenClipboard(hwnd))
1561  {
1562  EmptyClipboard();
1563  PhpHexEditNormalizeSel(hwnd, Context);
1564 
1565  if (Context->CurrentMode != EDIT_ASCII)
1566  {
1567  ULONG length = Context->SelEnd - Context->SelStart;
1568  HGLOBAL binaryMemory;
1569  HGLOBAL hexMemory;
1570 
1571  binaryMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length);
1572 
1573  if (binaryMemory)
1574  {
1575  PUCHAR p = GlobalLock(binaryMemory);
1576  memcpy(p, &Context->Data[Context->SelStart], length);
1577  GlobalUnlock(binaryMemory);
1578 
1579  hexMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (length * 3 + 1) * sizeof(WCHAR));
1580 
1581  if (hexMemory)
1582  {
1583  PWCHAR pw;
1584  ULONG i;
1585 
1586  pw = GlobalLock(hexMemory);
1587 
1588  for (i = 0; i < length; i++)
1589  {
1590  TO_HEX(pw, Context->Data[Context->SelStart + i]);
1591  *pw++ = ' ';
1592  }
1593  *pw = 0;
1594 
1595  GlobalUnlock(hexMemory);
1596 
1597  SetClipboardData(CF_UNICODETEXT, hexMemory);
1598  }
1599 
1600  SetClipboardData(RegisterClipboardFormat(L"BinaryData"), binaryMemory);
1601  }
1602  }
1603  else
1604  {
1605  ULONG length = Context->SelEnd - Context->SelStart;
1606  HGLOBAL binaryMemory;
1607  HGLOBAL asciiMemory;
1608 
1609  binaryMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length);
1610  asciiMemory = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, length + 1);
1611 
1612  if (binaryMemory)
1613  {
1614  PUCHAR p = GlobalLock(binaryMemory);
1615  memcpy(p, &Context->Data[Context->SelStart], length);
1616  GlobalUnlock(binaryMemory);
1617 
1618  if (asciiMemory)
1619  {
1620  ULONG i;
1621 
1622  p = GlobalLock(asciiMemory);
1623  memcpy(p, &Context->Data[Context->SelStart], length);
1624 
1625  for (i = 0; i < length; i++)
1626  {
1627  if (!IS_PRINTABLE(*p))
1628  *p = '.';
1629  p++;
1630  }
1631  *p = 0;
1632 
1633  GlobalUnlock(asciiMemory);
1634 
1635  SetClipboardData(CF_TEXT, asciiMemory);
1636  }
1637 
1638  SetClipboardData(RegisterClipboardFormat(L"BinaryData"), binaryMemory);
1639  }
1640  }
1641 
1642  CloseClipboard();
1643  }
1644 }
1645 
1647  _In_ HWND hwnd,
1648  _In_ PPHP_HEXEDIT_CONTEXT Context
1649  )
1650 {
1651  if (Context->AllowLengthChange)
1652  {
1653  PhpHexEditCopyEdit(hwnd, Context);
1654  PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd);
1655  REDRAW_WINDOW(hwnd);
1656  }
1657 }
1658 
1660  _In_ HWND hwnd,
1661  _In_ PPHP_HEXEDIT_CONTEXT Context
1662  )
1663 {
1664  if (OpenClipboard(hwnd))
1665  {
1666  HANDLE memory;
1667 
1668  memory = GetClipboardData(RegisterClipboardFormat(L"BinaryData"));
1669 
1670  if (!memory)
1671  memory = GetClipboardData(CF_TEXT);
1672 
1673  if (memory)
1674  {
1675  PUCHAR p = GlobalLock(memory);
1676  ULONG length = (ULONG)GlobalSize(memory);
1677  ULONG paste;
1678  ULONG oldCurrentAddress = Context->CurrentAddress;
1679 
1680  PhpHexEditNormalizeSel(hwnd, Context);
1681 
1682  if (Context->AllowLengthChange)
1683  {
1684  if (Context->SelStart == -1)
1685  {
1686  if (Context->CurrentMode == EDIT_LOW)
1687  Context->CurrentAddress++;
1688 
1689  paste = Context->CurrentAddress;
1690  PhpHexEditSelInsert(hwnd, Context, Context->CurrentAddress, length);
1691  }
1692  else
1693  {
1694  paste = Context->SelStart;
1695  PhpHexEditSelDelete(hwnd, Context, Context->SelStart, Context->SelEnd);
1696  PhpHexEditSelInsert(hwnd, Context, paste, length);
1697  PhpHexEditSetSel(hwnd, Context, -1, -1);
1698  }
1699  }
1700  else
1701  {
1702  if (Context->SelStart == -1)
1703  {
1704  if (Context->CurrentMode == EDIT_LOW)
1705  Context->CurrentAddress++;
1706 
1707  paste = Context->CurrentAddress;
1708  }
1709  else
1710  {
1711  paste = Context->SelStart;
1712  }
1713 
1714  if (length > Context->Length - paste)
1715  length = Context->Length - paste;
1716  }
1717 
1718  memcpy(&Context->Data[paste], p, length);
1719  GlobalUnlock(memory);
1720 
1721  Context->CurrentAddress = oldCurrentAddress;
1722  REDRAW_WINDOW(hwnd);
1723  }
1724 
1725  CloseClipboard();
1726  }
1727 }
1728 
1730  _In_ HWND hwnd,
1731  _In_ PPHP_HEXEDIT_CONTEXT Context
1732  )
1733 {
1734  Context->SelStart = 0;
1735  Context->SelEnd = Context->Length;
1736  DestroyCaret();
1737  REDRAW_WINDOW(hwnd);
1738 }
1739 
1741  _In_ HWND hwnd,
1742  _In_ PPHP_HEXEDIT_CONTEXT Context
1743  )
1744 {
1745  // TODO
1746 }
1747 
1749  _In_ HWND hwnd,
1750  _In_ PPHP_HEXEDIT_CONTEXT Context
1751  )
1752 {
1753  if (Context->SelStart > Context->SelEnd)
1754  {
1755  LONG t;
1756 
1757  t = Context->SelEnd;
1758  Context->SelEnd = Context->SelStart;
1759  Context->SelStart = t;
1760  }
1761 }
1762 
1764  _In_ HWND hwnd,
1765  _In_ PPHP_HEXEDIT_CONTEXT Context,
1766  _In_ LONG S,
1767  _In_ LONG E
1768  )
1769 {
1770  if (Context->AllowLengthChange && Context->Length > 0)
1771  {
1772  PUCHAR p = PhAllocate(Context->Length - (E - S) + 1);
1773 
1774  memcpy(p, Context->Data, S);
1775 
1776  if (S < Context->Length - (E - S))
1777  memcpy(&p[S], &Context->Data[E], Context->Length - E);
1778 
1779  PhFree(Context->Data);
1780  Context->Data = p;
1781  PhpHexEditSetSel(hwnd, Context, -1, -1);
1782  Context->Length -= E - S;
1783 
1784  if (Context->CurrentAddress > Context->Length)
1785  {
1786  Context->CurrentAddress = Context->Length;
1787  PhpHexEditRepositionCaret(hwnd, Context, Context->CurrentAddress);
1788  }
1789 
1790  Context->Update = TRUE;
1791  }
1792 }
1793 
1795  _In_ HWND hwnd,
1796  _In_ PPHP_HEXEDIT_CONTEXT Context,
1797  _In_ LONG S,
1798  _In_ LONG L
1799  )
1800 {
1801  if (Context->AllowLengthChange)
1802  {
1803  PUCHAR p = PhAllocate(Context->Length + L);
1804 
1805  memset(p, 0, Context->Length + L);
1806  memcpy(p, Context->Data, S);
1807  memcpy(&p[S + L], &Context->Data[S], Context->Length - S);
1808 
1809  PhFree(Context->Data);
1810  Context->Data = p;
1811  PhpHexEditSetSel(hwnd, Context, -1, -1);
1812  Context->Length += L;
1813 
1814  Context->Update = TRUE;
1815  }
1816 }
1817 
1819  _In_ HWND hwnd,
1820  _In_ PPHP_HEXEDIT_CONTEXT Context,
1821  _In_ PUCHAR Data,
1822  _In_ ULONG Length
1823  )
1824 {
1825  Context->Data = Data;
1826  PhpHexEditSetSel(hwnd, Context, -1, -1);
1827  Context->Length = Length;
1828  Context->CurrentAddress = 0;
1829  Context->EditPosition.x = Context->EditPosition.y = 0;
1830  Context->CurrentMode = EDIT_HIGH;
1831  Context->TopIndex = 0;
1832  Context->Update = TRUE;
1833 
1834  Context->UserBuffer = TRUE;
1835  Context->AllowLengthChange = FALSE;
1836 }
1837 
1839  _In_ HWND hwnd,
1840  _In_ PPHP_HEXEDIT_CONTEXT Context,
1841  _In_ PUCHAR Data,
1842  _In_ ULONG Length
1843  )
1844 {
1845  if (Context->Data) PhFree(Context->Data);
1846  Context->Data = PhAllocate(Length);
1847  memcpy(Context->Data, Data, Length);
1848  PhpHexEditSetBuffer(hwnd, Context, Context->Data, Length);
1849  Context->UserBuffer = FALSE;
1850  Context->AllowLengthChange = TRUE;
1851 }