//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       oledbg.cxx
//
//  Contents:   OLE debugger extention DLL
//
//  Classes:
//
//  Functions:
//
//  History:    8-05-94   kevinro   Created
//
//----------------------------------------------------------------------------
extern "C" {
#undef DBG
#define DBG 1
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
}

#include <windows.h>
#include <rpc.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <oledbg.h>

#pragma hdrstop

PNTSD_EXTENSION_APIS    pExtApis;
HANDLE                  hDbgThread;
HANDLE                  hDbgProcess;


//+---------------------------------------------------------------------------
//
//  Function:   ReadMemory
//
//  Synopsis:   Reads memory from the debuggee
//
//  Effects:
//
//  Arguments:  [pvAddress] --
//		[cbMemory] --
//		[pvLocalMemory] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    8-05-94   kevinro   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD
ReadMemory( PVOID               pvAddress,
            ULONG               cbMemory,
            PVOID               pvLocalMemory)
{
    ULONG       cbActual = cbMemory;

    if (ReadProcessMemory(hDbgProcess, pvAddress, pvLocalMemory, cbMemory, &cbActual))
    {
        if (cbActual != cbMemory)
        {
            return((DWORD)-1);
        }
        return(0);
    }
    return(GetLastError());

}

//+---------------------------------------------------------------------------
//
//  Function:   WriteMemory
//
//  Synopsis:   Writes memory to the debuggee
//
//  Effects:
//
//  Arguments:  [pvLocalMemory] --
//		[cbMemory] --
//		[pvAddress] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    8-05-94   kevinro   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
DWORD
WriteMemory(PVOID           pvLocalMemory,
            ULONG           cbMemory,
            PVOID           pvAddress)
{
    ULONG       cbActual = cbMemory;

    if (WriteProcessMemory(hDbgProcess, pvAddress, pvLocalMemory, cbMemory, &cbActual))
    {
        if (cbActual != cbMemory)
        {
            return((DWORD)-1);
        }
        return(0);
    }
    return(GetLastError());
}
#define AllocHeap(x)    RtlAllocateHeap(RtlProcessHeap(), 0, x)
#define FreeHeap(x) 	RtlFreeHeap(RtlProcessHeap(), 0, x)


DWORD
GetTlsEntry(ULONG       TlsValue,
            PVOID *     ppvValue)
{
    NTSTATUS                    Status;
    THREAD_BASIC_INFORMATION    ThreadInfo;
    ULONG                       cbReturned;
    PVOID                       *Slot;
    PTEB                        Teb;

    Status = NtQueryInformationThread(  hDbgThread,
                                        ThreadBasicInformation,
                                        &ThreadInfo,
                                        sizeof(ThreadInfo),
                                        &cbReturned);

    if (!NT_SUCCESS(Status))
    {
        return(Status);
    }

    Teb = (PTEB) AllocHeap(sizeof(TEB));

    ReadMemory(ThreadInfo.TebBaseAddress, sizeof(TEB), Teb);

    Slot = &Teb->TlsSlots[TlsValue];
    *ppvValue = *Slot;

    FreeHeap(Teb);

    return(0);

}

void ShowBinaryData(PBYTE   pData,
                    DWORD   cbData)
{
    DWORD                   i;
    char                    line[20];
    PNTSD_EXTENSION_APIS    lpExt = pExtApis;

    line[16] = '\0';
    if (cbData > 65536)
    {
        ntsdPrintf("ShowBinaryData:  Data @%x is said to be %d bytes in length\n");
        ntsdPrintf("                 Rejecting request.  Corrupt data\n");
        return;
    }
    for (; cbData > 0 ; )
    {
        for (i = 0; i < 16 && cbData > 0 ; i++, cbData-- )
        {
            ntsdPrintf(" %02x", (unsigned) *pData);
            if (isprint(*pData))
                line[i] = *pData;
             else
                line[i] = '.';
            pData++;
        }
        if (i < 16)
        {
            for (;i < 16 ; i++ )
            {
                ntsdPrintf("   ");
                line[i] = ' ';
            }
        }
        ntsdPrintf("\t%s\n",line);
        if (lpExt->lpCheckControlCRoutine())
        {
            break;
        }

    }
}

