/* This file is part of BKBTL.
BKBTL is free software: you can redistribute it and/or modify it under the terms
of the GNU Lesser General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
BKBTL 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with
BKBTL. If not, see . */
// SpriteView.cpp
#include "stdafx.h"
#include
#include "Main.h"
#include "Views.h"
#include "ToolWindow.h"
#include "Dialogs.h"
#include "Emulator.h"
#include "emubase/Emubase.h"
//////////////////////////////////////////////////////////////////////
HWND g_hwndSprite = (HWND)INVALID_HANDLE_VALUE; // Sprite view window handler
WNDPROC m_wndprocSpriteToolWindow = NULL; // Old window proc address of the ToolWindow
HWND m_hwndSpriteViewer = (HWND)INVALID_HANDLE_VALUE;
HDRAWDIB m_hSpriteDrawDib = NULL;
BITMAPINFO m_bmpinfoSprite;
HBITMAP m_hSpriteBitmap = NULL;
DWORD * m_pSprite_bits = NULL;
const int m_nSprite_scale = 2;
const int m_nSprite_ImageCX = 262;
const int m_nSprite_ImageCY = 256;
const int m_nSprite_ViewCX = m_nSprite_ImageCX * m_nSprite_scale;
const int m_nSprite_ViewCY = m_nSprite_ImageCY * m_nSprite_scale;
WORD m_wSprite_BaseAddress = 0;
int m_nSprite_width = 2;
int m_nSprite_PageSizeBytes = m_nSprite_ImageCY * (m_nSprite_ImageCX / (8 + 2));
void SpriteView_OnDraw(HDC hdc);
BOOL SpriteView_OnKeyDown(WPARAM vkey, LPARAM lParam);
BOOL SpriteView_OnVScroll(WPARAM wParam, LPARAM lParam);
BOOL SpriteView_OnMouseWheel(WPARAM wParam, LPARAM lParam);
void SpriteView_InitBitmap();
void SpriteView_DoneBitmap();
void SpriteView_UpdateWindowText();
void SpriteView_PrepareBitmap();
void SpriteView_UpdateScrollPos();
//////////////////////////////////////////////////////////////////////
void SpriteView_RegisterClass()
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = SpriteViewViewerWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = g_hInst;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = CLASSNAME_SPRITEVIEW;
wcex.hIconSm = NULL;
RegisterClassEx(&wcex);
}
void SpriteView_Create(int x, int y)
{
int cxBorder = ::GetSystemMetrics(SM_CXDLGFRAME);
int cyBorder = ::GetSystemMetrics(SM_CYDLGFRAME);
int cxScroll = ::GetSystemMetrics(SM_CXVSCROLL);
int cyCaption = ::GetSystemMetrics(SM_CYSMCAPTION);
int width = m_nSprite_ViewCX + cxScroll + cxBorder * 2;
int height = m_nSprite_ViewCY + cyBorder * 2 + cyCaption;
g_hwndSprite = CreateWindowEx(
WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
CLASSNAME_OVERLAPPEDWINDOW, _T("Sprite Viewer"),
WS_POPUPWINDOW | WS_CAPTION | WS_VISIBLE,
x, y, width, height,
NULL, NULL, g_hInst, NULL);
// ToolWindow subclassing
m_wndprocSpriteToolWindow = (WNDPROC)LongToPtr(SetWindowLongPtr(
g_hwndSprite, GWLP_WNDPROC, PtrToLong(SpriteViewWndProc)));
RECT rcClient; GetClientRect(g_hwndSprite, &rcClient);
m_hwndSpriteViewer = CreateWindow(
CLASSNAME_SPRITEVIEW, NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP,
0, 0, rcClient.right, rcClient.bottom,
g_hwndSprite, NULL, g_hInst, NULL);
m_wSprite_BaseAddress = Settings_GetSpriteAddress();
m_nSprite_width = (int)Settings_GetSpriteWidth();
if (m_nSprite_width <= 0) m_nSprite_width = 1;
if (m_nSprite_width > 32) m_nSprite_width = 32;
SpriteView_InitBitmap();
SpriteView_UpdateWindowText();
SpriteView_UpdateScrollPos();
::SetFocus(m_hwndSpriteViewer);
}
void SpriteView_InitBitmap()
{
m_hSpriteDrawDib = DrawDibOpen();
HDC hdc = GetDC(g_hwnd);
m_bmpinfoSprite.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_bmpinfoSprite.bmiHeader.biWidth = m_nSprite_ImageCX;
m_bmpinfoSprite.bmiHeader.biHeight = m_nSprite_ImageCY;
m_bmpinfoSprite.bmiHeader.biPlanes = 1;
m_bmpinfoSprite.bmiHeader.biBitCount = 32;
m_bmpinfoSprite.bmiHeader.biCompression = BI_RGB;
m_bmpinfoSprite.bmiHeader.biSizeImage = 0;
m_bmpinfoSprite.bmiHeader.biXPelsPerMeter = 0;
m_bmpinfoSprite.bmiHeader.biYPelsPerMeter = 0;
m_bmpinfoSprite.bmiHeader.biClrUsed = 0;
m_bmpinfoSprite.bmiHeader.biClrImportant = 0;
m_hSpriteBitmap = CreateDIBSection(hdc, &m_bmpinfoSprite, DIB_RGB_COLORS, (void **)&m_pSprite_bits, NULL, 0);
VERIFY(::ReleaseDC(g_hwnd, hdc));
}
void SpriteView_DoneBitmap()
{
if (m_hSpriteBitmap != NULL)
{
VERIFY(::DeleteObject(m_hSpriteBitmap)); m_hSpriteBitmap = NULL;
}
DrawDibClose(m_hSpriteDrawDib);
}
LRESULT CALLBACK SpriteViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_SETFOCUS:
::SetFocus(m_hwndSpriteViewer);
break;
case WM_DESTROY:
g_hwndSprite = (HWND)INVALID_HANDLE_VALUE; // We are closed! Bye-bye!..
return CallWindowProc(m_wndprocSpriteToolWindow, hWnd, message, wParam, lParam);
default:
return CallWindowProc(m_wndprocSpriteToolWindow, hWnd, message, wParam, lParam);
}
return (LRESULT)FALSE;
}
LRESULT CALLBACK SpriteViewViewerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
SpriteView_OnDraw(hdc); // Draw memory dump
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
// Free resources
SpriteView_DoneBitmap();
return DefWindowProc(hWnd, message, wParam, lParam);
case WM_LBUTTONDOWN:
SetFocus(hWnd);
break;
case WM_KEYDOWN:
return (LRESULT)SpriteView_OnKeyDown(wParam, lParam);
case WM_VSCROLL:
return (LRESULT)SpriteView_OnVScroll(wParam, lParam);
case WM_MOUSEWHEEL:
return (LRESULT)SpriteView_OnMouseWheel(wParam, lParam);
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return (LRESULT)FALSE;
}
void SpriteView_OnDraw(HDC hdc)
{
ASSERT(g_pBoard != NULL);
SpriteView_PrepareBitmap();
DrawDibDraw(m_hSpriteDrawDib, hdc,
0, 0,
m_nSprite_ImageCX * m_nSprite_scale, m_nSprite_ImageCY * m_nSprite_scale,
&m_bmpinfoSprite.bmiHeader, m_pSprite_bits, 0, 0,
m_nSprite_ImageCX, m_nSprite_ImageCY,
0);
}
void SpriteView_GoToAddress(WORD address)
{
if (address == m_wSprite_BaseAddress)
return;
m_wSprite_BaseAddress = address;
Settings_SetSpriteAddress(m_wSprite_BaseAddress);
SpriteView_UpdateWindowText();
SpriteView_PrepareBitmap();
InvalidateRect(m_hwndSpriteViewer, NULL, TRUE);
SpriteView_UpdateScrollPos();
}
void SpriteView_SetSpriteWidth(int width)
{
if (width < 1) width = 1;
if (width > m_nSprite_ImageCX / 8) width = m_nSprite_ImageCX / 8;
if (width == m_nSprite_width)
return;
m_nSprite_width = width;
Settings_SetSpriteWidth((WORD)width);
SpriteView_UpdateWindowText();
SpriteView_PrepareBitmap();
InvalidateRect(m_hwndSpriteViewer, NULL, TRUE);
SpriteView_UpdateScrollPos();
}
void SpriteView_UpdateWindowText()
{
TCHAR buffer[48];
_sntprintf(buffer, sizeof(buffer) / sizeof(TCHAR) - 1,
_T("Sprite Viewer - address %06o, width %d"), m_wSprite_BaseAddress, m_nSprite_width);
::SetWindowText(g_hwndSprite, buffer);
}
BOOL SpriteView_OnKeyDown(WPARAM vkey, LPARAM /*lParam*/)
{
switch (vkey)
{
case VK_LEFT:
SpriteView_GoToAddress(m_wSprite_BaseAddress - (WORD)(m_nSprite_ImageCY * m_nSprite_width));
break;
case VK_RIGHT:
SpriteView_GoToAddress(m_wSprite_BaseAddress + (WORD)(m_nSprite_ImageCY * m_nSprite_width));
break;
case VK_UP:
if (GetKeyState(VK_CONTROL) & 0x8000)
SpriteView_GoToAddress(m_wSprite_BaseAddress - (WORD)m_nSprite_width);
else
SpriteView_GoToAddress(m_wSprite_BaseAddress - 1);
break;
case VK_DOWN:
if (GetKeyState(VK_CONTROL) & 0x8000)
SpriteView_GoToAddress(m_wSprite_BaseAddress + (WORD)m_nSprite_width);
else
SpriteView_GoToAddress(m_wSprite_BaseAddress + 1);
break;
case VK_OEM_4: // '[' -- Decrement Sprite Width
SpriteView_SetSpriteWidth(m_nSprite_width - 1);
break;
case VK_OEM_6: // ']' -- Increment Sprite Width
SpriteView_SetSpriteWidth(m_nSprite_width + 1);
break;
case 0x47: // 'G' - Go To Address
{
WORD value = m_wSprite_BaseAddress;
if (InputBoxOctal(m_hwndSpriteViewer, _T("Go To Address"), &value))
SpriteView_GoToAddress(value);
break;
}
case VK_HOME:
SpriteView_GoToAddress(0);
break;
case VK_END:
SpriteView_GoToAddress((WORD)(0x10000 - 1));
break;
case VK_PRIOR:
SpriteView_GoToAddress(m_wSprite_BaseAddress - (WORD)m_nSprite_PageSizeBytes);
break;
case VK_NEXT:
SpriteView_GoToAddress(m_wSprite_BaseAddress + (WORD)m_nSprite_PageSizeBytes);
break;
default:
return TRUE;
}
return FALSE;
}
BOOL SpriteView_OnMouseWheel(WPARAM wParam, LPARAM /*lParam*/)
{
short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
int nDelta = zDelta / 120;
if (nDelta > 4) nDelta = 4;
if (nDelta < -4) nDelta = -4;
SpriteView_GoToAddress(m_wSprite_BaseAddress - (WORD)(nDelta * 3 * m_nSprite_width));
return FALSE;
}
BOOL SpriteView_OnVScroll(WPARAM wParam, LPARAM /*lParam*/)
{
//WORD scrollpos = HIWORD(wParam);
WORD scrollcmd = LOWORD(wParam);
switch (scrollcmd)
{
case SB_LINEDOWN:
SpriteView_GoToAddress(m_wSprite_BaseAddress + (WORD)m_nSprite_width);
break;
case SB_LINEUP:
SpriteView_GoToAddress(m_wSprite_BaseAddress - (WORD)m_nSprite_width);
break;
case SB_PAGEDOWN:
SpriteView_GoToAddress(m_wSprite_BaseAddress + (WORD)m_nSprite_PageSizeBytes);
break;
case SB_PAGEUP:
SpriteView_GoToAddress(m_wSprite_BaseAddress - (WORD)m_nSprite_PageSizeBytes);
break;
case SB_THUMBPOSITION:
{
WORD scrollpos = HIWORD(wParam);
SpriteView_GoToAddress(scrollpos);
}
break;
}
return FALSE;
}
void SpriteView_UpdateScrollPos()
{
int columns = 0;
int cx = m_nSprite_ImageCX;
while (cx > m_nSprite_width * 8)
{
columns++;
cx -= m_nSprite_width * 8;
if (cx <= 2)
break;
cx -= 2;
}
m_nSprite_PageSizeBytes = m_nSprite_ImageCY * columns * m_nSprite_width;
SCROLLINFO si;
ZeroMemory(&si, sizeof(si));
si.cbSize = sizeof(si);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
si.nPage = m_nSprite_PageSizeBytes;
si.nPos = m_wSprite_BaseAddress;
si.nMin = 0;
si.nMax = 0x10000 - 1 + m_nSprite_PageSizeBytes;
SetScrollInfo(m_hwndSpriteViewer, SB_VERT, &si, TRUE);
}
void SpriteView_PrepareBitmap()
{
// Clear bitmap
memset(m_pSprite_bits, 0x7f, m_nSprite_ImageCX * m_nSprite_ImageCY * 4);
WORD address = m_wSprite_BaseAddress;
for (int x = 0; x + m_nSprite_width * 8 <= m_nSprite_ImageCX; x += m_nSprite_width * 8 + 2)
{
for (int y = 0; y < m_nSprite_ImageCY; y++)
{
DWORD* pBits = m_pSprite_bits + (m_nSprite_ImageCY - 1 - y) * m_nSprite_ImageCX + x;
for (int w = 0; w < m_nSprite_width; w++)
{
// Get byte from memory
int addrtype = 0;
bool okHalt = g_pBoard->GetCPU()->IsHaltMode();
WORD value = g_pBoard->GetWordView(address & ~1, okHalt, FALSE, &addrtype);
if (address & 1)
value = value >> 8;
for (int i = 0; i < 8; i++)
{
COLORREF color = (value & 1) ? 0xffffff : 0;
*pBits = color;
pBits++;
value = value >> 1;
}
address++;
}
}
}
}
//////////////////////////////////////////////////////////////////////