/* 
   Copyright 2001-2003 Free Software Foundation, Inc.

   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., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
**********************************************************************

DUMPWND.C is the memory view.  It has the interfaces for getting addressing
and drawing the window.

**********************************************************************
*/
#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 HINSTANCE hInstance ;
extern HWND hwndClient,hwndStatus,hwndFrame, hwndError ;
extern PROCESS DebugProcess ;
extern enum DebugState uState;
extern CONTEXT StoppedRegs ;


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 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"
} ;

static HFONT DumpFont,staticFont ;
static int DumpAddress ;
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=StoppedRegs.Eip,l ;
      DEBUG_INFO * dbg ;
      VARINFO *var ;
      var = EvalExpr(&types,&syms, &dbg,&offset, buf) ;
      if (var) {
         if (var->constant)
            addr= var->ival ;
         else if (var->address < 0x1000) {
            char data[20] ;
            if (!var->explicitreg)
               ExtendedMessageBox("Address error",0,"Address is a register.  Dumping from value.");
            ReadValue(var->address,&data,4) ;
            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 char 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 Error",0,"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 += (HIWORD(wParam) - 32000) * 16 ;
					break ;
				case SB_THUMBTRACK :
					return 0 ;
//					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(GetParent(hwnd),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_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 ;
               InvalidateRect(hwndBlank,0,0) ;
               break ;                               
            case IDC_RETURN:
               SendMessage(hwndEdit,WM_GETTEXT,256,(LPARAM)buf) ;
               DumpAddress = GetDumpAddress(buf) ;
               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) ;
         SendMessage(hwndCtrl,LCF_ADJUSTRECT,0,(LPARAM)&r) ;
         hwndStatic = CreateWindow("STATIC","Address:",
            WS_CHILD + WS_CLIPSIBLINGS + WS_VISIBLE,
            r.left+20,r.top+2,48,20,
            hwndCtrl,0,hInstance, 0) ;
         SendMessage(hwndStatic,WM_SETFONT,(LPARAM)staticFont,1) ;
         hwndEdit = CreateWindow("EDIT","",
            WS_CHILD + WS_CLIPSIBLINGS + WS_BORDER + WS_VISIBLE,
            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 = "Memory Window" ;
	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 ;
}