/* 
CCIDE
Copyright 2001-2011 David Lindauer.

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 of the License, 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

You may contact the author at:
	mailto::camille@bluegrass.net
 */
#define STRICT
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <richedit.h>
#include <stdio.h>

#include "header.h"
#include "operands.h"
#include "opcodes.h"
#include <ctype.h>
extern HWND hwndSourceTab;
extern HINSTANCE hInstance;
extern HWND hwndClient, hwndStatus, hwndFrame;
extern PROCESS DebugProcess;
extern enum DebugState uState;
extern THREAD *StoppedThread;
extern int changedProject;
char *memhist[MAX_COMBO_HISTORY];
int memoryWordSize = 1;

HWND hwndDump;

static char szDumpClassName[] = "xccMemoryClass";
static char szDumpBlankClassName[] = "xccMemoryClass2";
static HWND hwndCtrl, hwndBlank, hwndCombo, hwndEdit;
static HBRUSH hbrBackground;
static WNDPROC oldproc;
static int cursrow, curscol, curpos;
static char cursmod[32];
static int modifying;
static int oldDumpAddress;

static LOGFONT fontdata = 
{
    16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH |
        FF_DONTCARE, "Courier New"
};
static LOGFONT Normalfontdata = 
{
    14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN |
        FF_DONTCARE, "Arial"
};

HFONT DumpFont;
static HFONT staticFont;
static int DumpAddress;
static char *szMemTitle = "Memory Window";
LRESULT CALLBACK historyComboProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam);

void SetCursMode(void)
{
        switch(memoryWordSize)
        {
                case 1:
                        strcpy(cursmod, "  ");
                        break;
                case 2:
                        strcpy(cursmod, "    ");
                        break;
                case 4:
                        strcpy(cursmod, "        ");
                        break;
        }
}
int GetDumpAddress(char *buf, BOOL error)
{
    int addr, newaddr;
    int stars = 0;
    while (*buf == ' ')
        buf++;
    //   while (*buf == '*') buf++,stars++ ;
    #ifdef XXXXX
        if (isdigit(buf[0]))
        {
            if (buf[0] == '0' && buf[1] == 'x' || buf[1] == 'X')
                sscanf(buf + 2, "%x", &addr);
            else
                sscanf(buf, "%d", &addr);
        }
        else
    #endif 
    {
        char *types,  *syms;
        int offset = StoppedThread->regs.Eip, l;
        DEBUG_INFO *dbg;
        VARINFO *var;
        var = EvalExpr(&types, &syms, &dbg, offset, &offset, buf, error);
        if (var)
        {
            if (var->constant)
                addr = var->ival;
            else if (var->address < 0x1000)
            {
                char data[20];
//                if (!var->explicitreg)
//                    ExtendedMessageBox("Address Error", MB_SETFOREGROUND |
//                        MB_SYSTEMMODAL, 
//                        "Address is a register.  Using its value as the address.");
                ReadValue(var->address, &data, 4, &StoppedThread->regs);
                addr = *(int*)data;
            }
            else
                addr = var->address;
            FreeVarInfo(var);
        }
        else
        {
            addr = 0;
        }
        SetFocus(hwndCombo);
    }
    //   newaddr = addr ;
    //   while (stars) {
    //      if (!ReadProcessMemory(DebugProcess.hProcess,(LPVOID)newaddr,(LPVOID)&newaddr, 4, 0) )
    //         break ;
    //      stars-- ;
    //   }
    //   if (!stars)
    //      addr = newaddr ;

    return addr;
}

//-------------------------------------------------------------------------

