Skip to content

Commit

Permalink
Show changed bytes in hex view
Browse files Browse the repository at this point in the history
fixes #3
  • Loading branch information
learn-more committed Sep 25, 2023
1 parent f9291f5 commit 2b64073
Showing 1 changed file with 98 additions and 22 deletions.
120 changes: 98 additions & 22 deletions src/MemView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
* PROJECT: MemView
* LICENSE: MIT (https://spdx.org/licenses/MIT)
* PURPOSE: The hex-dump window
* COPYRIGHT: Copyright 2021 Mark Jansen <[email protected]>
* COPYRIGHT: Copyright 2021-2023 Mark Jansen <[email protected]>
*/

#include "MemView.h"
#include "MemInfo.h"
#include <algorithm>

extern HINSTANCE g_hInst;
const UINT_PTR kUpdateTimerId = 0x1ea4;

// http://www.catch22.net/tuts/scrollbars-scrolling

Expand All @@ -20,8 +21,8 @@ struct MemView
, ProcessHandle(NULL), DisplayLines(0), PerLine(16)
, ScrollMax(0), ScrollPos(0)
, vMax(0), vPos(0), vInc(0)
, Dirty(true)
, FontX(0), FontY(0), FontNL(0)
, Dirty(true), Resizing(true), Scrolling(false)
, FontX(0), FontY(0)
{
updateTotal();
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &WheelLines, 0);
Expand Down Expand Up @@ -50,23 +51,49 @@ struct MemView
int WheelLines;

bool Dirty;
bool Resizing;
bool Scrolling;
std::vector<unsigned char> Buffer;
std::vector<bool> Changed;

int FontX;
int FontY;
int FontNL;
};

static WCHAR Hex2Str[] = L"0123456789abcdef";

