//////////////////////////////////////////////////////////////////////////////
// ppm2pps
//
// Copyright (c) 1996-1999 Microsoft Corporation
//
//     Dump contents of ppm file
//
//////////////////////////////////////////////////////////////////////////////

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <imagehlp.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gen.h"

// string to put in front of all error messages so that BUILD can find them.
const char *ErrMsgPrefix = "NMAKE :  U8603: 'PPM2PPS' ";

//
// global variables
BOOL fDebug;            // global debug flag
BOOL fDumpCCheck;       // when set build ppswind.h for checking sizes/offsets
HANDLE hFile;
HANDLE hMapFile = NULL;

PRBTREE pFunctions = NULL;
PRBTREE pStructures = NULL;
PRBTREE pTypedefs = NULL;
PKNOWNTYPES NIL = NULL;

// function prototypes
void __cdecl ErrMsg(char *pch, ...);
void __cdecl ExitErrMsg(BOOL bSysError, char *pch, ...);
BOOL DumpTypedefs(FILE *filePpsfile,            // file to write output
                   BOOL fDumpNamedOnly,         // when set don't do unnamed 
                   PRBTREE pHead);              // known types function list
BOOL DumpStructures(FILE *filePpsfile,          // file to write output
                   BOOL fDumpNamedOnly,         // when set don't do unnamed 
                   PRBTREE pHead);              // known types function list
BOOL DumpFunctions(FILE *filePpsfile,           // file to write output
                   BOOL fDumpNamedOnly,         // when set don't do unnamed
                   PRBTREE pHead);              // known types function list
void Usage(char *s);
BOOL ParseArguments(int argc, char *argv[], char *sPpmfile, char *sPpsfile,
                    BOOL *pfDumpNamedOnly, BOOL *pfDebug, 
                    BOOL *pfDumpCCheck);
BOOL DumpCCheckHeader(PRBTREE pTypedefs,   // typedef lsit
                      PRBTREE pStructs);   // structs sit

                        
int __cdecl main(int argc, char *argv[])
{
    void *pvPpmData = NULL;
    BOOL fDumpNamedOnly;
    char sPpmfile[MAX_PATH];
    char sPpsfile[MAX_PATH];
    FILE *pfilePpsfile;

    try {

    if (! ParseArguments(argc, argv, sPpmfile, sPpsfile, 
                                    &fDumpNamedOnly, &fDebug, &fDumpCCheck))
    {
        Usage(argv[0]);
        return(-1);
    }
    
    if (*sPpmfile)
    {
        PCVMHEAPHEADER pHeader;

        pvPpmData = MapPpmFile(sPpmfile, TRUE);

        pHeader = (PCVMHEAPHEADER)pvPpmData;

        pFunctions = &pHeader->FuncsList;
        pTypedefs =  &pHeader->TypeDefsList;
        pStructures =&pHeader->StructsList;
        NIL         =&pHeader->NIL;
    
        pfilePpsfile = fopen(sPpsfile, "w");
        if (pfilePpsfile == 0)
        {
            ErrMsg("ERROR - Could not open output file %s\n", sPpsfile);
            CloseHandle(hFile);
            CloseHandle(hMapFile);
            return(-1);
        }
    
        if (DumpFunctions(pfilePpsfile, fDumpNamedOnly, 
                                                    pFunctions))
        {
            if (DumpStructures(pfilePpsfile, fDumpNamedOnly, 
                                                    pStructures))
            {
                DumpTypedefs(pfilePpsfile,fDumpNamedOnly, pTypedefs);
            }
        }
        fclose(pfilePpsfile);
    }
    
    if (fDumpCCheck && pTypedefs && pStructures)
    {
        DumpCCheckHeader(pTypedefs, pStructures);
    }
        
    CloseHandle(hFile);
    CloseHandle(hMapFile);

   } except(EXCEPTION_EXECUTE_HANDLER) {
       ExitErrMsg(FALSE,
                  "ExceptionCode=%x\n",
                  GetExceptionCode()
                  );
       }

    return(0);
}

