#include <windows.h>
#include <commctrl.h>
#include <stdio.h>

#define TF_BIT 0x100

#include "header.h"

extern HWND hwndFrame,hwndASM, hwndRegister, hwndWatch, hwndToolBar, hwndStack ;
extern HANDLE children[MAX_CHILDREN] ;
extern int numberofdrawwindows ;
extern PROJLIST *selectedProject ;
enum DebugState uState = notDebugging;
PROCESS DebugProcess ;
HANDLE StartupSem, BreakpointSem ;
CONTEXT StoppedRegs ;

static HANDLE debugThread ;
static DWORD debugThreadID ;
static int Semaphores ;
static int SingleStepping ;
static int LastSkipAddr ;
static int stopWinMain ;
void StartDebug(char *cmd) ;

static void breakfileName(char *output, char *name)
{
   char *p ;
   strcpy(output,name) ;
   p = strrchr(output,'.') ;
   if (p[-1] != '.') {
      strcpy(p,".dbi") ;
   } else
      strcat(output,".dbi") ;
}
void SaveBreakpoints(char *name, BREAKPOINT *p)
{
   char buf[256] ;
   FILETIME time ;
   FILE *bpfil ;
   if (!p)
      return ;
   breakfileName(buf,name) ;
   if (!FileTime(&time,name))
      return ;
   bpfil = fopen(buf,"w") ;
   if (!bpfil)
      return ;
   fprintf(bpfil,"%X %X\n",time.dwLowDateTime, time.dwHighDateTime) ;
   while (p) {
      fprintf(bpfil,"%s %d %X\n",p->module, p->linenum, p->address) ;
      p = p->next ;
   }
   fclose(bpfil) ;

}
BREAKPOINT *RestoreBreakpoints(char *name)
{
   char buf[256] ;
   FILETIME time ;
   FILETIME timeread ;
   BREAKPOINT *p=0, **q = &p ;
   FILE *bpfil ;
   breakfileName(buf,name) ;
   if (!FileTime(&time,name))
      return 0 ;
   bpfil = fopen(buf,"r") ;
   if (!bpfil)
      return 0 ;
   fgets(buf,256,bpfil) ;
   sscanf(buf,"%X %X",&timeread.dwLowDateTime, & timeread.dwHighDateTime) ;
   if (timeread.dwLowDateTime != time.dwLowDateTime || 
         timeread.dwHighDateTime != time.dwHighDateTime) {
      fclose(bpfil) ;
      return 0;
   }
   while (!feof(bpfil)) {
      char *s,*t ;
      buf[0] = 0 ;
      fgets(buf,256,bpfil) ;
      if (!buf[0]) {
         fclose(bpfil) ;
         return p ;
      }
      *q = calloc(sizeof(BREAKPOINT),1) ;
      if (!*q) {
         fclose(bpfil) ;
         return p ;
      }
      s = buf ;
      t = (*q)->module ;
      while (*s && *s != ' ')
         *t++ = *s++ ;
      *t = 0 ;
      while (*s && *s == ' ')
         s++ ;
      sscanf(s,"%d %x",&(*q)->linenum, &(*q)->address) ;
      q = &(*q)->next ;
   }
   return p ;    
}
int initiateDebug(PROJLIST *plist)
{
   static char cmd[512] ;
	if (uState == notDebugging) {
      int val = IsPEFile(plist->name) ;
      stopWinMain = plist->buildFlags & BF_BREAKWINMAIN ? 1 : 0 ;
      if (val <= 0) {
         if (val == 0)
            ExtendedMessageBox("File not found", MB_TASKMODAL | MB_SETFOREGROUND,"File %s was not found", cmd);
         else 
            ExtendedMessageBox("Not PE file", MB_TASKMODAL | MB_SETFOREGROUND,"File %s must be a win32 executable to debug.", cmd);
         return uState;
      }
      Semaphores = TRUE ;
		StartupSem = CreateSemaphore(0,0,1,0) ;
		BreakpointSem = CreateSemaphore(0,0,1,0) ;
      sprintf(cmd,"%s %s",plist->name, plist->cmdline) ;
		debugThread = CreateThread(0,0,
			(LPTHREAD_START_ROUTINE)StartDebug,(LPVOID)cmd,
			0,&debugThreadID) ;

	
		WaitForSingleObject(StartupSem, INFINITE) ;
		PostMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0) ;
	}
	return uState ;
}
HANDLE GetThreadHandle(DWORD procid, DWORD threadid)
{
	THREAD *thread = DebugProcess.threads ;
	while (thread) {
		if (thread->idThread == threadid)
			return thread->hThread ;
		thread = thread -> next ;
	}
	return DebugProcess.threads->hThread ;
}
BOOL CALLBACK topenumfunc(HWND wnd, LPARAM value)
{
   DWORD id ;
   GetWindowThreadProcessId(wnd, &id) ;
   if (id == value) {
      SetForegroundWindow(wnd) ;
      if (IsIconic(wnd))
         ShowWindow(wnd,SW_RESTORE) ;
      return TRUE ;
   }
   return TRUE ;
}
/* if we don't do this, the debugger locks up sometimes */
void ProcessToTop(DWORD processid)
{
   HWND top ;
   return ;
   top = GetTopWindow(0) ;
   EnumWindows((WNDENUMPROC)topenumfunc, processid) ;
   while (GetTopWindow(0) != top) 
      Sleep(1);
}
static int HandleBreakpoint(DEBUG_EVENT *info) 
{
	if (uState != Aborting)
		uState = atBreakpoint ;
   PostMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0 );
   PostMessage(hwndFrame,WM_BREAKPOINT,0,(LPARAM)info) ;
   if (hwndRegister)
      PostMessage(hwndRegister,WM_COMMAND,ID_SETADDRESS,
                     (LPARAM)GetThreadHandle(info->dwProcessId, info->dwThreadId)) ;
   PostMessage(hwndWatch,WM_COMMAND,ID_SETADDRESS,0 );
   if (hwndStack)
      PostMessage(hwndStack, WM_RESTACK,(WPARAM)1,0) ;
	if (uState != Aborting && uState != notDebugging) {
		WaitForSingleObject(BreakpointSem, INFINITE) ;
	}
   PostMessage(hwndRegister,WM_COMMAND,ID_SETCONTEXT,0) ;
   if (hwndStack)
      PostMessage(hwndStack, WM_RESTACK, (WPARAM)0,0) ;
   ApplyBreakAddress(0,0) ; // get rid of the break line
	if (uState == notDebugging || uState == Aborting) {
      PostMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0 );
      return DBG_TERMINATE_PROCESS ;
	} else {
      if (uState != SteppingOut && uState != SteppingOver && uState != SteppingIn && uState != StepInOut)
			uState = Running ;
      PostMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0 );
		return DBG_CONTINUE ;
	} 
}
static int HandleException(DEBUG_EVENT *info) 
{
	uState = atException ;
   if (info->u.Exception.dwFirstChance && (selectedProject->buildFlags & BF_DEBUGEXCEPTION))
      return DBG_EXCEPTION_NOT_HANDLED ;
	PostMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0 );
	PostMessage(hwndFrame,WM_EXCEPTION,0,(LPARAM)info) ;
   PostMessage(hwndWatch,WM_COMMAND,ID_SETADDRESS,0);
   if (hwndRegister)
      PostMessage(hwndRegister,WM_COMMAND,ID_SETADDRESS,
                     (LPARAM)GetThreadHandle(info->dwProcessId, info->dwThreadId)) ;
	WaitForSingleObject(BreakpointSem, INFINITE) ;
   PostMessage(hwndRegister,WM_COMMAND,ID_SETCONTEXT,0) ;
	if (uState == notDebugging || uState == Aborting) {
      PostMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0 );
      return DBG_TERMINATE_PROCESS ;
   }
	return DBG_EXCEPTION_NOT_HANDLED ;
}
void SingleStep( DWORD procID, DWORD threadID)
{
   StoppedRegs.EFlags |= TF_BIT ;
   SingleStepping = TRUE ;
}
void ClearTraceFlag(DWORD procID, DWORD threadID)
{
	SingleStepping = FALSE ;
}
void BlastExitProcFunc(HANDLE hProcess, DWORD procID, DWORD threadID, DWORD address)
{
   int buf = 0 ; // exit code
   StoppedRegs.Eip = address ;
   StoppedRegs.Esp -= 4 ;
   WriteProcessMemory(hProcess,(LPVOID)StoppedRegs.Esp,(LPVOID)&buf,4,0) ;
   StoppedRegs.Esp -= 4 ;
}
void GetRegs(DWORD procID, DWORD threadID)
{
	HANDLE handle = GetThreadHandle(procID, threadID) ;
   StoppedRegs.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
   GetThreadContext(handle, &StoppedRegs) ;
}
void SetRegs(DWORD procID, DWORD threadID)
{
	HANDLE handle = GetThreadHandle(procID, threadID) ;
   StoppedRegs.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
   SetThreadContext(handle, &StoppedRegs) ;
}
void SetTempBreakPoint(int procid, int threadid, int address)
{
	DebugProcess.breakpoints.address = address ;
	DebugProcess.idTempBpThread = threadid ;
}
void ClearTempBreakPoint(int procid)
{
	DebugProcess.breakpoints.address = 0 ;
}
int SittingOnBreakPoint(DEBUG_EVENT *dbe )
{
	return isBreakPoint((int)dbe->u.Exception.ExceptionRecord.ExceptionAddress) ;
}