void DumpDoPaint(HWND hwnd, int focussed)
{
    int i;
    char buf[256];
    unsigned char charbuf[100];
    PAINTSTRUCT ps;
    CONTEXT context;
    HDC dc;
    HFONT oldFont;
    COLORREF oldbk, oldtxt;
    RECT rect;
    int lines;
    int chars;
    memset(charbuf, 0, sizeof(charbuf));
    GetClientRect(hwnd, &rect);
    lines = (rect.bottom - rect.top) / 16;
    chars = (rect.right - rect.left - 10 * 8) / 8 / 4;
    chars /= memoryWordSize;
    chars *= memoryWordSize;
    dc = BeginPaint(hwnd, &ps);
    SelectObject(dc, DumpFont);
    for (i = 0; i < lines; i++)
    {
        int j;
        sprintf(buf, "%08X: ", DumpAddress + i * chars);
        TextOut(dc, 0, i *16+rect.top, buf, strlen(buf));
        for (j = 0; j < chars; j+= memoryWordSize)
        {
			int k;
            if (i == cursrow && j == curscol && focussed)
            {
                oldbk = GetBkColor(dc);
                oldtxt = SetTextColor(dc, oldbk);
                if (modifying)
                    SetBkColor(dc, 0xff0000);
                else
                    SetBkColor(dc, oldtxt);
            }
            if (ReadProcessMemory(DebugProcess.hProcess, (LPVOID)(DumpAddress +
                i * chars + j), (LPVOID)(charbuf + j), memoryWordSize, 0))
            {
                switch(memoryWordSize)
                {
                        case 1:
                sprintf(buf, "%02X ", charbuf[j]);
                                break;
                        case 2:
                sprintf(buf, "%04X ", (*(short *)(charbuf+j)));
                                break;
                        case 4:
                sprintf(buf, "%08X ", (*(int *)(charbuf+j)));
                                break;
                }
                if (i == cursrow && j == curscol)
                    if (modifying)
                        strcpy(buf, cursmod);
            }
            else
            {
                if (i == cursrow && j == curscol)
                {
                    modifying = FALSE;
                    curpos = 0;
                    SetCursMode();
                }
                switch(memoryWordSize)
                {
                        case 1:
                strcpy(buf, "?? ");
                                break;
                        case 2:
                strcpy(buf, "???? ");
                                break;
                        case 4:
                strcpy(buf, "???????? ");
                                break;
                }
                charbuf[j] = '.';
            }
            if (i == cursrow && j == curscol && focussed && modifying)
                TextOut(dc, (10+ (j * 2 + j/memoryWordSize)) *8-4, 
                                                i *16+rect.top, buf, 2 * memoryWordSize);
            else
                TextOut(dc, (10+ (j * 2 + j/memoryWordSize)) *8, 
                                                i *16+rect.top, buf, 2 * memoryWordSize);
                        
            for (k=0; k < memoryWordSize; k++)
                if (charbuf[j+k] < 32 || charbuf[j+k] > 126)
                    charbuf[j+k] = '.';
            if (i == cursrow && j == curscol && focussed)
            {
                SetTextColor(dc, oldtxt);
                SetBkColor(dc, oldbk);
            }
            if (i == cursrow && j == curscol && focussed && modifying)
                TextOut(dc, (10 + memoryWordSize * 2 + 
                            (j * 2 + j/memoryWordSize)) *8-4, 
                             i *16+rect.top, " ", 1);
            else
                TextOut(dc, (10 + memoryWordSize * 2 + 
                            (j * 2 + j/memoryWordSize)) *8, 
                             i *16+rect.top, " ", 1);
        }
        TextOut(dc, (10+ (j * 2 + j/memoryWordSize)) *8, i *16+rect.top, charbuf, strlen(charbuf));
    }

    SelectObject(dc, oldFont);

    EndPaint(hwnd, &ps);
}

//-------------------------------------------------------------------------

