/****************************** Module Header ******************************\
* Module Name: dsocode.c
*
* Copyright (c) 1985-96, Microsoft Corporation
*
* This file contains the dump structure offset (dso) extension. It is
*  included by $(ALT_PROJECT)\dsotable.c which is generated by structo.exe
*
* History:
* 06/17/96 GerardoB Created
* 10/06/98 Rajeshsu edited to use wdbgexts.h
\***************************************************************************/

#define DsoPrint(x) dprintf("%.*s", nIndent, "                      "); dprintf x

#define OFLAG(l)        (1L << ((DWORD)#@l - (DWORD)'a'))
#define EvalExp GetExpression

#define EXACT_MATCH 0xFFFF

int gnIndent = 0; // caller should set and restore this appropriately.

VOID moveBlock(
    PVOID pdst,
    PVOID src,
    DWORD size)
{
    BOOL fSuccess = TRUE;
    ULONG Result;
    
    try {
        if (!ReadMemory((DWORD_PTR)src, pdst, size, &Result)) {
            fSuccess = FALSE;
        }
    } except (EXCEPTION_EXECUTE_HANDLER) {
        //dprintf(pszMoveException, pszExtName);
        fSuccess = FALSE;
    }

    if (!fSuccess) {
        dprintf("moveBlock(%x, %x, %x) failed.\n", pdst, src, size);
    }
}


/***************************************************************************\
* dsoTerminateString
*
* This is used to "parse" the command line. It null-terminates a space
*  delimited string, returns its size and a pointer to the begining
*  of next string
*
* 06/17/96 Created Gerardob
\***************************************************************************/
LPSTR dsoTerminateString(LPSTR psz, PDWORD pdwSize)
{
    LPSTR pszWork = psz;

    while (*pszWork != 0) {
        if (*pszWork == ' ') {
            *pszWork++ = 0;
            break;
        }
        pszWork++;
    }

    *pdwSize = (DWORD)(pszWork - psz);
    if (*pszWork != 0) {
        (*pdwSize)--;
    }

    while ((*pszWork != 0) && (*pszWork == ' ')) {
        pszWork++;
    }

    return pszWork;
}
/***************************************************************************\
* dsoGetOffset
*
* If the highest order bit of psot->dwOffset is set, then the value is a
*  relative offset from the previous field; otherwise, it is the
*  actual field offset from the beginnig of the structure
*
* 06/20/96 Created Gerardob
\***************************************************************************/
UINT dsoGetOffset (PSTRUCTUREOFFSETSTABLE psot)
{
    if (!(psot->dwOffset & 0x80000000)) {
        return psot->dwOffset;
    } else {
        return ((psot->dwOffset & ~0x80000000) + dsoGetOffset(psot - 1));
    }
}
/***************************************************************************\
* dsoGetSize
*
* The field size is calculated by substracting its offset from the next
*  field's offset. If the struct has unions, several "fields" might have
*  the same offset, or a given table entry (i.e., a field) might have an
*  offset value greater than the offset value for the next entry (a union
*  of two structures).
*
* 06/26/96 Created Gerardob
\***************************************************************************/
UINT dsoGetSize (PSTRUCTUREOFFSETSTABLE psot, DWORD dwOffset)
{
    DWORD dwNextFieldOffset;

    do {
        psot++;
        dwNextFieldOffset = dsoGetOffset(psot);
    } while (dwNextFieldOffset <= dwOffset);

    return dwNextFieldOffset - dwOffset;
}
/***************************************************************************\
* dsoGetStruct
*
* 07/03/96 Created Gerardob
\***************************************************************************/
PSTRUCTURESTABLE dsoGetStruct (LPSTR pszStruct, DWORD dwSize)
{
    PSTRUCTURESTABLE pst = gst;

    /*
     * If dwSize is EXACT_MATCH, we try an exact
     * case sensitive match
     */
    if (dwSize == EXACT_MATCH) {
        while (pst->pszName != NULL) {
            if (!strcmp(pszStruct, pst->pszName)) {
                return pst;
            }
            pst++;
        }
        return NULL;
    }
    /*
     * Try an exact case insensitive match
     */
    while (pst->pszName != NULL) {
        if (!_stricmp(pszStruct, pst->pszName)) {
            return pst;
        }
        pst++;
    }

    /*
     * Partial prefix match
     */
    pst = gst;
    while (pst->pszName != NULL) {
        if (!_strnicmp(pszStruct, pst->pszName, dwSize)) {
            return pst;
        }
        pst++;
    }

    return NULL;

}
/***************************************************************************\
* dsoGetField
*
* 07/03/96 Created Gerardob
\***************************************************************************/
PSTRUCTUREOFFSETSTABLE dosGetField (PSTRUCTUREOFFSETSTABLE psot, LPSTR pszField, DWORD dwSize)
{
    PSTRUCTUREOFFSETSTABLE psotFirst = psot;

    /*
     * try an exact match
     */
    while (psot->pszField != NULL) {
        if (!_stricmp(pszField, psot->pszField)) {
            return psot;
        }
        psot++;
    }

    /*
     * Partial prefix match
     */
    psot = psotFirst;
    while (psot->pszField != NULL) {
        if (!_strnicmp(pszField, psot->pszField, dwSize)) {
            return psot;
        }
        psot++;
    }
    return NULL;

}
/***************************************************************************\
* Idso
*
* !dso StructName [FieldName] [Address]
*
* 06/17/96 Created Gerardob
* 05/12/97 MCostea Added bit field support
\***************************************************************************/

