/*++

Copyright (c) 1998  Microsoft Corporation

Module Name:

    dumpoff.cxx

Abstract:

    Structure dumper

Author:

    Bilal Alam      (balam)     Oct-17-1998 Initial Revision

--*/

#include "inetdbgp.h"
#include "oemdbi.h"
#include "cvinfo.h"
#include "imagehlp.h"

#define INVALID_LENGTH              ((DWORD)-1)

#define MAX_MEMBERNAME_LENGTH       256
#define MAX_TYPENAME_LENGTH         256
#define MAX_ARG_SIZE                256

typedef DWORD (*PFN_READ_MEMORY) (
    VOID * address, 
    DWORD cbBytes, 
    VOID *pBuffer 
);

typedef DWORD (*PFN_PRINTF) (
    CHAR * pszBuffer,
    DWORD cbBytes
);

typedef struct _STRUCTURE_MEMBER {
    CHAR                    achMemberName[ MAX_MEMBERNAME_LENGTH + 1 ];
    DWORD                   cbOffset;
    DWORD                   cbMaxSize;
} STRUCTURE_MEMBER, *PSTRUCTURE_MEMBER;

typedef struct _STRUCTURE_TEMPLATE {
    CHAR                    achName[ MAX_TYPENAME_LENGTH + 1 ];
    PSTRUCTURE_MEMBER       pMembers;
    DWORD                   cMembers;
    DWORD                   cUseful;
    DWORD                   cbTotalSize;
    DWORD                   Type;
} STRUCTURE_TEMPLATE, *PSTRUCTURE_TEMPLATE;

DWORD
GetOffset(
    BYTE *              pBuffer,
    DWORD *             pcbOffset
);

DWORD
ReadFieldList(
    TPI *               pTypeInterface,
    STRUCTURE_TEMPLATE* pStructure,
    lfFieldList *       pFieldList, 
    DWORD               cbLen,
    DWORD               dwFlags
);

DWORD
ReadBClass(
    TPI *               pTypeInterface,
    lfBClass *          pBClass, 
    DWORD *             pcbReturnSize, 
    DWORD *             pcbOffset,
    CHAR *              pszBuffer,
    DWORD               cbBuffer
);

DWORD
ReadMember(
    lfMember*           pMember, 
    DWORD *             pcbReturnSize, 
    DWORD *             pcbOffset,
    CHAR *              pszBuffer,
    DWORD               cbBuffer
);

DWORD
ReadNestType(
    lfNestType*         pNestType, 
    DWORD *             pcbReturnSize, 
    DWORD *             pcbOffset,
    CHAR *              pszBuffer,
    DWORD               cbBuffer
);

DWORD
ReadOneMethod(
    lfOneMethod*        pOneMethod, 
    DWORD *             pcbReturnSize, 
    DWORD *             pcbOffset,
    CHAR *              pszBuffer,
    DWORD               cbBuffer
);

DWORD
ReadMethod(
    lfMethod*           pMethod, 
    DWORD *             pcbReturnSize, 
    DWORD *             pcbOffset,
    CHAR *              pszBuffer,
    DWORD               cbBuffer
);

DWORD
ReadVTable(
    lfVFuncTab*         pVTable, 
    DWORD *             pcbReturnSize, 
    DWORD *             pcbOffset,
    CHAR *              pszBuffer,
    DWORD               cbBuffer
);

DWORD
ReadStaticMember(
    lfSTMember*         pStaticMember, 
    DWORD *             pcbReturnSize, 
    DWORD *             pcbOffset,
    CHAR *              pszBuffer,
    DWORD               cbBuffer
);

DWORD
InitializeStructureTemplate(
    PSTRUCTURE_TEMPLATE pTemplate
);

DWORD
TerminateStructureTemplate(
    PSTRUCTURE_TEMPLATE pTemplate
);

VOID
DumpoffUsage(
    VOID
);

DWORD
OutputTemplate(
    STRUCTURE_TEMPLATE *    pTemplate,
    CHAR *                  pszMemberName,
    DWORD                   dwFlags,
    PVOID                   pvAddress,
    PFN_READ_MEMORY         pfnReadMemory,
    PFN_PRINTF              pfnPrintf 
);

DWORD
BuildMemberList(
    IN PSTRUCTURE_TEMPLATE  pTemplate,
    IN TPI *                pTypeInterface,
    IN TI                   tiType,
    IN BOOL                 fTypeSizeOnly
);

DWORD
BuildMemberListForTypeName(
    IN PSTRUCTURE_TEMPLATE  pTemplate,
    IN PDB *                pPDB,
    IN LPSTR                pszTypeName
);

DWORD
FindMembersOfTypeSize(
    IN PDB *                pPDB,
    IN DWORD                cbSize,
    IN PFN_PRINTF           pfnPrintf
);