LRESULT CALLBACK _export DumpBlankProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    RECT r;
    int lines, chars;
    static int focussed;
    switch (iMessage)
    {
        case WM_CREATE:
            SetScrollRange(hwnd, SB_VERT, 0, 64000, FALSE);
            SetScrollPos(hwnd, SB_VERT, 32000, TRUE);
            break;
        case WM_PAINT:
            DumpDoPaint(hwnd, focussed);
            return 0;
        case WM_SETFOCUS:
            //         SendMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0) ;
            focussed = TRUE;
            InvalidateRect(hwnd, 0, 1);
            break;
        case WM_KILLFOCUS:
            focussed = FALSE;
            modifying = FALSE;
            SetCursMode();
            InvalidateRect(hwnd, 0, 1);
            break;
        case WM_RBUTTONDOWN:
            {
                HMENU menu = LoadMenu(hInstance, "MEMORYBYTESIZEMENU");
                                POINT pos;
                HMENU popup = GetSubMenu(menu, 0);
                GetCursorPos(&pos);

                TrackPopupMenuEx(popup, TPM_BOTTOMALIGN | TPM_LEFTBUTTON, pos.x,
                    pos.y, hwnd, NULL);
                DestroyMenu(menu);
            }
            return 0;
	    case WM_COMMAND:
            switch(wParam)
            {
                    case IDM_BYTE:
                            memoryWordSize = 1;
                            break;
                    case IDM_WORD:
                            memoryWordSize = 2;
                            break;
                    case IDM_DWORD:
                            memoryWordSize = 4;
                            break;
            }
            changedProject = TRUE;
            modifying = FALSE;
            InvalidateRect(hwndBlank, 0, 1);
            break;
        case WM_LBUTTONDOWN:
            lines = r.bottom / 16;
            chars = ((r.right - 10 * 8) / 8 / 4 / memoryWordSize) 
                                                * memoryWordSize;
            SetCursMode();
            curpos = 0;
            modifying = FALSE;
            if (LOWORD(lParam) >= 10 *8 && LOWORD(lParam) < (10+chars) *8 * (1 + 2 * memoryWordSize))
            {
                curscol = (LOWORD(lParam) - 10 * 8) / (1 + memoryWordSize * 2) / 8;
                                curscol *= memoryWordSize;
                cursrow = HIWORD(lParam) / 16;
                if (focussed)
                    InvalidateRect(hwnd, 0, 1);
            }
            SetFocus(hwnd);
            break;
        case WM_KEYDOWN:
            GetClientRect(hwnd, &r);
            lines = r.bottom / 16;
            chars = ((r.right - 10 * 8) / 8 / 4 / memoryWordSize) * memoryWordSize;
            switch (wParam)
            {
            case VK_BACK:
                if (modifying)
                {
                    cursmod[curpos] = ' ';
                    if (curpos)
                        curpos--;
                    InvalidateRect(hwnd, 0, 1);
                }
                break;
            case VK_LEFT:
                if (modifying)
                {
                    cursmod[curpos] = ' ';
                    if (curpos)
                        curpos--;
                    InvalidateRect(hwnd, 0, 1);
                    break;
                }
                if (curscol != 0)
                {
                    curscol-= memoryWordSize;
                    InvalidateRect(hwnd, 0, 1);
                    break;
                }
                curscol = chars - memoryWordSize;
                // FALL THROUGH
            case VK_UP:
                modifying = FALSE;
				SetCursMode();
                curpos = 0;
                if (cursrow == 0)
                    SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
                else
                {
                    cursrow--;
                    InvalidateRect(hwnd, 0, 1);
                }
                break;
            case VK_RIGHT:
                if (modifying)
                {
                    if (curpos < memoryWordSize * 2)
                        curpos++;
                    break;
                }
                if (curscol + memoryWordSize < chars)
                {
                    curscol+=memoryWordSize;
                    InvalidateRect(hwnd, 0, 1);
                    break;
                }
                curscol = 0;
                // FALL THROUGH
            case VK_DOWN:
                modifying = FALSE;
				SetCursMode();
                curpos = 0;
                if (cursrow + 1 >= lines)
                    SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
                else
                {
                    cursrow++;
                    InvalidateRect(hwnd, 0, 1);
                }
                break;
            case VK_RETURN:
                if (modifying)
                {
                    MEMORY_BASIC_INFORMATION mbi;
                    DWORD dwOldProtect;
                    unsigned v = 0;
                    int address = DumpAddress + cursrow * chars + curscol;
                    sscanf(cursmod, "%x", &v);
                    GetClientRect(hwnd, &r);
                    lines = r.bottom / 16;
                    chars = (r.right - 10 * 8) / 8 / 4;
                    // so it will work on the code page if protected...
                    VirtualQueryEx(DebugProcess.hProcess, (LPVOID)address, &mbi,
                        sizeof(mbi));
                    VirtualProtectEx(DebugProcess.hProcess, mbi.BaseAddress,
                        mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
                    if (!WriteProcessMemory(DebugProcess.hProcess, (LPVOID)
                        address, (LPVOID) &v, memoryWordSize, 0))
                        ExtendedMessageBox("Memory Window", MB_SETFOREGROUND |
                            MB_SYSTEMMODAL, 
                            "Could not write process memory with new value");
                    VirtualProtectEx(DebugProcess.hProcess, mbi.BaseAddress,
                        mbi.RegionSize, mbi.Protect, &dwOldProtect);
                    FlushInstructionCache(DebugProcess.hProcess, (LPVOID)
                        address, 1);
                    modifying = FALSE;
                    curpos = 0;
					SetCursMode();
                    InvalidateRect(hwnd, 0, 1);
                }
                break;
            case VK_ESCAPE:
                modifying = FALSE;
                curpos = 0;
				SetCursMode();
                InvalidateRect(hwnd, 0, 1);
                break;
            case VK_NEXT:
                SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
                break;
            case VK_PRIOR:
                SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
                break;
            }
            break;
        case WM_CHAR:
            if (isxdigit(wParam))
            {
                modifying = TRUE;
                cursmod[curpos] = toupper(wParam);
                if (curpos < memoryWordSize * 2)
                    curpos++;
                InvalidateRect(hwnd, 0, 0);
            }
            break;
        case WM_VSCROLL:
            GetClientRect(hwnd, &r);
            lines = r.bottom / 16;
            chars = (r.right - 10 * 8) / 8 / 4;
            switch (LOWORD(wParam))
            {
            case SB_BOTTOM:
                DumpAddress =  - (lines *chars);
                break;
            case SB_TOP:
                DumpAddress = 0;
                break;
            case SB_ENDSCROLL:
                return 0;
            case SB_LINEDOWN:
                DumpAddress += chars;
                break;
            case SB_LINEUP:
                DumpAddress -= chars;
                break;
            case SB_PAGEDOWN:
                DumpAddress += chars *(lines - 1);
                break;
            case SB_PAGEUP:
                DumpAddress -= chars *(lines - 1);
                break;
            case SB_THUMBPOSITION:
                DumpAddress = oldDumpAddress + (HIWORD(wParam) - 32000) *16;
                oldDumpAddress = DumpAddress;
                break;
            case SB_THUMBTRACK:
                DumpAddress = oldDumpAddress + (HIWORD(wParam) - 32000) *16;
                break;
                //					return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
            }
            InvalidateRect(hwnd, 0, 1);
            SetScrollPos(hwnd, SB_VERT, 32000, TRUE);
            return 0;
    }
    return DefWindowProc(hwnd, iMessage, wParam, lParam);
}