void WriteBreakPoint(HANDLE hProcess, int address, int value)
{
	char bf = value ;
	MEMORY_BASIC_INFORMATION mbi ;
	DWORD dwOldProtect ;

//	if (!IsNT() && address >= 0x80000000)
//		return ;
	VirtualQueryEx(hProcess,(LPVOID)address, &mbi, sizeof(mbi)) ;
	VirtualProtectEx(hProcess,mbi.BaseAddress,mbi.RegionSize,
						PAGE_EXECUTE_READWRITE, &mbi.Protect) ;
	
	WriteProcessMemory(hProcess,(LPVOID)address,(LPVOID)&bf,1,0) ;

	VirtualProtectEx(hProcess,mbi.BaseAddress,mbi.RegionSize,
						mbi.Protect, &dwOldProtect) ;
   FlushInstructionCache(hProcess,(LPVOID)address,1) ;
}
void allocBreakPoint(HANDLE hProcess, BREAKPOINT *pt)
{
   unsigned char bf ;
	if (!pt->active && pt->address) {
      DWORD len ;
      ReadProcessMemory(hProcess,(LPVOID)pt->address,(LPVOID) &bf,1, &len ) ;
      if (!len)
         return ;
		if (bf == 0xcc)
			return ;
		pt->active = TRUE ;
		pt->tempVal = bf ;
      WriteBreakPoint(hProcess,pt->address,0xcc) ;
	}
}
void freeBreakPoint(HANDLE hProcess, BREAKPOINT *pt)
{
	if (pt->active) {
		WriteBreakPoint(hProcess,pt->address,pt->tempVal) ;
		pt->active = FALSE ;
	}
}
void SetBreakPoints(int procid)
{
	BREAKPOINT *p = &DebugProcess.breakpoints ;
	while (p) {
		if (p->address)
			allocBreakPoint(DebugProcess.hProcess , p) ;
		p = p->next ;
	}
	
}
void ClearBreakPoints(int procid)
{
	BREAKPOINT *p = &DebugProcess.breakpoints ;
	THREAD *th = DebugProcess.threads ;
	while (p) {
		freeBreakPoint(DebugProcess.hProcess , p) ;
		p = p->next ;
	}
	while (th) {
      freeBreakPoint(DebugProcess.hProcess, &th->breakpoint) ;
		th = th->next ;
	}
	
}
int isBreakPoint(int addr)
{
	BREAKPOINT *p = DebugProcess.breakpoints.next ;
	THREAD *th = DebugProcess.threads ;
	while (p) {
		if (addr == p->address)
			return TRUE ;
		p = p->next ;
	}
	return FALSE ;
}
int isLocalBreakPoint(int addr)
{
	THREAD *th = DebugProcess.threads ;
   while (th) {
      if (addr == th->breakpoint.address)
			return TRUE ;
      th = th->next ;
	}
   return isBreakPoint(addr) ;
}
int IsBreakpointLine(char *module, int line)
{
	BREAKPOINT *p = DebugProcess.breakpoints.next ;
	while (p) {
      if (p->linenum)
         if (p->linenum == line && !xstricmp(module,p->module))
            return TRUE ;
		p = p->next ;
	}
	return FALSE ;
}
int StepOverIncrement(DEBUG_EVENT *dbe)
{
	unsigned char buf[16] ;
	int word = 0 ;
	int address ;
   int skiplen = 0 ;
	ReadProcessMemory(DebugProcess.hProcess,
                                                (LPVOID)(address = dbe->u.Exception.ExceptionRecord.ExceptionAddress),
						(LPVOID) buf,16,0 ) ;
	switch(buf[0]) {
		case 0xCE:
			skiplen = 1 ;
			break ;
		case 0xCD : 
			skiplen = 2 ;
			break ;
		case 0xE8:
			skiplen = 5 ;
			break ;
		default:
			word = (*(short *)buf)  & 0x38ff;
			if (word == 0x10ff || word == 0x18ff) {
				skiplen = 2 ;
				if ((word = (buf[1] & 0xc7)) < 0xc0) { // not indirect through reg
					if (word == 6) // Offset
						skiplen += 4 ;
					else {
						if (word >= 8)
							if (word >= 0x80)
								skiplen += 4 ;
							else
								skiplen += 1 ;
						word &= 7 ;
						if (word == 4) {
							skiplen++ ; /* has a SIB */
							if (*(buf+1) < 0xc0) {
								word = *(buf+2) ;
								if ((word & 7) == 5) {/* EBP special cases */
									if (*(buf+1) & 0x40)
										skiplen += 4 ;
									else
										skiplen += 1 ;
								}
							}
						}
					}
				}
			}
			break ;
	}
   if (skiplen)
      return address + skiplen ;
   return 0 ;
}
int DoStepOver(DEBUG_EVENT *dbe)
{
   int skipaddr = StepOverIncrement(dbe) ;
   if (skipaddr) {
      SetTempBreakPoint(dbe->dwProcessId, dbe->dwThreadId, skipaddr) ;
	} else {
		SingleStep(dbe->dwProcessId, dbe->dwThreadId) ;
	}
}
int DoStepIn(DEBUG_EVENT *dbe)
{
   LastSkipAddr = StepOverIncrement(dbe) ;
   SingleStep(dbe->dwProcessId, dbe->dwThreadId) ;
}
int IsStepping(DEBUG_EVENT *dbe)
{
   char module[256] ;
   int line ;
   if (uState == SteppingOver) {
      int v ;
      if ((v = GetBreakpointLine(dbe->u.Exception.ExceptionRecord.ExceptionAddress,module,&line)) != dbe->u.Exception.ExceptionRecord.ExceptionAddress) {
         DoStepOver(dbe) ;
         return TRUE ;
      } else {
         uState = Running ;
         return FALSE ;
      }
   } else if (uState == SteppingIn) {
      int addr = GetBreakpointLine(dbe->u.Exception.ExceptionRecord.ExceptionAddress,module,&line) ;
      if (addr == dbe->u.Exception.ExceptionRecord.ExceptionAddress) {
         uState = Running ;
         return FALSE ;
      } else if (LastSkipAddr)  {
         if (dbe->u.Exception.ExceptionRecord.ExceptionAddress != LastSkipAddr) {
            if (addr) {
               uState = SteppingOver ;
               DoStepOver(dbe) ;
               return TRUE ;
            } else {
               SetTempBreakPoint(dbe->dwProcessId, dbe->dwThreadId, LastSkipAddr) ;
               uState = StepInOut ;
               return TRUE ;
            }
         }
         else {
               DoStepIn(dbe) ;
               return TRUE ;
         }
      } else {
         DoStepIn(dbe) ;
         return TRUE ;
      }   
   } else if (uState == StepInOut) {
      int addr = GetBreakpointLine(dbe->u.Exception.ExceptionRecord.ExceptionAddress,module,&line) ;
      if (addr == dbe->u.Exception.ExceptionRecord.ExceptionAddress) {
         uState = Running ;
         return FALSE ;
      } else {
         uState = SteppingIn ;
         DoStepIn(dbe) ;
         return TRUE ;
      }
   } else if (uState == FinishStepOut) {
      int addr = GetBreakpointLine(dbe->u.Exception.ExceptionRecord.ExceptionAddress,module,&line) ;
      if (!addr || addr == dbe->u.Exception.ExceptionRecord.ExceptionAddress) {
         uState = Running ;
         return FALSE ;
      } else {
         uState = SteppingIn ;
         DoStepIn(dbe) ;
         return TRUE ;
      }
   }
   return FALSE ;
}
void StepOver(DEBUG_EVENT *dbe)
{
   if (GetFocus() != hwndASM)
      uState = SteppingOver ;
   DoStepOver(dbe) ;
   ReleaseSemaphore(BreakpointSem,1,0) ;
}
void StepOut(DEBUG_EVENT *dbe)
{
	uState = SteppingOut ;
   ReleaseSemaphore(BreakpointSem,1,0) ;
}
void StepIn(DEBUG_EVENT *dbe)
{
   if (GetFocus() != hwndASM)
      uState = SteppingIn ;
   DoStepIn(dbe) ;
   ReleaseSemaphore(BreakpointSem,1,0) ;
}
int RunTo(DEBUG_EVENT *dbe)
{
	int addr ;
   HWND wnd = GetFocus() ;
   if (wnd == hwndASM) {
      if (!(addr = SendMessage(hwndASM,WM_GETCURSORADDRESS,0,0)))
         return FALSE ;
      SetTempBreakPoint(dbe->dwProcessId,dbe->dwThreadId,addr) ;
      ReleaseSemaphore(BreakpointSem,1,0) ;
   } else {
      wnd = GetParent(wnd) ;
      if (!IsSpecialWindow(wnd)) {
         DWINFO *ptr = (DWINFO *)GetWindowLong(wnd,0) ;
         int linenum ;
         SendMessage(ptr->dwHandle,EM_GETSEL,(WPARAM) &linenum,0) ;
         linenum = SendMessage(ptr->dwHandle,EM_LINEFROMCHAR,linenum,0)+1 ;
         addr = GetBreakpointAddress(ptr->dwName,&linenum,FALSE ) ;
         if (addr) {
            SetTempBreakPoint(dbe->dwProcessId,dbe->dwThreadId,addr) ;
            ReleaseSemaphore(BreakpointSem,1,0) ;
         } else
            return FALSE ;
      } else
         return FALSE ;
   }
	return TRUE ;
}
void SetBP(DEBUG_EVENT *dbe)
{
	BREAKPOINT **p = &DebugProcess.breakpoints.next ;
   if (hwndASM && GetFocus() == hwndASM) {
		int addr = SendMessage(hwndASM,WM_GETCURSORADDRESS,0,0) ;
		while (*p) {
			if ((*p)->address == addr)  {
				BREAKPOINT *q = * p ;

				*p = (*p)->next ;
            if (uState == Running)
               freeBreakPoint(DebugProcess.hProcess, q) ;
				free(q) ;
				InvalidateRect(hwndASM,0,0) ;
				return ;
			}
			p = &(*p)->next ;
		}
		*p = malloc(sizeof(BREAKPOINT)) ;
		if (*p) {
			memset(*p,0,sizeof(BREAKPOINT)) ;
			(*p)->address = addr ;
         GetBreakpointLine(addr,&(*p)->module,&(*p)->linenum,FALSE) ;
         InvalidateRect(hwndASM,0,0) ;
         if (uState == Running)
            allocBreakPoint(DebugProcess.hProcess, *p) ;
		}
   } else {
      HWND hWnd = GetFocus() ;
      int i ;
      for (i=0; i < numberofdrawwindows; i++) {
         DWINFO *ptr = (DWINFO *)GetWindowLong(children[i],0) ;
         if (ptr->dwHandle == hWnd) {
            int addr,linenum ;
            SendMessage(ptr->dwHandle,EM_GETSEL,(WPARAM) &linenum,0) ;
            linenum = SendMessage(ptr->dwHandle,EM_LINEFROMCHAR,linenum,0)+1 ;
            addr = GetBreakpointAddress(ptr->dwName,&linenum,FALSE) ;
            if (addr) {
               while (*p) {
                  if ((*p)->address == addr)  {
                     BREAKPOINT *q = * p ;
                     *p = (*p)->next ;
                     if (uState == Running)
                        freeBreakPoint(DebugProcess.hProcess, q) ;
                     free(q) ;
                     if (hwndASM)
                        InvalidateRect(hwndASM,0,0) ;
                     InvalidateRect(children[i],0,0) ;
                     return ;
                  }
                  p = &(*p)->next ;
               }
               *p = malloc(sizeof(BREAKPOINT)) ;
               if (*p) {
                  memset(*p,0,sizeof(BREAKPOINT)) ;
                  (*p)->address = addr ;
                  strcpy((*p)->module,ptr->dwName) ;
                  (*p)->linenum = linenum ;
                  if (hwndASM)
                     InvalidateRect(hwndASM,0,0) ;
                  InvalidateRect(children[i],0,0) ;
                  if (uState == Running)
                     allocBreakPoint(DebugProcess.hProcess, *p) ;
               }
            }
         }
      }
   }
}
int isSteppingOut(DEBUG_EVENT *dbe)
{
	if (uState == SteppingOut) {
		unsigned char bf =0;
		HANDLE handle = GetThreadHandle(dbe->dwProcessId, dbe->dwThreadId) ;
		CONTEXT context ;
		context.ContextFlags = CONTEXT_CONTROL ;
		GetThreadContext(handle, &context) ;
		ReadProcessMemory(DebugProcess.hProcess,(LPVOID)context.Eip,(LPVOID)&bf,1,0) ;
		if (bf == 0xc2 || bf == 0xc3) {
			SingleStep(dbe->dwProcessId,dbe->dwThreadId) ;
         uState = FinishStepOut ;
		} else
         DoStepOver(dbe) ;
		return TRUE ;
	}
	return FALSE ;
}
void StopRunning(int newState)
{
	THREAD *th = DebugProcess.threads ;
   if (uState == Running || uState == SteppingOut || uState == StepInOut) {
      HANDLE hThisThread = GetCurrentThread ( ) ;
      int iOldPriority = GetThreadPriority ( hThisThread ) ;
      ProcessToTop(GetCurrentProcessId()) ;
      SetThreadPriority ( hThisThread , REALTIME_PRIORITY_CLASS + THREAD_PRIORITY_BELOW_NORMAL ) ;
		while (th) {
         SuspendThread(th->hThread) ;
			th = th->next ;
		}
		th = DebugProcess.threads ;
		while (th) {
			CONTEXT context ;
			context.ContextFlags = CONTEXT_CONTROL ;
         GetThreadContext(th->hThread, &context) ;
//         if (th->breakpoint.active)
//            freeBreakPoint(DebugProcess.hProcess, &th->breakpoint) ;
         th->breakpoint.address = context.Eip ;
         allocBreakPoint(DebugProcess.hProcess,&th->breakpoint) ;
			th = th -> next ;
		}
		th = DebugProcess.threads ;
		if (newState != nullState)
			uState = newState ;
		while (th) {
         ResumeThread(th->hThread) ;
         PostThreadMessage(th->idThread,WM_NULL,0,0) ;
			th = th->next ;
		}
      /* At this point it still may not stop if there is a deadlock
       * condition, requires further work
       */
    SetThreadPriority ( hThisThread , iOldPriority ) ;
    ProcessToTop(DebugProcess.idProcess) ;
	}
}
void abortDebug(void)
{  
	if (uState != notDebugging) {
		if  (uState == atBreakpoint || uState == atException) {
			uState = Aborting ;
			ReleaseSemaphore(BreakpointSem,1,0) ;
		}
      else {
         StopRunning(Aborting) ;
         uState = Aborting ;
			ReleaseSemaphore(BreakpointSem,1,0) ;
      }
	}
}
void StartDebug ( char *cmd )
{
    STARTUPINFO         stStartInfo     ;
    PROCESS_INFORMATION stProcessInfo   ;
    DEBUG_EVENT stDE                      ;
    BOOL        bSeenInitialBP   = FALSE  ;
    BOOL        bContinue        = TRUE   ;
    DWORD       dwContinueStatus          ;
		BOOL				bRet ;
		BOOL 				ContinueStep = FALSE ;
		BREAKPOINT	**bp ;
		THREAD			**th ;
      int            val,i  ;
      BOOL     terminating = FALSE ;
      int base = 0 ;

    memset ( &stStartInfo   , 0 , sizeof ( STARTUPINFO         ) ) ;
    memset ( &stProcessInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;

    stStartInfo.cb = sizeof ( STARTUPINFO ) ;

    bRet = CreateProcess ( NULL                      ,
                                cmd                       ,
                                NULL                      ,
                                NULL                      ,
                                FALSE                     ,
                                CREATE_NEW_CONSOLE |
                                  DEBUG_ONLY_THIS_PROCESS ,
                                NULL                      ,
                                NULL                      ,
                                &stStartInfo              ,
                                &stProcessInfo             ) ;

    if ( !bRet )
    {
				ReleaseSemaphore(StartupSem,1,0) ;
            ExtendedMessageBox("Debugger",MB_TASKMODAL,"Could not execute %s.",cmd) ;
        return ;
    }
    // Things work *so* much better with console processes that hook ctrl-c
    // when you set the priority of the debugger down...
    SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_LOWEST) ;
    Sleep(500) ; /* Needed to make things happy */
    uState = Running ;
    InvalidateRect(hwndRegister,0,TRUE) ;
    CloseHandle(stProcessInfo.hThread ); 
    CloseHandle(stProcessInfo.hProcess ); 

    ReleaseSemaphore(StartupSem,1,0) ;
    // Loop until told to stop.
    while ( TRUE == bContinue )
    {
        // Pause until a debug event notification happens.
        bContinue = WaitForDebugEvent ( &stDE , INFINITE ) ;
        switch ( stDE.dwDebugEventCode )
        {
            case CREATE_PROCESS_DEBUG_EVENT   :
            {
								THREAD *newThread = malloc(sizeof(THREAD)) ;
                // Save the handle information needed for later.
								DebugProcess.hProcess = stDE.u.CreateProcessInfo.hProcess;
								DebugProcess.idProcess = stDE.dwProcessId ;
								memset(newThread,0,sizeof(newThread ));
								newThread->hThread = stDE.u.CreateProcessInfo.hThread ;
								newThread->idThread = stDE.dwThreadId ;
								DebugProcess.threads = newThread ;
                        dwContinueStatus = DBG_CONTINUE ;
//                        DebugProcess.dbg_info = GetDebugInfo(stDE.u.CreateProcessInfo.hFile,
//                              stDE.u.CreateProcessInfo.dwDebugInfoFileOffset,
//                              stDE.u.CreateProcessInfo.nDebugInfoSize,
//                              stDE.u.CreateProcessInfo.lpBaseOfImage) ;
                        DebugProcess.dbg_info = GetDebugInfo(stDE.u.CreateProcessInfo.lpBaseOfImage,cmd,FALSE) ;
                        base = stDE.u.CreateProcessInfo.lpBaseOfImage ;
                        /* next line has to be deferred to the init bp */
//                        DebugProcess.ExitAddr = FindExitProcessAddress(DebugProcess.hProcess, stDE.u.CreateProcessInfo.lpBaseOfImage) ;
                        DebugProcess.breakpoints.next = RestoreBreakpoints(cmd) ;
                        SetBreakPoints(DebugProcess.idProcess) ;
                        for (i=0; i < numberofdrawwindows; i++)
                            InvalidateRect(children[i],0,0) ;
            }
            break ;

            case EXIT_PROCESS_DEBUG_EVENT   :
            {
                        DLL_INFO *dllInfo = DebugProcess.dll_info ;
                        SaveBreakpoints(cmd, DebugProcess.breakpoints.next) ;
                        if (uState != Aborting)
                           ExtendedMessageBox("Debugger",MB_TASKMODAL | MB_SETFOREGROUND,"Process has ended") ;
                        FreeDebugInfo(DebugProcess.dbg_info) ;
                        DebugProcess.dbg_info = 0 ;
								free( DebugProcess.threads ); /* Should only be the one left */
								PostMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0 );

                        // System dlls don't get an unload message
                        while (dllInfo) {
                           DLL_INFO *x = dllInfo->next ;
                           FreeDebugInfo(dllInfo->dbg_info) ;
                           free(dllInfo) ;
                           dllInfo = x ;
                        }
								bp = &DebugProcess.breakpoints.next ;
								while (*bp) {
									BREAKPOINT *q = *bp ;
									*bp = (*bp)->next ;
									free(q) ;
								}
								memset( &DebugProcess, 0, sizeof (PROCESS)) ;
                bContinue = FALSE ;
                dwContinueStatus = DBG_CONTINUE ;
            }
            break ;

            case LOAD_DLL_DEBUG_EVENT    :
            {
               LPVOID pos ;
               int len ;
               DLL_INFO *dllInfo = (DLL_INFO *)calloc(sizeof(DLL_INFO),1) ;
               if (dllInfo) {
                  DLL_INFO **info = &DebugProcess.dll_info ;
                  dllInfo->dbg_info = 0 ;
//                  GetDebugInfo(stDE.u.LoadDll.hFile,
//                           stDE.u.LoadDll.dwDebugInfoFileOffset,
//                           stDE.u.LoadDll.nDebugInfoSize,
//                           stDE.u.LoadDll.lpBaseOfDll) ;
                  dllInfo->base = stDE.u.LoadDll.lpBaseOfDll ;
                  pos = stDE.u.LoadDll.lpImageName ;
                  dllInfo->fUnicode = stDE.u.LoadDll.fUnicode ;
                  dllInfo->hFile = stDE.u.LoadDll.hFile ;
                  if (pos) {
                     ReadProcessMemory(DebugProcess.hProcess,pos,(LPVOID) &pos,4, &len ) ;
                     if (pos) {
                        ReadProcessMemory(DebugProcess.hProcess,(LPVOID)pos,(LPVOID) dllInfo->name,510, &len ) ;
                        dllInfo->name [len] = 0 ;
                        if (dllInfo->fUnicode) {
                           for (i=0 ; i < len; i+= 2)
                              dllInfo->name[i/2] = dllInfo->name[i] ;
                        }
                        dllInfo->dbg_info = GetDebugInfo(dllInfo->base,dllInfo->name,TRUE) ;
                     }
                  }
                    
                  while (*info && (unsigned)(*info)->base < (unsigned)stDE.u.LoadDll.lpBaseOfDll)
                     info = &(*info)->next ;
                  dllInfo->next = *info ;
                  *info = dllInfo ;
                  if (dllInfo->dbg_info && (selectedProject->buildFlags & BF_BREAKDLL)) {
                     int addr = GetMainAddress(dllInfo->dbg_info) ;
                     if (addr) {
                        BREAKPOINT **p = &DebugProcess.breakpoints.next ;
                        while (*p)
                           p = &(*p)->next ;
                        *p = calloc(sizeof(BREAKPOINT),1) ;
                        if (*p) {
                           GetBreakpointLine(addr,&(*p)->module,&(*p)->linenum,FALSE) ;
                           (*p)->address = addr ;
                           dllInfo->breakpoint = addr ;
                           for (i=0; i < numberofdrawwindows; i++)
                              InvalidateRect(children[i],0,0) ;
                        }
                     }
                  }
                  // set breakpoints for recently loaded DLL.
                  SetBreakPoints(DebugProcess.idProcess) ;
               }
               dwContinueStatus = DBG_CONTINUE ;
            }
            break ;
            case UNLOAD_DLL_DEBUG_EVENT  :
            {
                DLL_INFO **info = &DebugProcess.dll_info ;
                while (*info && (*info)->base != (int) stDE.u.UnloadDll.lpBaseOfDll)
                  info = &(*info)->next ;

                if (*info) {
                  DLL_INFO *x = (*info) ;
                  CloseHandle(x->hFile ) ;
                  *info = (*info)->next ;
                  FreeDebugInfo (x->dbg_info) ;
                  free(x) ;
                }

                dwContinueStatus = DBG_CONTINUE ;
            }
            break ;

            case CREATE_THREAD_DEBUG_EVENT  :
            {
								
								th = & DebugProcess.threads ;
								while (*th)
									th = &(*th)->next ;
								*th = malloc(sizeof(THREAD)) ;
								if (*th) {
									memset(*th,0,sizeof(THREAD)) ;
									(*th)->idThread = stDE.dwThreadId ;
									(*th)->hThread = stDE.u.CreateThread.hThread ;
								}
                dwContinueStatus = DBG_CONTINUE ;
            }
            break ;
            case EXIT_THREAD_DEBUG_EVENT    :
            {
								th = &DebugProcess.threads ;
								while ((*th) && (*th)->idThread != stDE.dwThreadId)
									th = &(*th)->next ;
								if (*th) {
									THREAD *p = *th ;
									(*th) = (*th)->next ;
									free(p) ;
								}
                dwContinueStatus = DBG_CONTINUE ;
            }
            break ;

            case OUTPUT_DEBUG_STRING_EVENT  :
            {
							char buf[256] ;
							int len = stDE.u.DebugString.nDebugStringLength ;
							if (len > 255)
								len = 255 ;
							memset(buf,0,256) ;
    					ReadProcessMemory ( DebugProcess.hProcess                  ,
                                stDE.u.DebugString.lpDebugStringData  ,
                                buf , len , 0 ) ;
							ExtendedMessageBox("Debug String",0,"%s",buf) ;
              dwContinueStatus = DBG_CONTINUE ;
            }
            break ;

            case RIP_EVENT  :
            {
                dwContinueStatus = DBG_CONTINUE ;
            }
            break ;

            case EXCEPTION_DEBUG_EVENT      :
            {
                switch ( stDE.u.Exception.ExceptionRecord.ExceptionCode)
                {
										case EXCEPTION_SINGLE_STEP:
											ClearTraceFlag(stDE.dwProcessId, stDE.dwThreadId) ;
											if (ContinueStep) {
												if (!SittingOnBreakPoint(&stDE)) {
                                       ContinueStep = FALSE ;
                                       SetBreakPoints(stDE.dwProcessId) ;
													dwContinueStatus = DBG_CONTINUE ;
													break ;
												}
											}
                    case EXCEPTION_BREAKPOINT :
                                    ClearBreakPoints(stDE.dwProcessId) ;
                                    GetRegs(stDE.dwProcessId, stDE.dwThreadId) ;
                                    if (((int)stDE.u.Exception.ExceptionRecord.ExceptionAddress != DebugProcess.breakpoints.address) ||
                                          stDE.dwThreadId == DebugProcess.idTempBpThread)  {
                                       if (bSeenInitialBP) {
                                          if (stDE.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT
                                              && (isLocalBreakPoint((int)stDE.u.Exception.ExceptionRecord.ExceptionAddress) 
                                              || (int)stDE.u.Exception.ExceptionRecord.ExceptionAddress == DebugProcess.breakpoints.address) ){
                                             StoppedRegs.Eip-- ;
                                          }
                                          ClearTempBreakPoint(stDE.dwProcessId) ;
                                          if (!terminating && uState != SteppingOut && !IsStepping(&stDE)) {
                                             ProcessToTop(GetCurrentProcessId()) ;
                                             dwContinueStatus = HandleBreakpoint(&stDE) ;
                                             ProcessToTop(stDE.dwProcessId) ;
                                          } else
                                             dwContinueStatus = DBG_CONTINUE ;
                                       } else {
                                          DWORD addr ;
                                          bSeenInitialBP = TRUE ;
                                          /* This had to be deferred for win2K */
                                          DebugProcess.ExitAddr = FindExitProcessAddress(DebugProcess.hProcess, base) ;
                                          if (addr = GetMainAddress(DebugProcess.dbg_info)) {
                                                if (stopWinMain)
                                                  SetTempBreakPoint(stDE.dwProcessId, stDE.dwThreadId, addr) ;
                                          } else {
                                             ClearTempBreakPoint(stDE.dwProcessId) ;
                                             if (stopWinMain)
                                                SetTempBreakPoint(stDE.dwProcessId, stDE.dwThreadId, GetEntryPoint()) ;
                                          }
                                          dwContinueStatus = DBG_CONTINUE ;
                                       }
                                       isSteppingOut(&stDE) ;
                                    } else {
                                       dwContinueStatus = DBG_CONTINUE ;
                                    }
        // WIN32 doesn't support the DBG_TERMINATE_PROCESS flag, so we
        // have to go to some effort to get the process terminated.
        // This started at process creation time, when we found the address
        // of the ExitProcess function if there was one.
doterm :
                                    if (dwContinueStatus == DBG_TERMINATE_PROCESS) {
                                       terminating = TRUE ;
                                       dwContinueStatus = DBG_CONTINUE ;
                                       if (DebugProcess.ExitAddr) {
                                          BlastExitProcFunc(DebugProcess.hProcess,stDE.dwProcessId, 
                                             stDE.dwThreadId, DebugProcess.ExitAddr) ;
                                       } else {
                                          TerminateProcess(DebugProcess.hProcess,0) ;
                                          ExtendedMessageBox("Forced Termination",MB_SETFOREGROUND | MB_TASKMODAL,
                                          "A forced termination has been used.\n  This may leave the system in an unstable state.") ;
                                       }
                                    }
                                    if (!SingleStepping && !terminating)
                                       if (SittingOnBreakPoint(&stDE)) {
                                          ContinueStep = TRUE ;
                                          SingleStep(stDE.dwProcessId, stDE.dwThreadId) ;
                                       } else {
                                          SetBreakPoints(stDE.dwProcessId) ;
                                       }
                                    SetRegs(stDE.dwProcessId, stDE.dwThreadId) ;
													
	            	    		break ;

                    default:
                                    ClearBreakPoints(stDE.dwProcessId) ;
                                    ProcessToTop(GetCurrentProcessId()) ;
                                    GetRegs(stDE.dwProcessId, stDE.dwThreadId) ;
												dwContinueStatus = HandleException(&stDE) ;
                                    if (dwContinueStatus == DBG_TERMINATE_PROCESS)
                                       goto doterm ;
                                    SetRegs(stDE.dwProcessId, stDE.dwThreadId) ;
                                    ProcessToTop(stDE.dwProcessId) ;
                    		break ;
                }
            }
            break ;

            // For any other events, just continue on.
            default                         :
            {
                dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED ;
            }
            break ;
        }
        // Pass on to the operating system.
        ContinueDebugEvent ( stDE.dwProcessId ,
                             stDE.dwThreadId  ,
                             dwContinueStatus  ) ;

    }
    CloseHandle(StartupSem) ;
    CloseHandle(BreakpointSem) ;
      if (hwndASM) {
			SendMessage(hwndASM,WM_CLOSE,0,0) ;
         hwndASM = 0 ;
      }
    if (hwndStack)
      PostMessage(hwndStack,WM_CLOSE,0,0) ;
    ProcessToTop(GetCurrentProcessId()) ;
    uState = notDebugging ;
    PostMessage(hwndRegister,WM_COMMAND,ID_SETADDRESS,0) ;
    PostMessage(hwndWatch,WM_ADDWATCH,0,0) ;
    PostMessage(hwndWatch,WM_CLOSE,0,0) ;
    InvalidateRect(hwndRegister,0,TRUE) ;
    i = SendMessage(hwndToolBar, TB_GETSTATE, IDM_STARTSTOP,0) ;
    SendMessage(hwndToolBar, TB_SETSTATE, IDM_STARTSTOP, MAKELONG(~TBSTATE_CHECKED & i,0)) ;
}