DWORD
OutputTemplate(
    STRUCTURE_TEMPLATE *    pTemplate,
    CHAR *                  pszMemberName,
    DWORD                   dwFlags,
    PVOID                   pvAddress,
    PFN_READ_MEMORY         pfnReadMemory,
    PFN_PRINTF              pfnPrintf 
)
/*++

Routine Description:

    Output a structure template.
    
    If pvAddress is NULL, then this function will output a general template
    of the structure, listing each member along with its offset from the 
    start of the structure.
    
    If pvAddress is non-NULL, then this function will output the structure
    with the memory at pvAddress cast as this type.  
    
    If pszMemberName is NULL, then the above two statements apply to all 
    members of the structure.  If pszMember is not NULL, then the statements
    apply only the member whose name is pszMember.
    
Arguments:

    pTemplate - Template to dump
    pszMemberName - Optional member name filter
    dwFlags - Flags describing Output() details.  Currently not supported
    pvAddress - Optional address of memory to cast as type
    pfnReadMemory - Provides read memory functionality
    pfnPrintf - Provides printf functionality

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    DWORD                   cCounter;
    BOOL                    fDidSomething = FALSE;
    PBYTE                   pBuffer = NULL;
    DWORD                   dwError = ERROR_SUCCESS;
    CHAR                    achFormat[ 256 ];
    DWORD                   cbRunningLength;
    BOOL                    fLastBitField = FALSE;
    DWORD                   cBitField = 0;
    DWORD                   dwTotalMask;
    DWORD                   cbSize;
    INT                     i;
    
    pBuffer = (PBYTE) LocalAlloc( LPTR, pTemplate->cbTotalSize );
    if ( pBuffer == NULL )
    {
        dwError = GetLastError();
        goto Finished;
    }

    // 
    // If address is specified, then read the amount required for this 
    // structure.  Otherwise, we are simply dumping the template and thus
    // output the size of the type 
    //
    
    if ( pvAddress )
    {
        dwError = pfnReadMemory( pvAddress, pTemplate->cbTotalSize, pBuffer );
        if ( dwError != ERROR_SUCCESS )
        {
            goto Finished;
        }
    }
    else
    {
        _snprintf( achFormat,
                   sizeof( achFormat ),
                   "sizeof( %s %s ) = 0x%X bytes (%d bytes)\n",
                   pTemplate->Type == LF_CLASS ? "class" : "struct",
                   pTemplate->achName,
                   pTemplate->cbTotalSize,
                   pTemplate->cbTotalSize );
        pfnPrintf( achFormat, -1 );
    }
    
    //
    // Iterate through consequential members of type
    //
   
    for( cCounter = 0;
         cCounter < pTemplate->cUseful;
         cCounter++ )
    {
    
        //
        // Do filtering on member name if specified
        //
    
        if ( pszMemberName )
        {
            if ( fDidSomething )
            {
                break;
            }
            
            if ( strcmp( pTemplate->pMembers[ cCounter ].achMemberName,
                         pszMemberName ) )
            {
                continue;
            }
        }
        
        //
        // Dump member name
        //
        
        cbRunningLength = pfnPrintf( pTemplate->pMembers[ cCounter ].achMemberName,
                                     -1 );
        
        //
        // Formatting junk
        //
        
        for ( i = cbRunningLength;
              i < 25;
              i++ )
        {
            cbRunningLength += pfnPrintf( " ", -1 );
        }
        
        cbRunningLength += pfnPrintf( " = ", -1 );
        
        achFormat[ 0 ] = '\0';
        cbSize = pTemplate->pMembers[ cCounter ].cbMaxSize;
       
        if ( !pvAddress )
        {
            //
            // Just dumping template.  Output the offset from the start of 
            // the type
            //
        
            _snprintf( achFormat,
                       sizeof( achFormat ),
                       "+%8X",
                       pTemplate->pMembers[ cCounter ].cbOffset );
        }
        else if ( !cbSize ||
             ( ( cbSize == sizeof( DWORD ) ) && fLastBitField ) ) 
        {
            //
            // If the maxsize is 0, then this must be a bitfield
            // If the maxsize is 4, and the last item was a bit field, then
            // this must be the last bit of the bit field.
            //
            // TODO:  Need to make this work for bit fields larger than 32
            //
            
            if ( !fLastBitField )
            {
                fLastBitField = TRUE;
            }
            cBitField++;
            
            dwTotalMask = (DWORD) *(DWORD*) ((PBYTE)pBuffer+
                             pTemplate->pMembers[ cCounter ].cbOffset);
            
            _snprintf( achFormat,
                       sizeof( achFormat ),
                       "%s",
                       (dwTotalMask & (1 << ( cBitField - 1 ))) ? "TRUE" : "FALSE" );
            
            if ( cbSize == sizeof( DWORD ) )
            {
                fLastBitField = FALSE;
                cBitField = 0;
            }
        }
        else if ( cbSize != sizeof( DWORD ) )
        {
            //
            // If this structure is not a DWORD in size, then assume we don't 
            // know how to dump it affectly.  In this case, we will simply
            // dump out the address at which the member data starts
            //
            
            _snprintf( achFormat,
                       sizeof( achFormat ),
                       "%016p..",
                       (PBYTE) pvAddress +
                         pTemplate->pMembers[ cCounter ].cbOffset );
        }
        else
        {
            //
            // This is a DWORD sized member.  We can dump out the value
            // effectively -> so we do it
            //
            
            _snprintf( achFormat,
                       sizeof( achFormat ),
                       "%08X",
                       (DWORD) *(DWORD*) ((PBYTE)pBuffer+ 
                         pTemplate->pMembers[ cCounter ].cbOffset ) );
        }
        
        cbRunningLength += pfnPrintf( achFormat, -1 );
        
        //
        // Two column display.  Given 80 columns, seems like the only 
        // reasonable setting (maybe 1 column is useful?)
        //
        
        if ( pszMemberName ||
             ( cCounter % 2 ) )
        {
            pfnPrintf( "\n", -1 );
        }
        else
        {
            for ( i = cbRunningLength;
                  i < 40;
                  i++ )
            {
                pfnPrintf( " ", -1 );
            }
        }
        
        //
        // Keep tabs on whether we actually dumped something 
        //
        
        fDidSomething = TRUE;
    }
    
    if ( !fDidSomething )
    {
        dwError = ERROR_FILE_NOT_FOUND;
    }
    
    pfnPrintf( "\n", -1 );

Finished:
    if ( pBuffer != NULL )
    {
        LocalFree( pBuffer );
    }
    return dwError;
}

DWORD
BuildMemberListForTypeName(
    IN PSTRUCTURE_TEMPLATE  pTemplate,
    IN PDB *                pPDB,
    IN LPSTR                pszTypeName
)
/*++

Routine Description:

    Build the structure template for a given type
    
Arguments:

    pTemplate - Template to populate (must have been previously inited)
    pPDB - PDB structure opened using MSDBI!PDBOpen
    pszTypeName - Name of type

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    TPI *                   pTypeInterface = NULL;
    TI                      RootTI;
    DWORD                   dwError = ERROR_SUCCESS;
    PB                      pb;
    
    if ( !pTemplate || !pPDB || !pszTypeName )
    {
        dwError = ERROR_INVALID_PARAMETER;
        goto Finished;
    }

    //
    // Get the type interface
    //
    
    if ( !PDBOpenTpi( pPDB,
                      pdbRead,
                      &pTypeInterface ) )
    {
        dwError = GetLastError();
        goto Finished;
    }

    //
    // Does this PDB have the necessary type information?
    //

    if ( TypesQueryTiMinEx( pTypeInterface ) ==
         TypesQueryTiMacEx( pTypeInterface ) )
    {
        dwError = ERROR_NOT_SUPPORTED;
        goto Finished;
    }
    
    //
    // Lookup with specified type
    //
    
    if ( !TypesQueryTiForUDTEx( pTypeInterface,
                                pszTypeName,
                                TRUE,
                                &RootTI) )
    {
        dwError = ERROR_FILE_NOT_FOUND;
        goto Finished;
    }
    
    strncpy( pTemplate->achName,
             pszTypeName,
             sizeof( pTemplate->achName ) - 1 );
    
    dwError = BuildMemberList( pTemplate,
                               pTypeInterface,
                               RootTI,
                               FALSE );

Finished:

    if ( pTypeInterface != NULL )
    {
        TypesClose( pTypeInterface );
    }
    return dwError;
}

DWORD
FindMembersOfTypeSize(
    IN PDB *                pPDB,
    IN DWORD                cbSize,
    IN PFN_PRINTF           pfnPrintf
)
/*++

Routine Description:

    Find members of a certain size.  Output these types
    
Arguments:

    pPDB - PDB structure opened using MSDBI!PDBOpen
    cbSize - Size in question
    pfnPrintf - Output routine

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    STRUCTURE_TEMPLATE      Template;
    TPI *                   pTypeInterface = NULL;
    DWORD                   dwError = ERROR_SUCCESS;
    TI                      tiMin;
    TI                      tiMax;
    TI                      tiCursor;
    CHAR                    achLast[ MAX_TYPENAME_LENGTH ];
    CHAR                    achBuffer[ 256 ];

    if ( !pPDB || !pfnPrintf || !cbSize )
    {
        dwError = ERROR_INVALID_PARAMETER;
        goto Finished;
    }

    //
    // Get the type interface
    //
    
    if ( !PDBOpenTpi( pPDB,
                      pdbRead,
                      &pTypeInterface ) )
    {
        dwError = GetLastError();
        goto Finished;
    }

    //
    // Get min/max type indices
    //

    tiMin = TypesQueryTiMinEx( pTypeInterface );
    tiMax = TypesQueryTiMacEx( pTypeInterface );

    if ( tiMin == tiMax )
    {
        //
        // Probably no type info available in PDB
        //

        dwError = ERROR_NOT_SUPPORTED;
        goto Finished;
    }

    //
    // Cursor thru
    //

    achLast[ 0 ] = '\0';

    for ( tiCursor = tiMin;
          tiCursor < tiMax;
          tiCursor++ )
    {
        dwError = BuildMemberList( &Template,
                                   pTypeInterface,
                                   tiCursor,
                                   TRUE );
        if ( dwError != ERROR_SUCCESS )
        {
            if ( dwError == ERROR_NOT_SUPPORTED )
            {
                //
                // Not a struct/class. Ignore
                //

                dwError = ERROR_SUCCESS;
                continue;
            }
            else
            {
                break;
            }
        }

        if ( Template.cbTotalSize == cbSize &&
             strcmp( Template.achName, achLast ) )
        {
            pfnPrintf( Template.Type == LF_CLASS ? "class " : "struct ", -1 );
            pfnPrintf( Template.achName, -1 );
            pfnPrintf( "\n", -1 );

            strncpy( achLast,
                     Template.achName,
                     sizeof( achLast ) );
        }
        
    }
    
Finished:

    if ( pTypeInterface != NULL )
    {
        TypesClose( pTypeInterface );
    }
    return dwError;
}

DWORD
BuildMemberList(
    IN PSTRUCTURE_TEMPLATE  pTemplate,
    IN TPI *                pTypeInterface,
    IN TI                   tiType,
    IN BOOL                 fTypeSizeOnly
)
/*++

Routine Description:

    Build a template describing the given type.  This template contains
    an array of members representing the member of the type.
    
Arguments:

    pTemplate - Template to populate (must have been previously inited)
    pTypeInterface - Type interface
    tiType - Type ID to retrieve
    fStructSizeOnly - TRUE if we only need type size (and not the members)

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    TYPTYPE *               pType = NULL;
    lfStructure *           pStructure;
    lfFieldList *           pFieldList;
    PB                      pb;
    DWORD                   dwError = ERROR_SUCCESS;
    DWORD                   cbTotalSize = 0;
    DWORD                   cbStructSize = 0;
    DWORD                   cUseful;
    DWORD                   cbNameOffset;
    TI                      RootTI;

    if ( !pTypeInterface || !pTemplate )
    {
        dwError = ERROR_INVALID_PARAMETER;
        goto Finished;
    }

    RootTI = tiType;
    
    //
    // Parse root record of the type, verifying that it is of type 
    // STRUCTURE or CLASS
    //
    
    if ( !TypesQueryPbCVRecordForTiEx( pTypeInterface,
                                       RootTI,
                                       &pb ) )
    {
        dwError = ERROR_FILE_NOT_FOUND;
        goto Finished;
    }
    
    pType = (TYPTYPE*) pb;

    if ( ( pType->leaf != LF_CLASS ) && 
         ( pType->leaf != LF_STRUCTURE ) )
    {
        dwError = ERROR_NOT_SUPPORTED;
        goto Finished;
    }
    pTemplate->Type = pType->leaf;
    
    pStructure = (lfStructure*) &(pType->leaf);

    cbNameOffset = GetOffset( pStructure->data, &cbStructSize );

    //
    // If we only need the overall structure size, then we can exit out now
    //

    if ( fTypeSizeOnly )
    {
        pTemplate->cbTotalSize = cbStructSize;

        memset( pTemplate->achName,
                0,
                sizeof( pTemplate->achName ) );

        strncpy( pTemplate->achName,
                 (LPSTR) pStructure->data + cbNameOffset + 1,
                 min( (DWORD) *(CHAR*)(pStructure->data + cbNameOffset),
                      sizeof( pTemplate->achName ) ) );

        goto Finished;
    }
    
    //
    // In allocating # of members for the structure, get upper bound by 
    // taking structure member count.
    //
    // OPTIMIZATION:  Dynamically grow the list to avoid gross overestimation.
    //
    
    if ( pTemplate->cMembers < pStructure->count )
    {
        pTemplate->pMembers = (PSTRUCTURE_MEMBER) LocalAlloc( LPTR,
                                          sizeof( STRUCTURE_MEMBER ) *
                                            pStructure->count );
        if ( pTemplate->pMembers == NULL )
        {
            dwError = ERROR_NOT_ENOUGH_MEMORY;
            goto Finished;
        }

        pTemplate->cMembers = pStructure->count;
    }
    
    if ( !TypesQueryPbCVRecordForTi( pTypeInterface,
                                     pStructure->field,
                                     &pb ) )
    {
        dwError = ERROR_FILE_NOT_FOUND;
        goto Finished;
    }
    
    pType = (TYPTYPE*)pb;
    pFieldList = (lfFieldList*) &(pType->leaf);

    //
    // Read the list of the fields in the type
    //
    
    dwError = ReadFieldList( pTypeInterface,
                             pTemplate,
                             pFieldList,
                             pType->len,
                             0 );

    cUseful = pTemplate->cUseful;

    if ( cUseful && ( dwError == ERROR_SUCCESS ) )
    {
        pTemplate->pMembers[ cUseful - 1 ].cbMaxSize =
            cbStructSize - pTemplate->pMembers[ cUseful - 1 ].cbOffset;

        pTemplate->cbTotalSize = cbStructSize;
    }                                

Finished:

    return dwError;    
}

DWORD
ReadFieldList(
    IN TPI *                        pTypeInterface,
    IN STRUCTURE_TEMPLATE *         pTemplate,
    IN lfFieldList *                pFieldList,
    IN DWORD                        cbLen,
    IN DWORD                        dwFlags
)
/*++

Routine Description:

    Read the elements of the field list which represents the class/struct
    
Arguments:

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    DWORD               cbBytes = 0;
    PBYTE               pBuffer;
    DWORD               cbReturnSize = 0;
    BOOL                fExit = FALSE;
    CHAR                achMemberBuffer[ 256 ];
    DWORD               cbMemberBuffer;
    DWORD               dwError = ERROR_SUCCESS;
    DWORD               cFields = 0;
    DWORD               cbLastOffset = 0;

    while ( cbBytes < cbLen )
    {
        //
        // Account for padding the field list blob
        //
        
        for ( ; ; )
        {
            pBuffer = (PBYTE) pFieldList->data + cbBytes;
            if ( *(BYTE*)pBuffer < LF_PAD0 )
            {
                break;
            }
            cbBytes++;
        }
        
        //
        // After each padding block (if any), the first SHORT will contain
        // the field type of the next field in the struct/class.  Handle
        // each type accordingly.  If the handle function (Read*) returns 
        // a cbReturnSize of -1 then this type will not contribute to the
        // offsets in the struct/class.  For example, member functions.  
        //
        
        achMemberBuffer[ 0 ] = '\0';
        cbReturnSize = 0;
        cbMemberBuffer = sizeof( achMemberBuffer );
        
        switch ( *(USHORT*) pBuffer ) 
        {
        case LF_BCLASS:
            dwError = ReadBClass( pTypeInterface,
                                  (lfBClass*) pBuffer,
                                  &cbReturnSize,
                                  &cbBytes,
                                  achMemberBuffer,
                                  cbMemberBuffer );
            break;
    
        case LF_MEMBER:
            dwError = ReadMember( (lfMember*) pBuffer,
                                  &cbReturnSize,
                                  &cbBytes,
                                  achMemberBuffer,
                                  cbMemberBuffer );
            break;
            
        case LF_NESTTYPE:
            dwError = ReadNestType( (lfNestType*) pBuffer,
                                    &cbReturnSize,
                                    &cbBytes,
                                    achMemberBuffer,
                                    cbMemberBuffer );
            break;     
        
        case LF_ONEMETHOD:
            dwError = ReadOneMethod( (lfOneMethod*) pBuffer,
                                     &cbReturnSize,
                                     &cbBytes,
                                     achMemberBuffer,
                                     cbMemberBuffer );
            break;

        case LF_METHOD:
            dwError = ReadMethod( (lfMethod*) pBuffer,
                                  &cbReturnSize,
                                  &cbBytes,
                                  achMemberBuffer,
                                  cbMemberBuffer );
            break;
       
        case LF_STMEMBER:
            dwError = ReadStaticMember( (lfSTMember*) pBuffer,
                                        &cbReturnSize,
                                        &cbBytes,
                                        achMemberBuffer,
                                        cbMemberBuffer );
            break;

        case LF_VFUNCTAB:
            dwError = ReadVTable( (lfVFuncTab*) pBuffer,
                                  &cbReturnSize,
                                  &cbBytes,
                                  achMemberBuffer,
                                  cbMemberBuffer );
            break;
            
        default:
            fExit = TRUE;
            break;
        }
        
        if ( fExit )
        {
            break;
        }
        
        if ( dwError != ERROR_SUCCESS ||
             cbReturnSize == INVALID_LENGTH )
        {
            continue;
        }
        
        //
        // We got a useful member of the struct/class.  Add it to the 
        // template.
        //
        
        pTemplate->cUseful++;

        strncpy( pTemplate->pMembers[ cFields ].achMemberName,
                 achMemberBuffer,
                 sizeof( pTemplate->pMembers[ cFields ].achMemberName ) - 1 );

        pTemplate->pMembers[ cFields ].cbOffset = cbReturnSize;
        
        //
        // Calculate the maximum size of the previous member by taking the 
        // difference between the start offset of the member and the start 
        // offset of the previous member.  Note that this is not necessarily
        // the exact size because of potential alignment padding.  It is only
        // an upper bound on the size of the previous member.
        //
        
        if ( cFields )
        {
            pTemplate->pMembers[ cFields - 1 ].cbMaxSize =
                                            cbReturnSize - cbLastOffset;
        }
        
        cbLastOffset = cbReturnSize;
        
        cFields++;
    }
    
    return dwError;
}

DWORD
ReadBClass(
    IN TPI *                    pTypeInterface,
    IN lfBClass*                pBClass,
    OUT DWORD *                 pcbReturnSize,
    OUT DWORD *                 pcbOffset,
    OUT CHAR *                  pszBuffer,
    IN DWORD                    cbBuffer
)
/*++

Routine Description:

    Read the type info of base class field
    
Arguments:

    pTypeInterface - MSDBI type interface
    pBClass - Points to a base class descriptor
    pcbReturnSize - Filled with offset from start of original type
    pcbOffset - Filled with new offset to result field traversal in 
                field list handler
    pszBuffer - Filled with name of base class
    cbBuffer - Size of pszBuffer

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    PB                      pb;
    DWORD                   Offset;
    TYPTYPE*                pType;
    lfStructure *           pStructure;
    DWORD                   cbUnused;
    
    Offset = GetOffset( pBClass->offset, pcbReturnSize );
    *pcbOffset += sizeof( lfBClass ) + Offset;
    
    //
    // We have to lookup the name of the base class explicitly by using the 
    // index type in the base class descriptor
    //

    if ( !TypesQueryPbCVRecordForTiEx( pTypeInterface,
                                       pBClass->index,
                                       &pb ) )
    {
        return ERROR_FILE_NOT_FOUND;
    }
    
    //
    // Process/munge/extract
    // 
    
    pType = (TYPTYPE*)pb;
    pStructure = (lfStructure*) &(pType->leaf );

    Offset = GetOffset( pStructure->data, &cbUnused );
    
    memset( pszBuffer, 0, cbBuffer );
    memcpy( pszBuffer,
            (CHAR*) pStructure->data + Offset + 1,
            min( (DWORD) *(CHAR*) ( pStructure->data + Offset ), cbBuffer ) );
            
    return ERROR_SUCCESS;
}

DWORD
ReadMember(
    IN lfMember *               pMember,
    OUT DWORD *                 pcbReturnSize,
    OUT DWORD *                 pcbOffset,
    IN CHAR *                   pszBuffer,
    IN DWORD                    cbBuffer
)
/*++

Routine Description:

    Read the type info of a member
    
Arguments:

    pMember - Points to a member descriptor
    pcbReturnSize - Filled with offset from start of original type
    pcbOffset - Filled with new offset to result field traversal in 
                field list handler
    pszBuffer - Filled with name of base class
    cbBuffer - Size of pszBuffer

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    DWORD Offset = GetOffset( pMember->offset, pcbReturnSize );

    memset( pszBuffer, 0, cbBuffer );
    memcpy( pszBuffer,
            (CHAR*) pMember->offset + Offset + 1,
            min( (DWORD) *(CHAR*) ( pMember->offset + Offset ), cbBuffer ) );
            
    *pcbOffset += sizeof( lfMember ) + Offset +  pMember->offset[Offset] + 1;

    return ERROR_SUCCESS;
}

DWORD
ReadOneMethod(
    IN lfOneMethod *            pOneMethod,
    OUT DWORD *                 pcbReturnSize,
    OUT DWORD *                 pcbOffset,
    IN CHAR *                   pszBuffer,
    IN DWORD                    cbBuffer
)
/*++

Routine Description:

    Read the type info of a non-overloaded member function.  
    We process this only to up the offset within the field list for 
    traversal purposes.  Member methods themselves have no affect on 
    the offsets/size of the data structure 
    
Arguments:

    pOneMethod - Method type descriptor
    pcbReturnSize - Filled with offset from start of original type
    pcbOffset - Filled with new offset to result field traversal in 
                field list handler
    pszBuffer - Filled with name of base class
    cbBuffer - Size of pszBuffer

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    CHAR *              pszSource = NULL;
    
    pszSource = (CHAR*) pOneMethod + sizeof( lfOneMethod );
    *pcbOffset += sizeof( lfOneMethod );
    if ( ( pOneMethod->attr.mprop == CV_MTintro ) ||
         ( pOneMethod->attr.mprop == CV_MTpureintro ) )
    {
        *pcbOffset += sizeof( LONG );
        pszSource += sizeof( LONG );
    }
    *pcbOffset += *(CHAR*)pszSource + 1;
    *pcbReturnSize = INVALID_LENGTH;

    return ERROR_SUCCESS;
}

DWORD
ReadMethod(
    IN lfMethod *               pMethod,
    OUT DWORD *                 pcbReturnSize,
    OUT DWORD *                 pcbOffset,
    IN CHAR *                   pszBuffer,
    IN DWORD                    cbBuffer
)
/*++

Routine Description:

    Read the type info of a member function.  We process this only to 
    up the offset within the field list for traversal purposes.  Member
    methods themselves have no affect on the offsets/size of the data 
    structure 
    
Arguments:

    pMethod - Method type descriptor
    pcbReturnSize - Filled with offset from start of original type
    pcbOffset - Filled with new offset to result field traversal in 
                field list handler
    pszBuffer - Filled with name of base class
    cbBuffer - Size of pszBuffer

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    *pcbOffset += sizeof( lfMethod ) + pMethod->Name[ 0 ];
    *pcbReturnSize = INVALID_LENGTH;
    return ERROR_SUCCESS;
}

DWORD
ReadVTable(
    IN lfVFuncTab *             pVTable,
    OUT DWORD *                 pcbReturnSize,
    OUT DWORD *                 pcbOffset,
    IN CHAR *                   pszBuffer,
    IN DWORD                    cbBuffer
)
/*++

Routine Description:

    Read the vtable of the structure.
    
Arguments:

    pVTable - Vtable type descriptor
    pcbReturnSize - Filled with offset from start of original type
    pcbOffset - Filled with new offset to result field traversal in 
                field list handler
    pszBuffer - Filled with name of base class
    cbBuffer - Size of pszBuffer

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    *pcbOffset += sizeof( lfVFuncTab );
    
    strncpy( pszBuffer,
             "'vftable'",
             cbBuffer - 1 );

    //
    // Assume at the beginning of the data structure.  
    // 

    *pcbReturnSize = 0;
    
    return ERROR_SUCCESS;
}


DWORD
ReadStaticMember(
    IN lfSTMember *             pStaticMember,
    OUT DWORD *                 pcbReturnSize,
    OUT DWORD *                 pcbOffset,
    IN CHAR *                   pszBuffer,
    IN DWORD                    cbBuffer
)
{
    *pcbOffset += sizeof( lfSTMember ) + pStaticMember->Name[ 0 ];
    *pcbReturnSize = INVALID_LENGTH;
    return ERROR_SUCCESS;
}


DWORD
ReadNestType(
    IN lfNestType *             pNestType,
    OUT DWORD *                 pcbReturnSize,
    OUT DWORD *                 pcbOffset,
    IN CHAR *                   pszBuffer,
    IN DWORD                    cbBuffer
)
{
    *pcbOffset += sizeof( lfNestType ) + pNestType->Name[ 0 ];
    *pcbReturnSize = INVALID_LENGTH;
    return ERROR_SUCCESS;
}

DWORD
GetOffset(
    BYTE *              pBuffer,
    DWORD *             pcbOffset
)
/*++

Routine Description:

    Read the offset for the type record.  Then advance the cursor.
    
Arguments:

    pBuffer - Points to current position in field list buffer
    pcbOffset - Filled with offset of field member
    
Return Value:

    Amount to advance cursor to next field member

--*/
{
    USHORT leaf = *(USHORT*)pBuffer;
    
    if ( leaf < LF_NUMERIC )
    {
        *pcbOffset = leaf;
        return sizeof( leaf );
    }
    else
    {
        switch( leaf )
        {
        case LF_CHAR:
            *pcbOffset = *((char*)pBuffer);
            return sizeof(leaf) + sizeof(char);
        case LF_SHORT:
            *pcbOffset = *(short*)pBuffer;
            return sizeof(leaf) + sizeof(short);
        case LF_USHORT:
            *pcbOffset = *(USHORT*)pBuffer;
            return sizeof(leaf) + sizeof(USHORT);
        case LF_LONG:
            *pcbOffset = *(long*)pBuffer;
            return sizeof(leaf) + sizeof(long);
        case LF_ULONG:
            *pcbOffset = *(ULONG*)pBuffer;
            return sizeof(leaf) + sizeof(ULONG);
        }
    }
    return 0;
}