//-------------------------------------------------------------------------

LRESULT CALLBACK EditHook(HWND hwnd, UINT iMessage, WPARAM wParam, LPARAM
    lParam)
{
    switch (iMessage)
    {
        case WM_COMMAND:
        {
                char *buf;
                switch(wParam)
                {
                        case IDM_EAX:
                                buf = "eax";
                                break;
                        case IDM_EBX:
                                buf = "ebx";
                                break;
                        case IDM_ECX:
                                buf = "ecx";
                                break;
                        case IDM_EDX:
                                buf = "edx";
                                break;
                        case IDM_ESP:
                                buf = "esp";
                                break;
                        case IDM_EBP:
                                buf = "ebp";
                                break;
                        case IDM_ESI:
                                buf = "esi";
                                break;
                        case IDM_EDI:
                                buf = "edi";
                                break;
                }
                if (buf)
                {
                        SendMessage(hwnd, WM_SETTEXT, 0 , (LPARAM)buf);
        				SendMessage(hwndDump, WM_COMMAND, IDC_RETURN, 0);
                }
        }
                break;
        case WM_RBUTTONDOWN:
        {
	        HMENU menu = LoadMenu(hInstance, "MEMORYREGISTERMENU");
	                        POINT pos;
	        HMENU popup = GetSubMenu(menu, 0);
	        GetCursorPos(&pos);
	
	        TrackPopupMenuEx(popup, TPM_BOTTOMALIGN | TPM_LEFTBUTTON, pos.x,
	            pos.y, hwnd, NULL);
	        DestroyMenu(menu);
        }
            return 0;
        case WM_KEYDOWN:
            switch (wParam)
            {
            case VK_RETURN:
                if (GetKeyState(VK_SHIFT) &0x80000000)
                    return SendMessage(hwndBlank, iMessage, wParam, lParam);
                else
                    return SendMessage(hwndDump, WM_COMMAND, IDC_RETURN, 0);
            case VK_LEFT:
            case VK_RIGHT:
            case VK_UP:
            case VK_DOWN:
                if (GetKeyState(VK_SHIFT) &0x80000000)
                    return SendMessage(hwndBlank, iMessage, wParam, lParam);
                break;
            case VK_NEXT:
            case VK_PRIOR:
                return SendMessage(hwndBlank, iMessage, wParam, lParam);
            }
            break;
        case WM_KEYUP:
        case WM_CHAR:
            if (wParam == VK_RETURN)
                return 0;
            if (wParam == VK_TAB)
                return 0;
            break;
    }
    return CallWindowProc(oldproc, hwnd, iMessage, wParam, lParam);
}

