uglobal
  hSplitterDumper dd ?
  Dumper  TDumper
  hDumperPopupMenu dd ?
endg

cDefaultDumperFontWidth = 6
cDefaultDumperFontHeight = 8

iglobal
  cDefaultDumperFont du 'Terminal', 0

  CoolMenu  DumperPopupMenu,    \
    mfNormal, actDumpAddress
endg

proc InitDumperWindow, .hParent
.DumperFont dd ?
begin
        xor     ebx, ebx

        invoke  CreateFontW, cDefaultDumperFontHeight, cDefaultDumperFontWidth, ebx, ebx, FW_DONTCARE,                \
                            ebx, ebx, ebx,                                                   \
                            1, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,     \
                            FIXED_PITCH or FF_DONTCARE, cDefaultDumperFont
        mov     esi, eax

        invoke   CreateWindowExW, ebx, cDataGridClassName, NULL,                  \
                 WS_CHILD or WS_HSCROLL or WS_VSCROLL,                           \
                 ebx, ebx , 400, ebx,                                            \
                 [.hParent],                                                     \
                 ebx, [hInstance],                                               \
                 Dumper

        mov     ebx, eax

        invoke  SendMessageW, ebx, WM_SETFONT, esi, FALSE
        stdcall SubclassWindow, ebx, DumperWinProc

        mov     [Dumper.Flags], dgfGridLinesFixed
        mov     [Dumper.OnDrawItem], TDumper.DrawCell

        stdcall TDataGrid.SetCounts, Dumper, 18, 100
        stdcall TDataGrid.SetColumnsWidth, Dumper, 1, 16, 16
        stdcall TDataGrid.SetColumnsWidth, Dumper, 17, 17, 100

        stdcall CreateCoolMenu, DumperPopupMenu, TRUE
        mov     [hDumperPopupMenu], eax

        return
endp



proc DumperWinProc, .hwnd, .wmsg, .wparam, .lparam
begin

        mov     ebx, [.wmsg]
        dispatch ebx

.ondefault:
        stc
        return

oncase WM_DESTROY
        invoke  DestroyMenu, [hDumperPopupMenu]

        cmp     [Dumper.ptrData], 0
        je      .ondefault

        stdcall FreeMem, [Dumper.ptrData]
        stc
        return

oncase WM_RBUTTONDOWN
locals
  .pnt POINT
endl
        lea     eax, [.pnt]
        invoke  GetCursorPos, eax

        invoke  TrackPopupMenu, [hDumperPopupMenu], TPM_LEFTBUTTON or TPM_LEFTBUTTON,   \
                [.pnt.x],[.pnt.y], 0, [hEditorsHost], NULL
        xor     eax, eax
        clc
        return


oncase DMM_SHOW
        invoke  ShowWindow, [hSplitterDumper], [.wparam]
        invoke  ShowWindow, [.hwnd], [.wparam]
        invoke  GetParent, [.hwnd]
        clc
        return


        enddispatch
endp


iglobal
  cDumpTitle du 'Memory dump', 0
endg

proc OnDumpAddress, .wparam, .lparam
begin

        stdcall CreateForm, dlgGotoAddr, [hMainForm]

        invoke  SendMessageW, ebx, WM_SETTEXT, 0, cDumpTitle

        stdcall ShowModal, ebx, MSF_CENTER
        cmp     eax, MR_OK
        jne     .finish

        stdcall GetDlgItemTextUtf8, ebx, 100
        push    eax eax

        invoke  DestroyWindow, ebx

        stdcall StrToFasmNum ; from the stack
        stdcall StrDel ; from the stack

        stdcall TDumper.DumpAddress, eax, [dbgPInfo.hProcess]

        invoke  InvalidateRect, [Dumper.hwnd], 0, 0
        mov     [Dumper.fShadowReady], 0

.finish:
        return
endp





; todo: Check if the [.addr] is inside existing buffer and to not allocate
; new buffer.

proc TDumper.DumpAddress, .addr, .hProcess
.buffer dd ?
begin
        push    esi edi ebx

        mov     esi, [.addr]
        and     esi, $fffff000  ; address of the begining of the page.
        mov     [Dumper.pointer], esi

; determine the size of the memory block.
        lea     edi, [.buffer]
        lea     ebx, [esi-4096]
.getsz:
        add     ebx, 4096
        invoke  ReadProcessMemory, [.hProcess], ebx, edi, 1, NULL
        test    eax, eax
        jnz     .getsz