void
DumpFuncinfo(FILE *pfilePpsfile, PFUNCINFO pf)
{
    while (pf) {
        int i;

        fprintf(pfilePpsfile,
                "%s %s%s %s %s %s",
                TokenString[pf->tkDirection],
                (pf->fIsPtr64) ? "__ptr64 " : "",
                TokenString[pf->tkPreMod],
                TokenString[pf->tkSUE],
                pf->sType,
                TokenString[pf->tkPrePostMod]
                );
        i = pf->IndLevel;
        while (i--) {
            fprintf(pfilePpsfile, "*");
        }
        fprintf(pfilePpsfile,
                "%s %s",
                TokenString[pf->tkPostMod],
                (pf->sName) ? pf->sName : ""
                );

        pf = pf->pfuncinfoNext;
        if (pf) {
            fprintf(pfilePpsfile, ", ");
        }
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  DumpCCheckHeader
//
//      dump header file that can be used to check sizes in ppm file against
//      those that are generated by C
//
//      returns TRUE on success
//
/////////////////////////////////////////////////////////////////////////////
BOOL DumpCCheckHeader(PRBTREE pTypedefs,   // typedef lsit
                      PRBTREE pStructs)    // structs lsit

{
    PKNOWNTYPES pknwntyp, pknwntypBasic;
    FILE *pfile;
    
    pfile = fopen("ppswind.h", "w");
    if (pfile == NULL) 
    {
        ErrMsg("Error opening ppwind.h for output\n");
        return(FALSE);
    }
    
    fprintf(pfile, "CCHECKSIZE cchecksize[] = {\n");

//
// typedefs
    pknwntyp = pTypedefs->pLastNodeInserted;

    while (pknwntyp) {
        if ((! isdigit(*pknwntyp->TypeName)) &&
            (strcmp(pknwntyp->TypeName,"...")) &&
            (strcmp(pknwntyp->TypeName,"()")) && 
            (strcmp(pknwntyp->BasicType, szFUNC)) &&
            (pknwntyp->Size > 0) &&
            (pknwntyp->dwScopeLevel == 0)) {

            pknwntypBasic = GetBasicType(pknwntyp->TypeName, 
                                     pTypedefs, pStructs);

            if (! ( (pknwntypBasic == NULL) || 
                    ( (! strcmp(pknwntypBasic->BaseName, szVOID)) &&
                      (pknwntypBasic->pmeminfo == NULL)))) {
 
                fprintf(pfile, " { %4d, sizeof(%s), \"%s\"}, \n",
                    pknwntyp->Size,
                    pknwntyp->TypeName,
                    pknwntyp->TypeName);        
            }
        }
        pknwntyp = pknwntyp->Next;
    }
    
    
//
// structs
    pknwntyp = pStructs->pLastNodeInserted;

    while (pknwntyp) {
        if ((! isdigit(*pknwntyp->TypeName) &&
            (pknwntyp->pmeminfo)))
        {
            if (!(pknwntyp->Flags & BTI_ANONYMOUS) && (pknwntyp->Size > 0) && (pknwntyp->dwScopeLevel == 0)) {
                fprintf(pfile, " { %4d, sizeof(%s %s), \"%s %s\" }, \n",
                    pknwntyp->Size,
                    pknwntyp->BaseName,
                    pknwntyp->TypeName,
                    pknwntyp->BaseName,
                    pknwntyp->TypeName);
            }
        }
        pknwntyp = pknwntyp->Next;
    }

    fprintf(pfile, " {0xffffffff, 0xffffffff,  \"\"}\n");
    fprintf(pfile,"\n};\n");
    
//
// structs fields
    fprintf(pfile, "CCHECKOFFSET ccheckoffset[] = {\n");

    pknwntyp = pStructs->pLastNodeInserted;

    while (pknwntyp) {
        if (! isdigit(*pknwntyp->TypeName)) 
        {
            if (!(pknwntyp->Flags & BTI_ANONYMOUS) && !(pknwntyp->Flags & BTI_VIRTUALONLY) && (pknwntyp->Size > 0) && (pknwntyp->dwScopeLevel == 0)) {
                PMEMBERINFO pmeminfo = pknwntyp->pmeminfo;
                while (pmeminfo != NULL) {
                    if ((pmeminfo->sName != NULL) && (*pmeminfo->sName != 0) && !(pmeminfo->bIsBitfield))
                    { 
                        fprintf(pfile, " { %4d, (long) (& (((%s %s *)0)->%s)), \"%s\", \"%s\" },\n",
                            pmeminfo->dwOffset,
                            pknwntyp->BaseName,
                            pknwntyp->TypeName,
                            pmeminfo->sName,
                            pknwntyp->TypeName,                   
                            pmeminfo->sName);
                    }
                    pmeminfo = pmeminfo->pmeminfoNext;
                }
                
            }
        }
        pknwntyp = pknwntyp->Next;
    }
    
    fprintf(pfile, " {0xffffffff, 0xffffffff, \"\", \"\"}\n");
    fprintf(pfile,"\n};\n");
    fclose(pfile);
    return(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
//
//  DumpTypedefs
//
//      dump structures from ppm file to output file
//
//      returns TRUE on success
//
/////////////////////////////////////////////////////////////////////////////
BOOL DumpTypedefs(FILE *pfilePpsfile,            // file to write output
                   BOOL fDumpNamedOnly,          // when set don't do unnamed 
                   PRBTREE pHead)                // known types function list
{
    KNOWNTYPES *pknwntyp;

    pknwntyp = pHead->pLastNodeInserted;

    fprintf(pfilePpsfile,"[Typedefs]\n\n");
    while (pknwntyp) {
        fprintf(pfilePpsfile,
                   "%2.1x|%2.1x|%2.1x|%s|%s|%s|%s|%s|",
                   pknwntyp->Flags,
                   pknwntyp->IndLevel,
                   pknwntyp->Size,
                   pknwntyp->BasicType,
                   pknwntyp->BaseName ? pknwntyp->BaseName : szNULL,
                   pknwntyp->FuncRet ? pknwntyp->FuncRet : szNULL,
                   pknwntyp->FuncMod ? pknwntyp->FuncMod : szNULL,
                   pknwntyp->TypeName
                   );
        DumpFuncinfo(pfilePpsfile, pknwntyp->pfuncinfo);
        fprintf(pfilePpsfile, "|\n");

        pknwntyp = pknwntyp->Next;
    }
    return(TRUE);
}


/////////////////////////////////////////////////////////////////////////////
//
//  DumpStructures
//
//      dump structures from ppm file to output file
//
//      returns TRUE on success
//
/////////////////////////////////////////////////////////////////////////////
BOOL DumpStructures(FILE *pfilePpsfile,          // file to write output
                   BOOL fDumpNamedOnly,          // when set don't do unnamed 
                   PRBTREE pHead)                // known types function list
{
    KNOWNTYPES *pknwntyp;
    DWORD dw;
    PMEMBERINFO pmeminfo;

    pknwntyp = pHead->pLastNodeInserted;

    fprintf(pfilePpsfile,"[Structures]\n\n");
    while (pknwntyp) {
        if (! fDumpNamedOnly || ! isdigit(*pknwntyp->TypeName)) {
            fprintf(pfilePpsfile,
                   "%2.1x|%2.1x|%2.1x|%s|%s|%s|%s|%s|",
                   pknwntyp->Flags,
                   pknwntyp->IndLevel,
                   pknwntyp->Size,                
                   pknwntyp->BasicType,
                   pknwntyp->BaseName ? pknwntyp->BaseName : szNULL,
                   pknwntyp->FuncRet ? pknwntyp->FuncRet : szNULL,
                   pknwntyp->FuncMod ? pknwntyp->FuncMod : szNULL,
                   pknwntyp->TypeName);

            // dump out the structure member info, if present
            pmeminfo = pknwntyp->pmeminfo;
            while (pmeminfo) {
                int i;

                fprintf(pfilePpsfile, "%s", pmeminfo->sType);
                i = pmeminfo->IndLevel;
                if (i) {
                    fprintf(pfilePpsfile, " ");
                    while (i--) {
                        fprintf(pfilePpsfile, "*");
                    }
                }
                if (pmeminfo->sName) {
                    fprintf(pfilePpsfile, " %s", pmeminfo->sName);
                }
                fprintf(pfilePpsfile, " @ %d|", pmeminfo->dwOffset);
                pmeminfo = pmeminfo->pmeminfoNext;
            }

            // dump out the function info, if present
            DumpFuncinfo(pfilePpsfile, pknwntyp->pfuncinfo);

            fprintf(pfilePpsfile, "\n");
        }

        pknwntyp = pknwntyp->Next;
    }
    return(TRUE);
}


/////////////////////////////////////////////////////////////////////////////
//
//  DumpFunctions
//
//      dump fucntion prototypes from ppm file to output file
//
//      returns TRUE on success
//
/////////////////////////////////////////////////////////////////////////////
BOOL DumpFunctions(FILE *pfilePpsfile,            // file to write output
                   BOOL fDumpNamedOnly,           // when set don't do unnamed
                   PRBTREE pHead)                 // known types function list
{
    KNOWNTYPES *pknwntyp;
    PFUNCINFO pf;

    pknwntyp = pHead->pLastNodeInserted;

    fprintf(pfilePpsfile,"[Functions]\n\n");
    while (pknwntyp) {
        fprintf(pfilePpsfile,
                   "%s|%s|%s|%s|",
                   (pknwntyp->Flags & BTI_DLLEXPORT) ? "dllexport" : "",
                   pknwntyp->FuncRet,
                   pknwntyp->FuncMod ? pknwntyp->FuncMod : szNULL,
                   pknwntyp->TypeName
                   );
        DumpFuncinfo(pfilePpsfile, pknwntyp->pfuncinfo);
        fprintf(pfilePpsfile, "|\n");
        pknwntyp = pknwntyp->Next;
    }
    return(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Usgae
//
//      tells how to use
//
//
/////////////////////////////////////////////////////////////////////////////
void Usage(char *s)        // name of command invoked
{
    printf("Usage:\n");
    printf("    %s -d -n -x <ppm file> <pps output file>\n", s);
    printf("        -d set debug flag\n");
    printf("        -n dumps only named structs/enums/unions\n");
    printf("        -x creates ppswind.h for size/offset checking\n");
}

/////////////////////////////////////////////////////////////////////////////
//
//  ParseArgumaners
//
//      parse arguments
//
//      returnms FALSE on syntax error
//
/////////////////////////////////////////////////////////////////////////////

BOOL ParseArguments(int argc, char *argv[], char *sPpmfile, char *sPpsfile,
                    BOOL *pfDumpNamedOnly, BOOL *pfDebug, 
                    BOOL *pfDumpCCheck)
{
    int i;
    
    *sPpmfile = 0;
    *sPpsfile = 0;
    *pfDumpNamedOnly = FALSE;
    *pfDebug = FALSE;
    *pfDumpCCheck = FALSE;
    
    for (i = 1; i < argc; i++)
    {
        if (*argv[i] == '-')
        {
            switch(tolower(argv[i][1]))
            {
                case 'd':
                {
                    *pfDebug = TRUE;
                    break;
                }
                
                case 'n':
                {
                    *pfDumpNamedOnly = TRUE;
                    break;
                }
                
                case 'x':
                {
                    *pfDumpCCheck = TRUE;
                    break;
                }
                
                default:
                {
                    return(FALSE);
                }
            }
        } else {
            if (lstrlenA(argv[i]) >= MAX_PATH)
            {
                return(FALSE);
            }
            if (*sPpmfile == 0)
            {
                strcpy(sPpmfile, argv[i]);
            } else if (*sPpsfile == 0)
            {
                strcpy(sPpsfile, argv[i]);
            } else {
                return(FALSE);
            }
        }        
    }
    return( *pfDumpCCheck || ((*sPpmfile != 0) && (*sPpsfile != 0)));
}

void
HandlePreprocessorDirective(
    char *p
    )
{
    ExitErrMsg(FALSE, "Preprocessor directives not allowed by ppm2pps\n");
}