//-------------------------------------------------------------------------

LRESULT CALLBACK _export DumpProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    static HWND hwndStatic;
    static int charwidth;
    char buf[256];
    int i, lines, chars;
    HDC dc;
    TEXTMETRIC metric;
    DEBUG_EVENT *xc;
    HFONT oldFont;
    LOGBRUSH brushstr;
    RECT r;
    switch (iMessage)
    {
        case WM_MDIACTIVATE:
            SendMessage(hwndCtrl, LCF_SETACTIVE, 0, lParam == (LPARAM)hwnd);
            if (lParam == (LPARAM)hwnd)
            {
                SendMessage(hwndSourceTab, WM_SETACTIVETAB, 0, (LPARAM)hwnd);
                SetFocus(hwndBlank);
            }
            return TRUE;
        case WM_SETFOCUS:
            break;
        case WM_SYSCOMMAND:
            if (wParam == SC_CLOSE)
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            break;
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            case IDM_GOTO:
                DumpAddress = lParam;
                oldDumpAddress = DumpAddress;
                InvalidateRect(hwndBlank, 0, 1);
                break;
            case IDC_RETURN:
                SendMessage(hwndEdit, WM_GETTEXT, 256, (LPARAM)buf);
                DumpAddress = GetDumpAddress(buf, TRUE);
                oldDumpAddress = DumpAddress;
                InvalidateRect(hwndBlank, 0, 1);
                SendMessage(hwndCombo, WM_SAVEHISTORY, 0, 0);
                break;
            default:
                return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
            }
            break;
        case WM_CTLCOLORSTATIC:
            SetBkColor((HDC)wParam, GetSysColor(COLOR_INACTIVEBORDER));
            return hbrBackground;
        case WM_PAINT:
            break;
        case WM_KILLFOCUS:
            break;
        case WM_LBUTTONDOWN:
            break;
        case WM_RESTACK:
            SendMessage(hwndEdit, WM_GETTEXT, 256, (LPARAM)buf);
            DumpAddress = GetDumpAddress(buf, FALSE);
            oldDumpAddress = DumpAddress;
            InvalidateRect(hwndBlank, 0, 1);
            break;
        case WM_CREATE:
            hwndDump = hwnd;
            modifying = 0;
            cursrow = curscol = curpos = 0;
			SetCursMode();
            DumpFont = CreateFontIndirect(&fontdata);
            staticFont = CreateFontIndirect(&Normalfontdata);
            GetClientRect(hwnd, &r);
            hwndCtrl = CreateControlWindow(DID_MEMWND, hwnd, &r, (int)(
                (LPMDICREATESTRUCT)(*(int*)lParam))->lParam, szMemTitle);
            SendMessage(hwndCtrl, LCF_ADJUSTRECT, 0, (LPARAM) &r);
            hwndStatic = CreateWindow("STATIC", "Address:", WS_CHILD +
                WS_VISIBLE, r.left + 20, r.top + 2, 48, 20,
                hwnd, 0, hInstance, 0);
            SendMessage(hwndStatic, WM_SETFONT, (LPARAM)staticFont, 1);
            hwndCombo = CreateWindow("COMBOBOX", "", WS_CHILD + WS_CLIPSIBLINGS +
                WS_BORDER + WS_VISIBLE + CBS_DROPDOWN + CBS_AUTOHSCROLL, 
                                r.left + 70, r.top, 200, 100, hwndCtrl, 0, hInstance, 0);
            SendMessage(hwndCombo, WM_SETFONT, (LPARAM)DumpFont, 1);
            SubClassHistoryCombo(hwndCombo);
            SendMessage(hwndCombo, WM_SETHISTORY, 0, (LPARAM)memhist);
            hwndBlank = CreateWindow(szDumpBlankClassName, 0, WS_CHILD +
                WS_VSCROLL + WS_CLIPSIBLINGS + WS_BORDER + WS_VISIBLE, r.left,
                r.top + 24, r.right - r.left, r.bottom - r.top - 24, hwndCtrl,
                0, hInstance, 0);
            {
                POINT pt;
                pt.x = pt.y = 5;
                hwndEdit = ChildWindowFromPoint(hwndCombo, pt);
                oldproc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (int)EditHook);
            }
            break;

        case WM_CLOSE:
            dmgrHideWindow(DID_MEMWND, TRUE);
            return 0;
        case WM_DESTROY:
            dmgrRemoveClient((CCW_params*)GetWindowLong(hwndCtrl, 0));
            DestroyWindow(hwndBlank);
            hwndDump = 0;
            DeleteObject(DumpFont);
            break;
        case WM_SIZE:
            r.left = r.top = 0;
            r.right = LOWORD(lParam);
            r.bottom = HIWORD(lParam);
            MoveWindow(hwndCtrl, 0, 0, r.right, r.bottom, TRUE);
            SendMessage(hwndCtrl, LCF_ADJUSTRECT, 0, (LPARAM) &r);
            MoveWindow(hwndStatic, r.left + 20, r.top + 2, 48, 20, TRUE);
            MoveWindow(hwndCombo, r.left + 70, r.top, 200, 20, TRUE);
            MoveWindow(hwndBlank, r.left, r.top + 24, r.right - r.left,
                r.bottom - r.top - 24, TRUE);
            break;
        case WM_INITMENUPOPUP:
            return 0;
        default:
            break;
    }
    return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
}