DWORD
InitializeStructureTemplate(
    IN PSTRUCTURE_TEMPLATE      pTemplate
)
/*++

Routine Description:

    Initialize structure template
    
Arguments:

    pTemplate - Template buffer to be initialized
    
Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    if ( pTemplate == NULL )
    {
        return ERROR_INVALID_PARAMETER;
    }

    pTemplate->pMembers       = NULL;
    pTemplate->cMembers       = 0;
    pTemplate->achName[ 0 ]   = '\0';
    pTemplate->cUseful        = 0;
    pTemplate->cbTotalSize    = 0;
    pTemplate->Type           = 0xFFFFFFFF;

    return ERROR_SUCCESS;
}

DWORD
TerminateStructureTemplate(
    IN PSTRUCTURE_TEMPLATE      pTemplate
)
/*++

Routine Description:

    Terminate structure template
    
Arguments:

    pTemplate - Template buffer to be terminated
    
Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    if ( pTemplate == NULL )
    {
        return ERROR_INVALID_PARAMETER;
    }

    if ( pTemplate->pMembers )
    {
        LocalFree( pTemplate->pMembers );
        pTemplate->pMembers = NULL;
        pTemplate->cMembers = 0;
    }

    return ERROR_SUCCESS;
}

DWORD
OutputStructure(
    IN PDB *                    pDebug,
    IN CHAR *                   pszStructureType,
    IN CHAR *                   pszMemberName,
    IN DWORD                    dwFlags,
    IN VOID *                   pvAddress,
    IN PFN_READ_MEMORY          pfnReadMemory,
    IN PFN_PRINTF               pfnPrintf
)
/*++

Routine Description:

    Top level call to output a structure
    
Arguments:

    pDebug - PDB handle
    pszStructureType - Name of structure/class to dump
    pszMemberName - (optional) Name of particular member of structure to dump
    dwFlags - (not supported) 
    pvAddress - (optional) Address of start of structure
    pfnReadMemory - (optional) Function to read memory.  Not needed if 
                    pvAddress==NULL
    pfnPrintf - Output function

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    STRUCTURE_TEMPLATE          Template;
    DWORD                       dwError;
    
    if ( !pDebug || 
         !pszStructureType || 
         !pfnReadMemory || 
         !pfnPrintf )
    {
        return ERROR_INVALID_PARAMETER;    
    }

    dwError = InitializeStructureTemplate( &Template );
    if ( dwError != ERROR_SUCCESS )
    {
        return dwError;
    }

    dwError = BuildMemberListForTypeName( &Template,
                                          pDebug,
                                          pszStructureType );
    if ( dwError != ERROR_SUCCESS )
    {
        return dwError;
    }

    dwError = OutputTemplate( &Template,
                              pszMemberName,
                              dwFlags,
                              pvAddress,
                              pfnReadMemory,
                              pfnPrintf );

    //
    // CODEWORK:  Cache the templates
    //

    TerminateStructureTemplate( &Template );

    return dwError;
}

DWORD
DoPrintf(
   CHAR *                   pszBuffer,
   DWORD                    cbBytes
)
/*++

Routine Description:

    Print out buffer
    
Arguments:

    pszBuffer - buffer to print
    cbBytes - Bytes to print

Return Value:

    Number of bytes printed

--*/
{
    dprintf( "%s", pszBuffer );
    return strlen( pszBuffer );
} 