#define NFIELDS 2  // per row.

BOOL Idso(DWORD opts, LPSTR pszCmdLine)
{
    BOOL fOneField = FALSE;
    DWORD dwValue, dwSize, dwBytesRead, dwOffset, dwOffsetNext, dwFieldsPerRow, dwMoveSize;
    DWORD dwBuffer [20];  /* Make sure it has an even number of elemnts and at least 4*/
    const DWORD *pcdwLimit = dwBuffer + (sizeof(dwBuffer) / sizeof(*dwBuffer));
    DWORD *pdwValue;
    LPSTR pszField, pszAddress;
    PBYTE pBufferOffset;
    PSTRUCTURESTABLE pst, pstNested;
    PSTRUCTUREOFFSETSTABLE psot;
    PVOID pAddress = NULL;

    int   cBFStart, cBFLength;   /* for printing bit field values: keeps count of field location */
    char* pTmp;
    DWORD dwMask;
    BOOL  fBF;
    int   cBF;                   /* no of dwords this set of bit-fields spread till now */
    int   cchName;               /* length of field name */
    int   nIndent = gnIndent;    /* amount to indent */

    UNREFERENCED_PARAMETER(opts);

    if (pszCmdLine == NULL) {
        return FALSE;
    }

    /*
     * Find the struct table
     */
    pszField = dsoTerminateString(pszCmdLine, &dwSize);
    pst = dsoGetStruct (pszCmdLine, dwSize);
    if (pst == NULL) {
        DsoPrint(("Structure not found: %s\n", pszCmdLine));
        return TRUE;
    }

    /*
     * Got a table
     */
    psot = pst->psot;

    /*
     * If there is another argument, let's assume a field name follows
     */
    if (*pszField != 0) {
        /*
         * Find the field
         */
        pszAddress = dsoTerminateString(pszField, &dwSize);
        psot = dosGetField (psot, pszField, dwSize);

        /*
         * If it didn't find the field and an address was provided, game over.
         * Otherwise, the second parameter might be the address
         */
        if (psot == NULL) {
            if (*pszAddress != 0) {
                DsoPrint(("Field not found: %s. Struct: %s\n", pszField, pst->pszName));
                return TRUE;
            } else {
                pszAddress = pszField;
                /*
                 * Reset psot since this argument was not a field
                 */
                psot = pst->psot;
            }
        } else {
            fOneField = TRUE;
        }
    
        /*
         * Get the pointer to the struct
         */
        if (*pszAddress != 0) {
            pAddress = (PVOID) EvalExp(pszAddress);
            if (pAddress == NULL) {
                /*
                 * EvalExp displayed the error message, so return silently
                 */
                return TRUE;
            }
        }

    } /* if (*pszField != 0) */


    /*
     * If a field name was specified, dump that field only
     * Otherwise, dump the whole table.
     */
    if (fOneField) {
        /*
         * If no address available, just display the field name and offset
         */
        dwOffset = dsoGetOffset(psot);

        DsoPrint(("Structure %s - Size: %#lx\n", pst->pszName, pst->dwSize));

        /*
         * Try to see if the fields are not nested structures
         */
        if (*psot->pszField >= 'A' && *psot->pszField <= 'Z') {
            /*
             * Probably the field is a nested structure
             */
            if (pstNested = dsoGetStruct (psot->pszField, EXACT_MATCH)) {
                DWORD dwNestedOffset = dsoGetOffset(psot);
                char cmdLine[80];
                DsoPrint(("\nNested at offset %03lx: ", dwNestedOffset));
                if (pAddress) {
                    sprintf(cmdLine, "%s %p", psot->pszField, (PBYTE)pAddress + dwNestedOffset);
                    pTmp = cmdLine;
                }
                else {
                    pTmp = psot->pszField;
                }
                return Idso(opts, pTmp);
            }
        }

        DsoPrint(("Field: %s - Offset: %#lx\n", psot->pszField, dwOffset));
        if (pAddress == NULL) {
            return TRUE;
        }

        /*
         * Printing field value
         */

        /*123456789 1*/
        DsoPrint(("Address   Value\n"));

        dwBytesRead = 0;
        dwSize = dsoGetSize(psot, dwOffset);
        /*
         * Print 4 DWORDS per row; one row per loop
         */

        do { /* while ((int)dwSize > 0) */

            /*
             * Read values for next row
             */
            if (4 * sizeof(DWORD) >= dwSize) {
                dwMoveSize = dwSize;
            } else {
                dwMoveSize = 4 * sizeof(DWORD);
            }
            moveBlock(dwBuffer, (PBYTE)pAddress + dwOffset + dwBytesRead, dwMoveSize);
            pBufferOffset = (PBYTE)dwBuffer;

            /*
             * Print the address
             */
            DsoPrint(("%p  ", (DWORD_PTR)((PBYTE)pAddress + dwOffset + dwBytesRead)));
            /*
             * Keep track of bytes read (dwBytesRead) and bytes
             *  remaining to be read (dwSize)
             */
            dwBytesRead += dwMoveSize;
            dwSize -= dwMoveSize;
            /*
             * Print the values, one dword at the time
             */
            while (dwMoveSize >= sizeof(DWORD)) {
                DsoPrint(("%08lx ", *((DWORD *)pBufferOffset)));
                pBufferOffset += sizeof(DWORD);
                dwMoveSize -= sizeof(DWORD);
            }
            /*
             * If less than a DWORD left, zero extend and print a DWORD
             */
            if (dwMoveSize > 0) {
                dwValue = 0;
                memcpy(&dwValue, pBufferOffset, dwMoveSize);
                DsoPrint(("%0*lx", dwMoveSize * 2, dwValue));
            }
            dprintf("\n");

        } while ((int)dwSize > 0);

        return TRUE;

    } /* if (fOneField) */


    /*
     * Printing all the fields.
     */
    if (pAddress != NULL) {
        DsoPrint(("Structure %s %#lx - Size: %#lx", pst->pszName, pAddress, pst->dwSize));
    } else {
        DsoPrint(("Structure %s - Size: %#lx", pst->pszName, pst->dwSize));
    }

    dwOffset = 0;
    pBufferOffset = NULL; /* Forces the local buffer to be loaded */
    dwFieldsPerRow = NFIELDS;
    cBFStart = 0;
    cBF = 0;

    /*
     * Loop through all fields in the table. Print one field per loop
     */

    while (psot->pszField != NULL) {

        /*
         * Print two fields per row
         */
        if (dwFieldsPerRow == NFIELDS) {
            dprintf("\n");
            dwFieldsPerRow = 1;
            cchName = 24 - gnIndent/NFIELDS;
            nIndent = gnIndent;
            // Print("cchName = %d\n", cchName);
        } else {
            dwFieldsPerRow++;
            cchName = 24 - (gnIndent + 1)/NFIELDS;
            nIndent = 0;
            // Print("cchName = %d\n", cchName);
        }

        /*
         * -v functionality
         * Try to see if the fields are not nested structures
         * The naming convention assigns Uppercase names for them
         */
        if (opts & OFLAG(v)) {
            if (*psot->pszField >= 'A' && *psot->pszField <= 'Z') {
                /*
                 * Probably the field is a nested structure
                 */
                if (pstNested = dsoGetStruct (psot->pszField, EXACT_MATCH)) {
                    DWORD dwNestedOffset = dsoGetOffset(psot);
                    char cmdLine[80];
                    DsoPrint(("\nNested at offset %03lx: ", dwNestedOffset));
                    if (pAddress) {
                        sprintf(cmdLine, "%s %p", psot->pszField, (PBYTE)pAddress + dwNestedOffset);
                        pTmp = cmdLine;
                    }
                    else {
                        pTmp = psot->pszField;
                    }
                    Idso(opts, pTmp);
                    dwOffsetNext = dsoGetOffset(psot + 1);
                    dwFieldsPerRow = 0;
                    goto Continue;
                }
            }
        }

        /*
         * If no address provided, Print field name(s) and offset(s) only
         */
        if (pAddress == NULL) {
            DsoPrint(("%03lx %-*.*s", dsoGetOffset(psot),
                      cchName, cchName, psot->pszField));
        } else {
            /*
             * Printing offsets and values.
             *
             * Get the size of the value and max it to one DWORD
             */
            dwOffsetNext = dsoGetOffset(psot + 1);
            if (dwOffsetNext > dwOffset) {
                dwSize = dwOffsetNext - dwOffset;
            } else {
                dwSize = dsoGetSize(psot, dwOffset);
            }
            if (dwSize > sizeof(DWORD)) {
                dwSize = sizeof(DWORD);
            }

            /*
             * Get a pointer to the value in the local buffer
             * If the value is not in the buffer, load it
             */
            pdwValue = (PDWORD)(pBufferOffset + dwOffset);
            if ((pdwValue < dwBuffer) || (pdwValue + dwSize > pcdwLimit)) {
                pBufferOffset = (PBYTE)dwBuffer - dwOffset;
                pdwValue = dwBuffer;

                if (sizeof(dwBuffer) >= pst->dwSize - dwOffset) {
                    dwMoveSize = pst->dwSize - dwOffset;
                } else {
                    dwMoveSize = sizeof(dwBuffer);
                }
                moveBlock((PBYTE)dwBuffer, (PBYTE)pAddress + dwOffset, dwMoveSize);

            }

            /*
             * Copy the value and print it
             */
            dwValue = 0; /* in case size < sizeof(DWORD) */
            memcpy(&dwValue, pdwValue, dwSize);

            /*
             * Deal with bit fields
             */
            fBF = FALSE;
            pTmp = psot->pszField;
            while (*pTmp) {
                if (*pTmp++ == ':') {

                    fBF = TRUE;
                    while ((*pTmp == ' ') || (*pTmp == '\t')) {     /* skip white spaces */
                        ++pTmp;
                    }
                    cBFLength = *(pTmp++) - '0';      /* now get the bit size, maybe 2 digits */
                    if ((*pTmp >= '0') && (*pTmp <= '9'))
                        cBFLength = cBFLength*10 + (*pTmp - '0');

                    if (cBFStart == 0) {
                        DsoPrint(("(%03lx) %08lx BIT FIELDS\n", dwOffset, dwValue));
                        dwFieldsPerRow = 1;
                    }
                    else if (cBFStart >= 8*sizeof(DWORD)) {   /* check for multi-dword fields */
                        cBF ++;
                        cBFStart %= 8*sizeof(DWORD);
                    }

                    dwMask = (1L << cBFLength) - 1;
                    dwMask <<= cBFStart;
                    /* print byte offset and the bit offset inside it */
                    DsoPrint(("(%03lx) (%d)   %-2x %-*.*s", dwOffset + cBF*sizeof(DWORD) + cBFStart/8, cBFStart & 7,
                           (dwMask & dwValue) >> cBFStart,
                            cchName, cchName, psot->pszField));
                    cBFStart += cBFLength;
                    cBFLength = 0;
                    break;
                }
            }
            if (!fBF) {
                int width = 8;
                if (dwSize == sizeof(BYTE)) {
                    dwValue &= 0xff;
                    width = 2;
                } else if (dwSize == sizeof(WORD)) {
                    dwValue &= 0xffff;
                    width = 4;
                }
                DsoPrint(("(%03lx) %*s%0*lx %-*.*s", dwOffset, 8 - width, "", width, dwValue,
                          cchName, cchName, psot->pszField));
                cBFStart = 0;
                cBF = 0;
            }

        } /* if (pAddress == NULL) */

Continue:
        dwOffset = dwOffsetNext;
        psot++;

    } /* while (psot->pszField != NULL) */

    dprintf("\n");

    return TRUE;
}

DECLARE_API( gpcdso )
{
    LPSTR pszCmdLine; 
    DWORD opts = 0;

    pszCmdLine = (LPSTR) args;
    Idso(opts, pszCmdLine);
}