//-------------------------------------------------------------------------

void RegisterDumpWindow(void)
{
    WNDCLASS wc;
    memset(&wc, 0, sizeof(wc));
    wc.style = CS_HREDRAW + CS_VREDRAW;
    wc.lpfnWndProc = &DumpProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    hbrBackground = wc.hbrBackground = CreateSolidBrush(GetSysColor
        (COLOR_INACTIVEBORDER));
    wc.lpszMenuName = 0;
    wc.lpszClassName = szDumpClassName;
    RegisterClass(&wc);

    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpfnWndProc = &DumpBlankProc;
    wc.lpszClassName = szDumpBlankClassName;
    RegisterClass(&wc);
}

//-------------------------------------------------------------------------

HWND CreateDumpWindow(void)
{
    MDICREATESTRUCT mc;
    HWND rv;
    RECT r;
    if (hwndDump)
    {
        SendMessage(hwndDump, WM_SETFOCUS, 0, 0);
        return hwndDump;
    }
    GetClientRect(hwndClient, &r);
    mc.szClass = szDumpClassName;
    mc.szTitle = szMemTitle;
    mc.hOwner = hInstance;
    mc.x = CW_USEDEFAULT;
    mc.y = CW_USEDEFAULT;
    mc.cx = 80 * 8;
    mc.cy = 19 * 8+40;
    mc.style = WS_CHILD | WS_CLIPSIBLINGS | WS_DLGFRAME;
    mc.lParam = (LPARAM)0;
    rv = (HWND)SendMessage(hwndClient, WM_MDICREATE, 0, (LPARAM) &mc);
    return rv;
}
