visualboyadvance-m/src/win32/MemoryViewer.cpp

623 lines
13 KiB
C++

// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// MemoryViewer.cpp : implementation file
//
#include "stdafx.h"
#include "vba.h"
#include "MemoryViewer.h"
#include "../System.h"
extern int emulating;
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// MemoryViewer
bool MemoryViewer::isRegistered = false;
MemoryViewer::MemoryViewer()
{
address = 0;
addressSize = 0;
dataSize = 0;
editAddress = 0;
editNibble = 0;
displayedLines = 0;
hasCaret = false;
maxNibble = 0;
font = (HFONT)GetStockObject(SYSTEM_FIXED_FONT);
fontSize.cx = fontSize.cy = 0;
beginAscii = 0;
beginHex = 0;
dlg = NULL;
registerClass();
}
MemoryViewer::~MemoryViewer()
{
}
BEGIN_MESSAGE_MAP(MemoryViewer, CWnd)
//{{AFX_MSG_MAP(MemoryViewer)
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_VSCROLL()
ON_WM_GETDLGCODE()
ON_WM_LBUTTONDOWN()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_KEYDOWN()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_CHAR, OnWMChar)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// MemoryViewer message handlers
void MemoryViewer::setDialog(IMemoryViewerDlg *d)
{
dlg = d;
}
void MemoryViewer::setAddress(u32 a)
{
address = a;
if(displayedLines) {
if(addressSize) {
u16 addr = address;
if((u16)(addr+(displayedLines<<4)) < addr) {
address = 0xffff - (displayedLines<<4) + 1;
}
} else {
if((address+(displayedLines<<4)) < address) {
address = 0xffffffff - (displayedLines<<4) + 1;
}
}
}
if(addressSize)
address &= 0xffff;
setCaretPos();
InvalidateRect(NULL, TRUE);
}
void MemoryViewer::setSize(int s)
{
dataSize = s;
if(s == 0)
maxNibble = 1;
else if(s == 1)
maxNibble = 3;
else
maxNibble = 7;
InvalidateRect(NULL, TRUE);
}
BOOL MemoryViewer::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
void MemoryViewer::updateScrollInfo(int lines)
{
int page = lines * 16;
SCROLLINFO si;
ZeroMemory(&si, sizeof(si));
si.cbSize = sizeof(si);
si.fMask = SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL | SIF_POS;
si.nMin = 0;
if(addressSize) {
si.nMax = 0x10000/page;
si.nPage = 1;
} else {
si.nMax = 0xa000000 / page;
si.nPage = page;
}
si.nPos = address / page;
SetScrollInfo(SB_VERT,
&si,
TRUE);
}
void MemoryViewer::OnPaint()
{
CPaintDC dc(this); // device context for painting
RECT rect;
GetClientRect(&rect);
int w = rect.right - rect.left;
int h = rect.bottom - rect.top - 6;
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap bitmap, *pOldBitmap;
bitmap.CreateCompatibleBitmap(&dc, w, rect.bottom - rect.top);
pOldBitmap = memDC.SelectObject(&bitmap);
memDC.FillRect(&rect, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
memDC.DrawEdge(&rect, EDGE_ETCHED, BF_RECT);
CFont *oldFont = memDC.SelectObject(CFont::FromHandle(font));
fontSize = memDC.GetTextExtent("0", 1);
int lines = h / fontSize.cy;
displayedLines = lines;
updateScrollInfo(lines);
u32 addr = address;
memDC.SetTextColor(RGB(0,0,0));
u8 data[32];
RECT r;
r.top = 3;
r.left = 3;
r.bottom = r.top+fontSize.cy;
r.right = rect.right-3;
int line = 0;
for(int i = 0; i < lines; i++) {
CString buffer;
if(addressSize)
buffer.Format("%04X", addr);
else
buffer.Format("%08X", addr);
memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX);
r.left += 10*fontSize.cx;
beginHex = r.left;
readData(addr, 16, data);
int j;
if(dataSize == 0) {
for(j = 0; j < 16; j++) {
buffer.Format("%02X", data[j]);
memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX);
r.left += 3*fontSize.cx;
}
}
if(dataSize == 1) {
for(j = 0; j < 16; j += 2) {
buffer.Format("%04X", data[j] | data[j+1]<<8);
memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX);
r.left += 5*fontSize.cx;
}
}
if(dataSize == 2) {
for(j = 0; j < 16; j += 4) {
buffer.Format("%08X", data[j] | data[j+1]<<8 |
data[j+2] << 16 | data[j+3] << 24);
memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX);
r.left += 9*fontSize.cx;
}
}
line = r.left;
r.left += fontSize.cx;
beginAscii = r.left;
buffer.Empty();
for(j = 0; j < 16; j++) {
char c = data[j];
if(c >= 32 && c <= 127) {
buffer += c;
} else
buffer += '.';
}
memDC.DrawText(buffer, &r, DT_TOP | DT_LEFT | DT_NOPREFIX);
addr += 16;
if(addressSize)
addr &= 0xffff;
r.top += fontSize.cy;
r.bottom += fontSize.cy;
r.left = 3;
}
CPen pen;
pen.CreatePen(PS_SOLID, 1, RGB(0,0,0));
CPen *old = memDC.SelectObject(&pen);
memDC.MoveTo(3+fontSize.cx*9, 3);
memDC.LineTo(3+fontSize.cx*9, 3+displayedLines*fontSize.cy);
memDC.MoveTo(line, 3);
memDC.LineTo(line, 3+displayedLines*fontSize.cy);
memDC.SelectObject(old);
pen.DeleteObject();
memDC.SelectObject(oldFont);
dc.BitBlt(0, 0, w, rect.bottom - rect.top, &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(pOldBitmap);
memDC.DeleteDC();
bitmap.DeleteObject();
}
void MemoryViewer::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
int address = this->address;
switch(nSBCode) {
case SB_BOTTOM:
address = 0xffffff00;
break;
case SB_LINEDOWN:
address += 0x10;
break;
case SB_LINEUP:
address -= 0x10;
break;
case SB_PAGEDOWN:
address += (displayedLines<<4);
break;
case SB_PAGEUP:
address -= (displayedLines<<4);
break;
case SB_TOP:
address = 0;
break;
case SB_THUMBTRACK:
{
int page = displayedLines * 16;
SCROLLINFO si;
ZeroMemory(&si, sizeof(si));
si.cbSize = sizeof(si);
si.fMask = SIF_TRACKPOS;
GetScrollInfo(SB_VERT, &si);
address = page * si.nTrackPos;
}
break;
}
setAddress(address);
}
UINT MemoryViewer::OnGetDlgCode()
{
return DLGC_WANTALLKEYS;
}
void MemoryViewer::createEditCaret(int w, int h)
{
if(!hasCaret || caretWidth != w || caretHeight != h) {
hasCaret = true;
caretWidth = w;
caretHeight = h;
::CreateCaret(m_hWnd, (HBITMAP)0, w, h);
}
}
void MemoryViewer::destroyEditCaret()
{
hasCaret = false;
DestroyCaret();
}
void MemoryViewer::setCaretPos()
{
if(GetFocus() != this) {
destroyEditCaret();
return;
}
if(dlg)
dlg->setCurrentAddress(editAddress);
if(editAddress < address || editAddress > (address -1 + (displayedLines<<4))) {
destroyEditCaret();
return;
}
int subAddress = (editAddress - address);
int x = 3+10*fontSize.cx+editNibble*fontSize.cx;
int y = 3+fontSize.cy*((editAddress-address)>>4);
if(editAscii) {
x = beginAscii + fontSize.cx*(subAddress&15);
} else {
switch(dataSize) {
case 0:
x += 3*fontSize.cx*(subAddress & 15);
break;
case 1:
x += 5*fontSize.cx*((subAddress>>1) & 7);
break;
case 2:
x += 9*fontSize.cx*((subAddress>>2) & 3);
break;
}
}
RECT r;
GetClientRect(&r);
r.right -= 3;
if(x >= r.right) {
destroyEditCaret();
return;
}
int w = fontSize.cx;
if((x+fontSize.cx)>=r.right)
w = r.right - x;
createEditCaret(w, fontSize.cy);
::SetCaretPos(x, y);
ShowCaret();
}
void MemoryViewer::OnLButtonDown(UINT nFlags, CPoint point)
{
int x = point.x;
int y = point.y;
int line = (y-3)/fontSize.cy;
int beforeAscii = beginHex;
int inc = 1;
int sub = 3*fontSize.cx;
switch(dataSize) {
case 0:
beforeAscii += 47*fontSize.cx;
break;
case 1:
beforeAscii += 39*fontSize.cx;
inc = 2;
sub = 5*fontSize.cx;
break;
case 2:
beforeAscii += 35*fontSize.cx;
inc = 4;
sub = 9*fontSize.cx;
break;
}
editAddress = address + (line<<4);
if(x >= beginHex && x < beforeAscii) {
x -= beginHex;
editNibble = 0;
while(x > 0) {
x -= sub;
if(x >= 0)
editAddress += inc;
else {
editNibble = (x + sub)/fontSize.cx;
}
}
editAscii = false;
} else if(x >= beginAscii) {
int afterAscii = beginAscii+16*fontSize.cx;
if(x >= afterAscii)
x = afterAscii-1;
editAddress += (x-beginAscii)/fontSize.cx;
editNibble = 0;
editAscii = true;
} else {
return;
}
if(editNibble > maxNibble)
editNibble = maxNibble;
SetFocus();
setCaretPos();
}
void MemoryViewer::OnSetFocus(CWnd* pOldWnd)
{
setCaretPos();
InvalidateRect(NULL, TRUE);
}
void MemoryViewer::OnKillFocus(CWnd* pNewWnd)
{
destroyEditCaret();
InvalidateRect(NULL, TRUE);
}
void MemoryViewer::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
bool isShift = (GetKeyState(VK_SHIFT) & 0x80000000) == 0x80000000;
switch(nChar) {
case VK_RIGHT:
if(editAscii)
moveAddress(1,0);
else if(isShift)
moveAddress((maxNibble+1)>>1,0);
else
moveAddress(0, 1);
break;
case VK_LEFT:
if(editAscii)
moveAddress(-1, 0);
else if(isShift)
moveAddress(-((maxNibble+1)>>1),0);
else
moveAddress(0, -1);
break;
case VK_DOWN:
moveAddress(16, 0);
break;
case VK_UP:
moveAddress(-16, 0);
break;
case VK_TAB:
GetNextDlgTabItem(GetParent(), isShift)->SetFocus();
break;
}
}
void MemoryViewer::moveAddress(s32 offset, int nibbleOff)
{
if(offset == 0) {
if(nibbleOff == -1) {
editNibble--;
if(editNibble == -1) {
editAddress -= (maxNibble + 1) >> 1;
editNibble = maxNibble;
}
if(address == 0 && (editAddress >= (u32)(displayedLines<<4))) {
editAddress = 0;
editNibble = 0;
beep();
}
if(editAddress < address)
setAddress(address - 16);
} else {
editNibble++;
if(editNibble > maxNibble) {
editNibble = 0;
editAddress += (maxNibble + 1) >> 1;
}
if(editAddress < address) {
editAddress -= (maxNibble + 1) >> 1;
editNibble = maxNibble;
beep();
}
if(editAddress >= (address+(displayedLines<<4)))
setAddress(address+16);
}
} else {
editAddress += offset;
if(offset < 0 && editAddress > (address-1+(displayedLines<<4))) {
editAddress -= offset;
beep();
return;
}
if(offset > 0 && (editAddress < address)) {
editAddress -= offset;
beep();
return;
}
if(editAddress < address) {
if(offset & 15)
setAddress((address+offset-16) & ~15);
else
setAddress(address+offset);
} else if(editAddress > (address - 1 + (displayedLines<<4))) {
if(offset & 15)
setAddress((address+offset+16) & ~15);
else
setAddress(address+offset);
}
}
setCaretPos();
}
LRESULT MemoryViewer::OnWMChar(WPARAM wParam, LPARAM LPARAM)
{ // The WM_CHAR message uses Unicode Transformation Format (UTF)-16.
if(OnEditInput((UINT)(wParam & 0xFFFF)))
return 0;
return 1;
}
bool MemoryViewer::OnEditInput(UINT c)
{
if(c > 255 || !emulating) {
beep();
return false;
}
if(!editAscii)
c = tolower(c);
u32 value = 256;
if(c >= 'a' && c <= 'f')
value = 10 + (c - 'a');
else if(c >= '0' && c <= '9')
value = (c - '0');
if(editAscii) {
editData(editAddress, 8, 0, c);
moveAddress(1, 0);
InvalidateRect(NULL, TRUE);
} else {
if(value != 256) {
value <<= 4*(maxNibble-editNibble);
u32 mask = ~(15 << 4*(maxNibble - editNibble));
switch(dataSize) {
case 0:
editData(editAddress, 8, mask, value);
break;
case 1:
editData(editAddress, 16, mask, value);
break;
case 2:
editData(editAddress, 32, mask, value);
break;
}
moveAddress(0, 1);
InvalidateRect(NULL, TRUE);
}
}
return true;
}
void MemoryViewer::beep()
{
MessageBeep((UINT)-1);
}
void MemoryViewer::registerClass()
{
if(!isRegistered) {
WNDCLASS wc;
ZeroMemory(&wc, sizeof(wc));
wc.style = CS_PARENTDC | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
wc.lpfnWndProc = (WNDPROC)::DefWindowProc;
wc.hInstance = AfxGetInstanceHandle();
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH )GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = "VbaMemoryViewer";
AfxRegisterClass(&wc);
isRegistered = true;
}
}
void MemoryViewer::setAddressSize(int s)
{
addressSize = s;
}
u32 MemoryViewer::getCurrentAddress()
{
return editAddress;
}
int MemoryViewer::getSize()
{
return dataSize;
}