DWORD
DoReadMemory(
    VOID *                  pvAddress,
    DWORD                   cbBytes,
    VOID *                  pBuffer 
)
/*++

Routine Description:

    Read debuggee memory into buffer
    
Arguments:

    pvAddress - Address to read
    cbBytes - # of bytes to read
    pBuffer - Buffer to be filled

Return Value:

    If successful ERROR_SUCCESS, else Win32 Error Code

--*/
{
    if ( ReadMemory( pvAddress, pBuffer, cbBytes, NULL ) )
    {
        return ERROR_SUCCESS;
    }
    else
    {
        return GetLastError();
    }
}

VOID
DumpoffUsage(
    VOID
)
/*++

Routine Description:

    !dumpoff usage message
    
Arguments:

    None

Return Value:

    None

--*/
{
    dprintf(
        "Usage:  !dumpoff <pdb_file>!<type_name>[.<member_name>] [expression]\n"
        "        !dumpoff -s [<pdb_search_path>]\n"
        "\n"
        "pdb_file          Un-qualified name of PDB file (eg. KERNEL32, NTDLL)\n"
        "type_name         Name of type (struct/class) to dump, OR \n"
        "                  ==<cbHexSize> to dump struct/classes of size cbHexSize\n"
        "member_name       (optional) Name of member in type_name to dump\n"
        "expression        (optional) Address of memory to dump as type_name\n"
        "                  if not present, offset(s) are dumped\n"
        "pdb_search_path   Set the search path for PDBs\n"
        "\n"
        "Examples:  !dumpoff ntdll!_RTL_CRITICAL_SECTION 14d4d0\n"
        "           !dumpoff w3svc!HTTP_REQUEST._dwSignature w3svc!g_GlobalObj\n"
        "           !dumpoff ntdll!_RTL_CRITICAL_SECTION\n"
        "           !dumpoff w3svc!==840\n"
        "           !dumpoff -s \\\\mydrive\\pdbs;c:\\local\\pdbs\n"
    );
}