void ToBuffer(WCHAR* Buffer, size_t Cch, unsigned char* Data, SIZE_T DataLen, SIZE_T PerLine, PBYTE Address)
static void DrawLine(HDC hdc, int x, int y, WCHAR* Buffer, size_t Cch, const std::vector<unsigned char>& DataBuffer, const std::vector<bool>& Changed, SIZE_T StartAt, SIZE_T DataLen, SIZE_T PerLine, PBYTE Address)
{
StringCchPrintfW(Buffer, Cch, L"%p: ", Address);
WCHAR* p = Buffer + wcslen(Buffer);
WCHAR* Current = Buffer;
bool CurrentChanged = false;
const unsigned char* Data = DataBuffer.data() + StartAt;

for(size_t n = 0; n < PerLine; ++n)
{
if (n < DataLen)
{
// Check of we crossed the boundary from changed to unchanged
if (CurrentChanged != Changed[StartAt + n])
{
// Do we have new text?
if (Current != p)
{
// Draw it out first
TextOutW(hdc, x, y, Current, (int)(p - Current));
// Calculate the width
RECT r = {0};
DrawTextW(hdc, Current, (int)(p - Current), &r, DT_CALCRECT);
x += r.right;
// Save the new starting position
Current = p;
}
CurrentChanged = Changed[StartAt + n];
COLORREF col = GetTextColor(hdc);
SetTextColor(hdc, CurrentChanged ? RGB(255, 0, 0) : RGB(0,0,0));
}

*(p++) = Hex2Str[Data[n] >> 4];
*(p++) = Hex2Str[Data[n] & 0xf];
*(p++) = ' ';
Expand All @@ -84,16 +111,38 @@ void ToBuffer(WCHAR* Buffer, size_t Cch, unsigned char* Data, SIZE_T DataLen, SI
{
if (n < DataLen)
{
// Check of we crossed the boundary from changed to unchanged
if (CurrentChanged != Changed[StartAt + n])
{
if (Current != p)
{
TextOutW(hdc, x, y, Current, (int)(p - Current));
RECT r = {0};
DrawTextW(hdc, Current, (int)(p - Current), &r, DT_CALCRECT);
x += r.right;
Current = p;
}
CurrentChanged = Changed[StartAt + n];
COLORREF col = GetTextColor(hdc);
SetTextColor(hdc, CurrentChanged ? RGB(255, 0, 0) : RGB(0,0,0));
}

if (isprint(Data[n]))
*(p++) = (char)Data[n];
else
*(p++) = '.';
}
}
*p = 0;

// Do we have any text left over?
if (Current != p)
TextOutW(hdc, x, y, Current, (int)(p - Current));

SetTextColor(hdc, RGB(0,0,0));
}

static void ReadMemory(HWND hwnd, MemView* mv)
static void ReadMemory(HWND hwnd, MemView* mv, bool IsWmPaint)
{
SIZE_T Requested = mv->Buffer.size();
SIZE_T Read = 0;
Expand All @@ -106,11 +155,28 @@ static void ReadMemory(HWND hwnd, MemView* mv)
Requested -= ((Requested + start)-end);

std::vector<unsigned char> buf = mv->Buffer;
std::vector<bool> changed = mv->Changed;
ReadProcessMemory(mv->ProcessHandle, start, mv->Buffer.data(), Requested, &Read);
mv->Dirty = false;
mv->Changed.resize(mv->Buffer.size());
if (buf != mv->Buffer)
{
InvalidateRect(hwnd, NULL, FALSE);
// Some data changed, see which bytes are different
for (size_t n = 0; n < mv->Buffer.size(); ++n)
{
if (buf[n] != mv->Buffer[n])
mv->Changed[n] = true;
}
// Force a redraw if we are not inside WM_PAINT
if (!IsWmPaint)
InvalidateRect(hwnd, NULL, FALSE);
}
else
{
mv->Changed.assign(mv->Changed.size(), false);
// If the 'changed' state changed, we also need to redraw
if (changed != mv->Changed && !IsWmPaint)
InvalidateRect(hwnd, NULL, FALSE);
}
if (Requested != Read)
{
Expand All @@ -126,7 +192,14 @@ LRESULT HandleWM_PAINT(HWND hwnd, MemView* mv)
HDC hdc = BeginPaint(hwnd, &ps);

if (mv->Dirty)
ReadMemory(hwnd, mv);
ReadMemory(hwnd, mv, true);

if (mv->Resizing || mv->Scrolling)
{
// Don't show changing bytes when scrolling / resizing
mv->Changed.assign(mv->Changed.size(), false);
mv->Resizing = mv->Scrolling = false;
}

WCHAR Buffer[512];
SelectObject(hdc, getFont());
Expand All @@ -135,8 +208,7 @@ LRESULT HandleWM_PAINT(HWND hwnd, MemView* mv)
{
size_t offset = (mv->vPos*PerLine) + (n*PerLine);
size_t Left = std::min<size_t>(PerLine, mv->Info.size() - offset);
ToBuffer(Buffer, _countof(Buffer), mv->Buffer.data() + (n*PerLine), Left, PerLine, mv->Info.start() + offset);
TextOutW(hdc, 2, mv->FontY * (int)n, Buffer, (int)wcslen(Buffer));
DrawLine(hdc, 2, mv->FontY * (int)n, Buffer, _countof(Buffer), mv->Buffer, mv->Changed, (n*PerLine), Left, PerLine, mv->Info.start() + offset);
}

EndPaint(hwnd, &ps);
Expand Down Expand Up @@ -170,9 +242,11 @@ void HandleWM_SIZE(HWND hwnd, MemView* mv, LPARAM lParam)
}
mv->ScrollPos = std::min(mv->ScrollPos, mv->ScrollMax);
mv->vPos = std::min((long)mv->ScrollPos * mv->vInc, mv->vMax);
mv->Resizing = true;
mv->Dirty = true;
SetScrollRange(hwnd, SB_VERT, 0, mv->ScrollMax, FALSE);
SetScrollPos(hwnd, SB_VERT, mv->ScrollPos, TRUE);
InvalidateRect(hwnd, NULL, TRUE);
}

static void HandleWM_VSCROLL(HWND hwnd, MemView* mv, WPARAM wParam, LPARAM lParam)
Expand Down Expand Up @@ -222,6 +296,7 @@ static void HandleWM_VSCROLL(HWND hwnd, MemView* mv, WPARAM wParam, LPARAM lPara
mv->ScrollPos += nVscrollInc;
mv->vPos = std::min((long)mv->ScrollPos * mv->vInc, mv->vMax);
mv->Dirty = true;
mv->Scrolling = true;
SetScrollPos(hwnd, SB_VERT, mv->ScrollPos, TRUE);
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
Expand All @@ -236,7 +311,6 @@ static void CreateFont(HWND hwnd, MemView* mv)
GetTextMetrics(hdc, &tm);
mv->FontX = tm.tmAveCharWidth;
mv->FontY = tm.tmHeight + tm.tmExternalLeading;
mv->FontNL = tm.tmHeight;
ReleaseDC(hwnd, hdc);
}

Expand All @@ -254,35 +328,37 @@ static void SetPtr(HWND hwnd, MemView* mv)

LRESULT CALLBACK MemWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
MemView* mv;
switch (uMsg)
{
case WM_CREATE:
{
WCHAR Buffer[512];
MemView* mv = static_cast<MemView*>(((LPCREATESTRUCT)lParam)->lpCreateParams);
mv = static_cast<MemView*>(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetPtr(hwnd, mv);
StringCchPrintfW(Buffer, _countof(Buffer), L"%s (%u), %p - %p",
mv->ProcessName.c_str(), mv->ProcessPid, mv->Info.start(), mv->Info.start() + mv->Info.size());
SetWindowTextW(hwnd, Buffer);
CreateFont(hwnd, mv);
//ReadMemory(hwnd, mv);
SetTimer(hwnd, 0x1ea4, 1000, NULL);
SetTimer(hwnd, kUpdateTimerId, 1000, NULL);
}
break;
case WM_DESTROY:
{
KillTimer(hwnd, 0x1ea4);
MemView* mv = GetPtr(hwnd);
KillTimer(hwnd, kUpdateTimerId);
mv = GetPtr(hwnd);
SetPtr(hwnd, NULL);
CloseHandle(mv->ProcessHandle);
delete mv;
SetFocus(GetParent(hwnd));
}
break;

// fix vertical stuff, then this works
//case WM_ERASEBKGND:
// return 0;
case WM_ERASEBKGND:
mv = GetPtr(hwnd);
// If we are not resizing, skip erasebkgrnd to reduce flicker
if (mv && !mv->Resizing)
return 1;
break;

case WM_PAINT:
HandleWM_PAINT(hwnd, GetPtr(hwnd));
Expand All @@ -301,9 +377,9 @@ LRESULT CALLBACK MemWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
break;

case WM_TIMER:
if (wParam == 0x1ea4)
if (wParam == kUpdateTimerId)
{
ReadMemory(hwnd, GetPtr(hwnd));
ReadMemory(hwnd, GetPtr(hwnd), false);
}
break;
}
Expand Down

0 comments on commit 2b64073

Please sign in to comment.