//+---------------------------------------------------------------------------
//
//  Function:   DumpVtbl
//
//  Synopsis:   Dumps a vtbl to the debugger
//
//  Effects:	Given a pointer to a vtbl, output the name of the vtbl, and
//		its contents to the debugger.
//
//  Arguments:  [pvtbl] -- Address of vtbl
//		[pszCommand] -- Symbolic expression for pvtbl
//
//  History:    8-07-94   kevinro   Created
//
//----------------------------------------------------------------------------
extern "C"
void
DumpVtbl(PVOID pvtbl, LPSTR pszCommand)
{
   DWORD dwVtblOffset;
   char achNextSymbol[256];

   if (pvtbl == 0)
   {
       // Can't handle objects at zero
       ntsdPrintf("%s has a vtbl pointer of NULL\n",pszCommand);
       return;
   }

   if ((DWORD)pvtbl == 0xdededede)
   {
       // Can't handle objects at zero
       ntsdPrintf("%s may be deleted memory. pvtbl==0xdededede\n",pszCommand);
       return;
   }

   // This value points at the VTBL. Find a symbol for the VTBL
   ntsdGetSymbol((LPVOID)pvtbl,(UCHAR *)achNextSymbol,(LPDWORD)&dwVtblOffset);

   // If the dwVtblOffset is not zero, then we are pointing into the table.
   // This could mean multiple inheritance. We could be tricky, and try to
   // determine the vtbl by backing up here. Maybe later
   if (dwVtblOffset != 0)
   {
       ntsdPrintf("Closest Previous symbol is %s at 0x%x (offset -0x%x)\n",
		  achNextSymbol,
		  (DWORD)pvtbl - dwVtblOffset,
		  dwVtblOffset);
       return;
   }
   ntsdPrintf("0x%08x -->\t %s\n",pvtbl,achNextSymbol);
   // vtbl entries should always point at functions. Therefore, we should
   // always have a displacement of zero. To check for the end of the table
   // we will reevaluate the vtbl pointer. If the offset isn't what we
   // expected, then we are done.

   DWORD dwIndex;
   for (dwIndex = 0 ; dwIndex < 4096 ; dwIndex += 4)
   {
       DWORD dwVtblEntry;

       ntsdGetSymbol((LPVOID)((DWORD)pvtbl+dwIndex),
		     (UCHAR *)achNextSymbol,
		     (LPDWORD)&dwVtblOffset);

       if (dwVtblOffset != dwIndex)
       {
	   //
	   // May have moved on to another vtable
	   //
#ifdef DBG_OLEDBG
	   ntsdPrintf("?? %s + %x\n",achNextSymbol,dwVtblOffset);
	   ntsdPrintf("Moved to another table?\n");
#endif
	   return;
       }

       if (ReadMemory((LPVOID)((DWORD)pvtbl+dwIndex),
		      sizeof(dwVtblEntry),
		      (PVOID)&dwVtblEntry))
       {
	   //
	   // Must be off the end of a page or something.
	   //
#ifdef DBG_OLEDBG
	   ntsdPrintf("End of page?\n");
#endif
	   return;
       }

       // If the function is at zero, then must be at end of table
       if (dwVtblEntry == 0)
       {
#ifdef DBG_OLEDBG
	   ntsdPrintf("dwVtblEntry is zero. Must be end of table\n");
	   return;
#endif

       }

       // Now, determine the symbol for the entry in the vtbl
       ntsdGetSymbol((LPVOID)dwVtblEntry,
		     (UCHAR *)achNextSymbol,
		     (LPDWORD)&dwVtblOffset);

       // If it doesn't point to the start of a routine, then it
       // probably isn't part of the vtbl
       if (dwVtblOffset != 0)
       {
#ifdef DBG_OLEDBG
	   ntsdPrintf("?? %s + %x\n",achNextSymbol,dwVtblOffset);
	   ntsdPrintf("Doesn't point to function?\n");
#endif
	   return;
       }

       ntsdPrintf("   0x%08x\t %s\n",dwVtblEntry,achNextSymbol);
   }
   ntsdPrintf("Wow, there were at least 1024 entries in the table!\n");

}

extern "C"
void
punk(   HANDLE                  hProcess,
        HANDLE                  hThread,
        DWORD                   dwCurrentPc,
        PNTSD_EXTENSION_APIS    lpExt,
        LPSTR                   pszCommand)
{
   PVOID punk;
   PVOID pvtbl;

   InitDebugHelp(hProcess, hThread, lpExt);

   // Evalute the first pointer. This is a pointer to the object
   punk = (PVOID) ntsdGetExpr(pszCommand);

   if (punk == NULL)
   {
       // Can't handle objects at zero
       ntsdPrintf("%s is not a valid pointer\n",pszCommand);
       return;
   }
   // Now, read the first DWORD of this memory location
   // This is a pointer to the table
   if (ReadMemory(punk,sizeof(pvtbl),(PVOID)&pvtbl))
   {
       ntsdPrintf("Couldn't read memory at %x\n",punk);
       return;
   }

   DumpVtbl(pvtbl,pszCommand);

}
extern "C"
void
vtbl(   HANDLE                  hProcess,
        HANDLE                  hThread,
        DWORD                   dwCurrentPc,
        PNTSD_EXTENSION_APIS    lpExt,
        LPSTR                   pszCommand)
{
   PVOID pvtbl;

   InitDebugHelp(hProcess, hThread, lpExt);

   // Evalute the first pointer. This is a pointer to the table
   pvtbl = (PVOID) ntsdGetExpr(pszCommand);
   DumpVtbl(pvtbl,pszCommand);
}

extern "C"
void
expr(   HANDLE                  hProcess,
        HANDLE                  hThread,
        DWORD                   dwCurrentPc,
        PNTSD_EXTENSION_APIS    lpExt,
        LPSTR                   pszCommand)
{
    InitDebugHelp(hProcess, hThread, lpExt);

    UCHAR symbol[256];
    DWORD expr;
    DWORD disp;

    expr = ntsdGetExpr(pszCommand);

    ntsdGetSymbol((LPVOID)expr,(UCHAR *)symbol,(LPDWORD)&disp);

    ntsdPrintf("expr: %s = %x (%d) %s + %d\n",pszCommand,expr,expr,symbol,disp);

}

extern "C"
void
help(   HANDLE                  hProcess,
        HANDLE                  hThread,
        DWORD                   dwCurrentPc,
        PNTSD_EXTENSION_APIS    lpExt,
        LPSTR                   pszCommand)
{

    InitDebugHelp(hProcess, hThread, lpExt);

    ntsdPrintf(" help		\tThis Message\n");
    ntsdPrintf(" punk <expr>	\tDump IUnknown vtbl\n");
    ntsdPrintf(" vtbl <expr>	\tDump vtbl\n");

}