#if defined( _X86_ )
CHAR g_achPDBSearchPath[ 1024 ] = "\\\\x86fre\\symbols.pri\\retail\\dll";
#else
CHAR g_achPDBSearchPath[ 1024 ] = "\\\\alphafre\\symbols.pri\\retail\\dll";
#endif

DECLARE_API( dumpoff )

/*++

Routine Description:

    This function is called as an NTSD extension to dump a structure
    based on debug info in PDB

Arguments:

    hCurrentProcess - Supplies a handle to the current process (at the
        time the extension was called).

    hCurrentThread - Supplies a handle to the current thread (at the
        time the extension was called).

    CurrentPc - Supplies the current pc at the time the extension is
        called.

    lpExtensionApis - Supplies the address of the functions callable
        by this extension.

    lpArgumentString - Supplies the asciiz string that describes the
        ansi string to be dumped.

Return Value:

    None.

--*/

{
    CHAR *              pszPdb = NULL;
    CHAR *              pszType = NULL;
    CHAR *              pszAddress = NULL;
    CHAR *              pszMember = NULL;
    CHAR *              pszCursor = NULL;
    CHAR *              pszNext = NULL;
    CHAR                achArg1[ MAX_ARG_SIZE ];
    CHAR                achArg2[ MAX_ARG_SIZE ];
    CHAR                achBuffer[ MAX_ARG_SIZE ] = "";
    CHAR                achFileName[ MAX_PATH + 1 ];
    CHAR                achFullPath[ MAX_PATH + 1 ];
    CHAR                achSymPath[ MAX_PATH + 1 ];
    BOOL                fRet;
    EC                  ec;
    PDB *               pDebug = NULL;
    DWORD               dwError;
    CHAR *              pszError = NULL;
    DWORD               cArguments;
    DWORD               cbSize;
    BOOL                fRetry = TRUE;

    INIT_API();

    //
    // get the debugger symbol path
    //

    fRet = SymGetSearchPath( hCurrentProcess,
                             achSymPath,
                             sizeof( achSymPath ) );
    if (!fRet )
    {
        //
        // If we couldn't get the default sym path, just use the SYSTEMROOT
        //

        dwError = GetEnvironmentVariable( "SYSTEMROOT",
                                          achSymPath,
                                          sizeof( achSymPath ) );

        if ( dwError == 0 )
        {
            _snprintf( achBuffer,
                       sizeof( achBuffer ),
                       "Unable to determine symbol path.  Error = %d\n",
                       GetLastError() );
            goto Finished;
        }
    }

    //
    // parse out the argument style
    // <pdbfile>!<type>[.member] [address]
    //

    cArguments = sscanf( (CHAR*) lpArgumentString, 
                         "%256s%256s", 
                         achArg1, 
                         achArg2 );
    
    if ( cArguments == EOF || cArguments == 0 )
    {
        DumpoffUsage();
        goto Finished;
    }

    //
    // Handle the !dumpoff -s [sympath] case
    //

    if ( ( achArg1[ 0 ] == '-' || achArg1[ 0 ] == '/' ) &&
         ( achArg1[ 1 ] == 's' || achArg1[ 1 ] == 'S' ) )
    {
        if ( cArguments == 2 )
        {
            strncpy( g_achPDBSearchPath,
                     achArg2,
                     sizeof( g_achPDBSearchPath ) );
        }

        dprintf( "PDB search path set to\n%s%s%s\n",
                 g_achPDBSearchPath,
                 *g_achPDBSearchPath ? "\n" : "",
                 achSymPath );
                 
        goto Finished;
    }

    //
    // Parse the regular !dumpoff command
    //
    
    pszPdb = achArg1;

    pszCursor = strchr( achArg1, '!' );
    if ( pszCursor == NULL )
    {
        DumpoffUsage();
        goto Finished;
    }
    *pszCursor = '\0';

    pszType = pszCursor + 1;
    
    pszCursor = strchr( pszType, '.' );
    if ( pszCursor != NULL )
    {
        *pszCursor = '\0';
        pszMember = pszCursor + 1;
    } 
    
    if ( cArguments > 1 )
    {
        pszAddress = achArg2;
    }

    //
    // done parsing, now get the PDB 
    //
    
    strncpy( achFileName,
             pszPdb,
             MAX_ARG_SIZE );
    strcat( achFileName,
            ".pdb");

    //
    // Look for the PDB file.  First in the PDB search path, then sympath
    //

    pszCursor = g_achPDBSearchPath;

Retry:
    while ( pszCursor )
    {
        pszNext = strchr( pszCursor, ';' );
        if ( pszNext != NULL )
        {
            *pszNext = '\0';
        }
        
        fRet = SearchTreeForFile( pszCursor,
                                  achFileName,
                                  achFullPath );
        if ( fRet )
        {
            break;
        }
        
        if ( pszNext )
        {
            pszCursor = pszNext + 1;
        }
        else
        {
            pszCursor = NULL;
        }
    }

    if ( !pszCursor && fRetry )
    {
        fRetry = FALSE;
        
        // now try the debugger sympath

        pszCursor = achSymPath;
        goto Retry;
    }
    
    if ( !pszCursor )
    {
        _snprintf( achBuffer,
                   sizeof( achBuffer ),
                   "Couldn't find PDB file %s\n",
                   achFileName );
        goto Finished;
    }
    
    //
    // Open the PDB file
    //
    
    if ( !PDBOpen( achFullPath,
                   pdbRead,
                   0,
                   &ec,
                   achBuffer,
                   &pDebug ) )
    {
        _snprintf( achBuffer,
                   sizeof( achBuffer ),
                   "Error opening PDB file.  Error = %d\n",
                   ec );
        goto Finished;
    }

    if ( pszType[ 0 ] == '=' && pszType[ 1 ] == '=' )
    {
        //
        // Find all types of size after ==
        //

        cbSize = strtoul( pszType + 2,
                          NULL,
                          16 );

        dwError = FindMembersOfTypeSize( pDebug,
                                         cbSize,
                                         DoPrintf );
    }
    else
    {
        dwError = OutputStructure( pDebug,
                                   pszType,
                                   pszMember,
                                   0,
                                   pszAddress ? (VOID*) GetExpression( pszAddress ) : NULL, 
                                   DoReadMemory,
                                   DoPrintf );
    }

    if ( dwError != ERROR_SUCCESS )
    {
        switch ( dwError )
        {
        case ERROR_FILE_NOT_FOUND:
            _snprintf( achBuffer,
                       sizeof( achBuffer ),
                       "Could not find type '%s' in PDB file '%s'\n",
                       pszType,
                       achFullPath );
            break;
        case ERROR_NOT_SUPPORTED:
            _snprintf( achBuffer,
                       sizeof( achBuffer ),
                       "PDB file '%s' does not contain necessary type info\n",
                       achFullPath );
            break;
        default:
            _snprintf( achBuffer,
                       sizeof( achBuffer ),
                       "Error dumping structure.  Error = %d\n", 
                       dwError );
        }

        goto Finished;
    }

Finished:

    if ( achBuffer[ 0 ] )
    {
        dprintf( "%s", achBuffer );
    }

    if ( pDebug )
    {
        PDBClose( pDebug );
    }
  
} // DECLARE_API( dumpoff )