/* 
CCIDE
Copyright 2001-2006 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, hwndError;
extern PROCESS DebugProcess;
extern enum DebugState uState;
extern THREAD *StoppedThread;


HWND hwndDump;

static char szDumpClassName[] = "xccMemoryClass";
static char szDumpBlankClassName[] = "xccMemoryClass2";
static HWND hwndCtrl, hwndBlank, hwndEdit;
static HBRUSH hbrBackground;
static WNDPROC oldproc;
static int cursrow, curscol, curpos;
static char cursmod[4];
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";
int GetDumpAddress(char *buf)
{
    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, TRUE);
        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);
                addr = *(int*)data;
            }
            else
                addr = var->address;
            FreeVarInfo(var);
        }
        else
        {
            addr = 0;
        }
        SetFocus(hwndEdit);
    }
    //   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;
    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++)
        {
            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), 1, 0))
            {
                sprintf(buf, "%02X ", charbuf[j]);
                if (i == cursrow && j == curscol)
                    if (modifying)
                        strcpy(buf, cursmod);
            }
            else
            {
                if (i == cursrow && j == curscol)
                {
                    modifying = FALSE;
                    curpos = 0;
                    strcpy(cursmod, "   ");
                }
                strcpy(buf, "?? ");
                charbuf[j] = '.';
            }
            if (i == cursrow && j == curscol && focussed && modifying)
                TextOut(dc, (10+j * 3) *8-4, i *16+rect.top, buf, 2);
            else
                TextOut(dc, (10+j * 3) *8, i *16+rect.top, buf, 2);
            if (charbuf[j] < 32 || charbuf[j] > 126)
                charbuf[j] = '.';
            if (i == cursrow && j == curscol && focussed)
            {
                SetTextColor(dc, oldtxt);
                SetBkColor(dc, oldbk);
            }
            if (i == cursrow && j == curscol && focussed && modifying)
                TextOut(dc, (12+j * 3) *8-4, i *16+rect.top, " ", 1);
            else
                TextOut(dc, (12+j * 3) *8, i *16+rect.top, " ", 1);
        }
        TextOut(dc, (10+j * 3) *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, 0);
            break;
        case WM_KILLFOCUS:
            focussed = FALSE;
            modifying = FALSE;
            strcpy(cursmod, "   ");
            InvalidateRect(hwnd, 0, 0);
            break;
        case WM_LBUTTONDOWN:
        case WM_RBUTTONDOWN:
            GetClientRect(hwnd, &r);
            lines = r.bottom / 16;
            chars = (r.right - 10 * 8) / 8 / 4;
            if (LOWORD(lParam) >= 10 *8 && LOWORD(lParam) < (10+chars) *8 * 3)
            {
                curscol = (LOWORD(lParam) - 10 * 8) / 3 / 8;
                cursrow = HIWORD(lParam) / 16;
                if (focussed)
                    InvalidateRect(hwnd, 0, 0);
            }
            SetFocus(hwnd);
            break;
        case WM_KEYDOWN:
            GetClientRect(hwnd, &r);
            lines = r.bottom / 16;
            chars = (r.right - 10 * 8) / 8 / 4;
            switch (wParam)
            {
            case VK_BACK:
                if (modifying)
                {
                    cursmod[curpos] = ' ';
                    if (curpos)
                        curpos--;
                    InvalidateRect(hwnd, 0, 0);
                }
                break;
            case VK_LEFT:
                if (modifying)
                {
                    cursmod[curpos] = ' ';
                    if (curpos)
                        curpos--;
                    InvalidateRect(hwnd, 0, 0);
                    break;
                }
                if (curscol != 0)
                {
                    curscol--;
                    InvalidateRect(hwnd, 0, 0);
                    break;
                }
                curscol = chars - 1;
                // FALL THROUGH
            case VK_UP:
                modifying = FALSE;
                strcpy(cursmod, "   ");
                curpos = 0;
                if (cursrow == 0)
                    SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
                else
                {
                    cursrow--;
                    InvalidateRect(hwnd, 0, 0);
                }
                break;
            case VK_RIGHT:
                if (modifying)
                {
                    if (curpos == 0)
                        curpos++;
                    break;
                }
                if (curscol + 1 < chars)
                {
                    curscol++;
                    InvalidateRect(hwnd, 0, 0);
                    break;
                }
                curscol = 0;
                // FALL THROUGH
            case VK_DOWN:
                modifying = FALSE;
                strcpy(cursmod, "   ");
                curpos = 0;
                if (cursrow + 1 >= lines)
                    SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
                else
                {
                    cursrow++;
                    InvalidateRect(hwnd, 0, 0);
                }
                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, 1, 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;
                    strcpy(cursmod, "   ");
                    InvalidateRect(hwnd, 0, 0);
                }
                break;
            case VK_ESCAPE:
                modifying = FALSE;
                curpos = 0;
                strcpy(cursmod, "   ");
                InvalidateRect(hwnd, 0, 0);
                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 == 0)
                    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, 0);
            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_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, 0);
                break;
            case IDC_RETURN:
                SendMessage(hwndEdit, WM_GETTEXT, 256, (LPARAM)buf);
                DumpAddress = GetDumpAddress(buf);
                oldDumpAddress = DumpAddress;
                InvalidateRect(hwndBlank, 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:
            InvalidateRect(hwndBlank, 0, 0);
            break;
        case WM_CREATE:
            hwndDump = hwnd;
            modifying = 0;
            cursrow = curscol = curpos = 0;
            strcpy(cursmod, "   ");
            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);
            hwndEdit = CreateWindow("EDIT", "", WS_CHILD + WS_CLIPSIBLINGS +
                WS_BORDER + WS_VISIBLE | ES_AUTOHSCROLL, r.left + 70, r.top, 200, 20, hwndCtrl,
                0, hInstance, 0);
            SendMessage(hwndEdit, WM_SETFONT, (LPARAM)DumpFont, 1);
            oldproc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (int)
                EditHook);
            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);
            //         SetFocus(hwndEdit) ;            
            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(hwndEdit, 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;
}