; set grid size.
        sub     ebx, esi

        mov     ecx, ebx
        sar     ecx, 4
        inc     ecx
        stdcall TDataGrid.SetCounts, Dumper, [Dumper.ColCount], ecx

        cmp     [Dumper.ptrData], 0
        je      .bufferok

        stdcall FreeMem, [Dumper.ptrData]
        mov     [Dumper.ptrData], 0

.bufferok:
; allocate memory buffer
        stdcall GetMem, ebx
        jc      .dataok

        mov     edi, eax

; read memory from the debugged process.
        invoke  ReadProcessMemory, [.hProcess], esi, edi, ebx, NULL
        test    eax, eax
        jz      .dataok

        mov     [Dumper.ptrData], edi
        mov     [Dumper.DataSize], ebx

.dataok:
; Set focused cell.
        mov     eax, [.addr]
        mov     ecx, eax
        sub     eax, [Dumper.pointer]
        and     ecx, $0f
        sar     eax, 4
        inc     ecx
        inc     eax

        mov     [Dumper.focused.x], ecx
        mov     [Dumper.focused.y], eax

        stdcall TDataGrid.GetCellXY, Dumper, ecx, eax
        push    edx

        invoke  SetScrollPos, [Dumper.hwnd], SB_HORZ, eax, TRUE
        pop     edx
        invoke  SetScrollPos, [Dumper.hwnd], SB_VERT, edx, TRUE

        invoke  InvalidateRect, [Dumper.hwnd], NULL, FALSE
        pop     ebx edi esi
        return
endp


uglobal
  DumperBuffer rw 32
endg




proc TDumper.DrawCell, .hdc, .ptrRect, .Col, .Row, .dummy, .fSelected, .fFixed
begin
        push    esi ebx

; Choose text color
        mov     eax, [edi+TDataGrid.clTextFixed]
        cmp     [.fFixed], 0
        jne     .colorok

        mov     eax, [edi+TDataGrid.clTextSelected]
        cmp     [.fSelected], FALSE
        jne     .colorok

        mov     eax, [edi+TDataGrid.clTextCell]

.colorok:
        invoke  SetTextColor, [.hdc], eax

        cmp     [.fFixed], 0
        jne     .fixed

; Data..maybe
        cmp     [Dumper.ptrData], 0
        je      .drawok

        cmp     [.Col], 17      ; ASCII field
        je      .ascii

; yes it is data cell
        mov     eax, [.Row]
        mov     ecx, [.Col]
        dec     eax
        dec     ecx
        shl     eax, 4
        add     ecx, [Dumper.ptrData]

        movzx   eax, byte [eax+ecx]


        stdcall NumToStr, eax, ntsHex or ntsFixedWidth or 2
        jmp     .drawit

.ascii:
        push    edi

        mov     esi, [.Row]
        dec     esi
        shl     esi, 4
        add     esi, [Dumper.ptrData]
        mov     edi, DumperBuffer
        mov     ecx, $10
        xor     eax, eax

.copy:
        lodsb
        cmp     al, ' '
        jae     .store

        mov     ax, '.'

.store:
        stosw
        loop    .copy

        pop     edi

        invoke  DrawTextW, [.hdc], DumperBuffer, 16, [.ptrRect], DT_VCENTER or DT_CENTER or DT_SINGLELINE or DT_NOPREFIX
        jmp     .drawok

.fixed:
        cmp     [.Row], 0
        je      .offset

; address...
        mov     eax, [.Row]
        dec     eax
        shl     eax, 4  ; *16 bytes on row
        add     eax, [edi+TDumper.pointer]
        stdcall NumToStr, eax, ntsHex or ntsUnsigned or ntsFixedWidth or 8
        jmp     .drawit

.offset:
        mov     eax, [.Col]
        test    eax, eax
        jz      .drawok

        lea     eax, [DumperOffsets+4*eax]
        stdcall StrDup, eax

.drawit:
        push    eax

        stdcall utf8ToWideChar, eax
        stdcall StrDel ; from the stack
        push    eax

        invoke  DrawTextW, [.hdc], eax, -1, [.ptrRect], DT_VCENTER or DT_CENTER or DT_SINGLELINE
        stdcall FreeMem ; from the stack

.drawok:
        pop     ebx esi
        return
endp



iglobal
  DumperOffsets  dd      0, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'ASCI', 'I'
endg