/*++

Copyright 1996 - 1997 Microsoft Corporation

Module Name:

    symedit.c

Abstract:


Author:

    Wesley A. Witt (wesw) 19-April-1993

Environment:

    Win32, User Mode

--*/

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <symcvt.h>
#include <cv.h>
#include "strings.h"
#include <imagehlp.h>

#undef UNICODE

#define MAX_PATH 260 

//  prototypes for this module

BOOL    CalculateOutputFilePointers( PIMAGEPOINTERS pi, PIMAGEPOINTERS po );
void    ProcessCommandLineArgs( int argc, WCHAR *argv[] );
void    PrintCopyright( void );
void    PrintUsage( void );
void    FatalError( int, ... );
BOOL    MapOutputFile ( PPOINTERS p, char *fname, int );
void    ComputeChecksum(  char *szExeFile );
void    ReadDebugInfo( PPOINTERS p );
void    WriteDebugInfo( PPOINTERS p, BOOL);
void    MungeDebugHeadersCoffToCv( PPOINTERS  p, BOOL fAddCV );
void    MungeExeName( PPOINTERS p, char * szExeName );
void    DoCoffToCv(char *, char *, BOOL);
void    DoSymToCv(char *, char *, char *, char *);
void    DoNameChange(char *, char *, char *);
void    DoExtract(char *, char *, char *);
void    DoStrip(char *, char *);

IMAGE_DEBUG_DIRECTORY   DbgDirSpare;

#define AdjustPtr(ptr) (((ptr) != NULL) ? \
                          ((DWORD)ptr - (DWORD)pi->fptr + (DWORD)po->fptr) : \
                          ((DWORD)(ptr)))


int _cdecl
wmain(
    int        argc,
    WCHAR *     argv[]
    )
/*++

Routine Description:

    Shell for this utility.

Arguments:

    argc     - argument count
    argv     - argument pointers


Return Value:

    0        - image was converted
    >0       - image could not be converted

--*/

{
    // Scan the command line and check what operations we are doing

    ProcessCommandLineArgs( argc, argv );
    return 0;
}


__inline void PrintCopyright( void )
{
    puts( "\nMicrosoft(R) Windows NT SymEdit Version 1.0\n"
          "(C) 1989-1995 Microsoft Corp. All rights reserved.\n");
}


__inline void PrintUsage( void )
{
    PrintCopyright();
    puts ("\nUsage: SYMEDIT <OPERATION> -q -o<file out> <file in>\n\n"
          "\t<OPERATION> is:\n"
          "\tC\tModify CodeView symbol information\n"
          "\tN\tEdit name field\n"
          "\tX\tExtract debug information\n"
          "\tS\tStrip all debug information\n\n"
          "Options:\n"
          "\t-a\t\tAdd CodeView debug info to file\n" 
          "\t-n<name>\tName to change to\n"
          "\t-o<file>\tspecify output file\n"
          "\t-q\t\tquiet mode\n"
          "\t-r\t\tReplace COFF debug info with CV info\n"
          "\t-s<file>\tSym file source");
}


void
ProcessCommandLineArgs(
    int argc,
    WCHAR *argv[]
    )

/*++

Routine Description:

    Processes the command line arguments and sets global flags to
    indicate the user's desired behavior.

Arguments:

    argc     - argument count
    argv     - argument pointers


Return Value:

    void

--*/

{
    int     i;
    BOOL    fQuiet = FALSE;
    BOOL    fSilent = FALSE;
    char    szOutputFile[MAX_PATH];
    char    szInputFile[MAX_PATH];
    char    szExeName[MAX_PATH];
    char    szDbgFile[MAX_PATH];
    char    szSymFile[MAX_PATH];
    int     iOperation;
    BOOLEAN fAddCV = FALSE;

    // Minimun number of of arguments is 2 -- program and operation

    if (argc < 2 ||
        (wcscmp(argv[1], L"-?") == 0) ||
        (wcscmp(argv[1], L"?") == 0) )
    {
        PrintUsage();
        exit(1);
    }

    // All operations on 1 character wide

    if (argv[1][1] != 0) {
        FatalError(ERR_OP_UNKNOWN, argv[1]);
    }

    // Validate the operation

    switch( argv[1][0] ) {
        case L'C':
        case L'N':
        case L'X':
        case L'S':
            iOperation = argv[1][0];
            break;
        default:
            FatalError(ERR_OP_UNKNOWN, argv[1]);
    }

    // Parse out any other switches on the command line

    for (i=2; i<argc; i++) {
        if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
            switch (towupper(argv[i][1])) {

                // Add the CV debug information section rather than
                // replace the COFF section with the CV info.

                case L'A':
                    fAddCV = TRUE;
                    break;

                // Specify the output name for the DBG file

                case L'D':
                    if (argv[i][2] == 0) {
                        i += 1;
                        wcstombs(szDbgFile, argv[i], MAX_PATH);
                    } else {
                        wcstombs(szDbgFile, &argv[i][2], MAX_PATH);
                    }
                    break;

                // Specify a new name to shove into the name of the
                // debuggee field in the Misc. Debug info field

                case L'N':
                    if (argv[i][2] == 0) {
                        i += 1;
                        wcstombs(szExeName, argv[i], MAX_PATH);
                    } else {
                        wcstombs(szExeName, &argv[i][2], MAX_PATH);
                    }
                    break;

                // Specify the name of the output file

                case L'O':
                    if (argv[i][2] == 0) {
                        i += 1;
                        wcstombs(szOutputFile,argv[i], MAX_PATH);
                    } else {
                        wcstombs(szOutputFile, &argv[i][2], MAX_PATH);
                    }
                    break;

                // Be quite and don't put out the banner

                case L'Q':
                    fQuiet = TRUE;
                    fSilent = TRUE;
                    break;

                //  Replace COFF debug information with CODEVIEW debug information

                case L'R':
                    break;

                //  Convert a Symbol File to CV info

                case L'S':
                    if (argv[i][2] == 0) {
                        i += 1;
                        wcstombs(szSymFile, argv[i], MAX_PATH);
                    } else {
                        wcstombs(szSymFile, &argv[i][2], MAX_PATH);
                    }
                    break;

                // Print the command line options

                case L'?':
                    PrintUsage();
                    exit(1);
                    break;

                // Unrecognized option

                default:
                    FatalError( ERR_OP_UNKNOWN, argv[i] );
                    break;
            }
        } else {
            //  No leading switch character -- must be a file name

            wcstombs(szInputFile, &argv[i][0], MAX_PATH);

            //  Process the file(s)

            if (!fQuiet) {
                PrintCopyright();
                fQuiet = TRUE;
            }

            if (!fSilent) {
                printf("processing file: %s\n", szInputFile );
            }

            //  Do switch validation cheching and setup any missing global variables

            switch ( iOperation ) {

                // For conversions -- there are three types
                //
                //      1.  Coff to CV -- add
                //      2.  Coff to CV -- replace
                //      3.  SYM to CV --- add
                //      4.  SYM to CV -- seperate file
                //
                //      Optional input file (not needed for case 4)
                //      Optional output file
                //      Optional sym file (implys sym->CV)
                //      Optional DBG file

                case 'C':
                    if (szSymFile == NULL) {
                        DoCoffToCv(szInputFile, szOutputFile, fAddCV);
                    } else {
                        DoSymToCv(szInputFile, szOutputFile, szDbgFile, szSymFile);
                    }
                    break;

                //  For changing the name of the debuggee --
                //      Must specify input file
                //      Must specify new name
                //      Optional output file

                case 'N':
                    DoNameChange(szInputFile, szOutputFile, szExeName);
                    break;

                //  For extraction of debug information
                //      Must specify input file
                //      Optional output file name
                //      Optional debug file name

                case 'X':
                    DoExtract(szInputFile, szOutputFile, szDbgFile);
                    break;

                //  For full strip of debug information
                //      Must specify input file
                //      Optional output file

                case 'S':
                    DoStrip(szInputFile, szOutputFile);
                    break;
            }
        }
    }
    return;
}


void
ReadDebugInfo(
    PPOINTERS   p
    )

/*++

Routine Description:

    This function will go out and read in all of the debug information
    into memory -- this is required because the input and output
    files might be the same, if so then writing out informaiton may
    destory data we need at a later time.

Arguments:

    p   - Supplies a pointer to the structure describing the debug info file

Return Value:

    None.

--*/

{
    int                         i;
//    int                         cb;
//    char *                      pb;
//    PIMAGE_COFF_SYMBOLS_HEADER  pCoffDbgInfo;

    // Allocate space to save pointers to debug info

    p->iptrs.rgpbDebugSave = (PCHAR *) malloc(p->iptrs.cDebugDir * sizeof(PCHAR));
    memset(p->iptrs.rgpbDebugSave, 0, p->iptrs.cDebugDir * sizeof(PCHAR));

    // Check each possible debug type record

    for (i=0; i<p->iptrs.cDebugDir; i++) {

        // If there was debug information then copy over the
        // description block and cache in the actual debug data.

        if (p->iptrs.rgDebugDir[i] != NULL) {
            p->iptrs.rgpbDebugSave[i] =
              malloc( p->iptrs.rgDebugDir[i]->SizeOfData );
            if (p->iptrs.rgpbDebugSave[i] == NULL) {
                FatalError(ERR_NO_MEMORY);
            }
            __try {
                memcpy(p->iptrs.rgpbDebugSave[i],
                       p->iptrs.fptr +
                       p->iptrs.rgDebugDir[i]->PointerToRawData,
                       p->iptrs.rgDebugDir[i]->SizeOfData );
            } __except(EXCEPTION_EXECUTE_HANDLER ) {
                free(p->iptrs.rgpbDebugSave[i]);
                p->iptrs.rgpbDebugSave[i] = NULL;
            }
        }
    }
    return;
}


void
WriteDebugInfo(
    PPOINTERS   p,
    BOOL        fAddCV
    )
/*++

Routine Description:

    This function will go out and read in all of the debug information
    into memory -- this is required because the input and output
    files might be the same, if so then writing out informaiton may
    destory data we need at a later time.

Arguments:

    p   - Supplies a pointer to the structure describing the debug info file

Return Value:

    None.

--*/

{
    ULONG  PointerToDebugData = 0; //  Offset from the start of the file
                                   //  to the current location to write
                                   //  debug information out.
    ULONG  BaseOfDebugData = 0;
    int    i, flen;
    PIMAGE_DEBUG_DIRECTORY  pDir, pDbgDir = NULL;

    if (p->optrs.debugSection) {
        BaseOfDebugData = PointerToDebugData =
          p->optrs.debugSection->PointerToRawData;
    } else if (p->optrs.sepHdr) {
        BaseOfDebugData =  PointerToDebugData =
          sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
          p->optrs.sepHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) +
          p->optrs.sepHdr->ExportedNamesSize;
    }

    //  Step 2. If the debug information is mapped, we know this
    //          from the section headers, then we may need to write
    //          out a new debug director to point to the debug information

    if (fAddCV) {
        if (p->optrs.optHdr) {
            p->optrs.optHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].
              VirtualAddress = p->optrs.debugSection->VirtualAddress;
            p->optrs.optHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size +=
              sizeof(IMAGE_DEBUG_DIRECTORY);
        } else if (p->optrs.sepHdr) {
            p->optrs.sepHdr->DebugDirectorySize += sizeof(IMAGE_DEBUG_DIRECTORY);
        } else {
            exit(1);
        }

        if (p->optrs.sepHdr) {
            pDbgDir = (PIMAGE_DEBUG_DIRECTORY) malloc(p->optrs.cDebugDir * sizeof(IMAGE_DEBUG_DIRECTORY));
            for (i=0; i<p->optrs.cDebugDir; i++) {
                if (p->optrs.rgDebugDir[i] != NULL) {
                    pDbgDir[i] = *(p->optrs.rgDebugDir[i]);
                    p->optrs.rgDebugDir[i] = &pDbgDir[i];
                }
            }
        }
        for (i=0; i<p->optrs.cDebugDir; i++) {
            if (p->optrs.rgDebugDir[i]) {
                pDir = (PIMAGE_DEBUG_DIRECTORY) (PointerToDebugData +
                                                 p->optrs.fptr);
                *pDir = *(p->optrs.rgDebugDir[i]);
                p->optrs.rgDebugDir[i] = pDir;
                PointerToDebugData += sizeof(IMAGE_DEBUG_DIRECTORY);
            }
        }
    }

    // Step 3.  For every debug info type, write out the debug information
    //          and update any header information required

    for (i=0; i<p->optrs.cDebugDir; i++) {
        if (p->optrs.rgDebugDir[i] != NULL) {
            if (p->optrs.rgpbDebugSave[i] != NULL) {
                p->optrs.rgDebugDir[i]->PointerToRawData =
                  PointerToDebugData;
                if (p->optrs.debugSection) {
                    p->optrs.rgDebugDir[i]->AddressOfRawData =
                      p->optrs.debugSection->VirtualAddress +
                        PointerToDebugData - BaseOfDebugData;
                }
                memcpy(p->optrs.fptr + PointerToDebugData,
                       p->optrs.rgpbDebugSave[i],
                       p->optrs.rgDebugDir[i]->SizeOfData);

                if ((i == IMAGE_DEBUG_TYPE_COFF) &&
                    (p->optrs.fileHdr != NULL)) {

                    PIMAGE_COFF_SYMBOLS_HEADER  pCoffDbgInfo;
                    pCoffDbgInfo = (PIMAGE_COFF_SYMBOLS_HEADER)p->optrs.rgpbDebugSave[i];
                    p->optrs.fileHdr->PointerToSymbolTable =
                      PointerToDebugData + pCoffDbgInfo->LvaToFirstSymbol;
                }
            }
            PointerToDebugData += p->optrs.rgDebugDir[i]->SizeOfData;
        }
    }

    // Step 4.  Clean up any COFF structures if we are replacing
    //          the coff information with CV info.

    if ((p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_COFF] == NULL) &&
        (p->optrs.fileHdr != NULL)) {

        // Since there is no coff debug information -- clean out
        // both fields pointing to the debug info

        p->optrs.fileHdr->PointerToSymbolTable = 0;
        p->optrs.fileHdr->NumberOfSymbols = 0;
    }

    // Step 5.  Correct the alignments if needed.  If there is a real .debug
    //          section in the file (i.e. it is mapped) then update it.

    if (p->optrs.debugSection) {
        p->optrs.debugSection->SizeOfRawData =
          FileAlign(PointerToDebugData - BaseOfDebugData);

        // update the optional header with the new image size

        p->optrs.optHdr->SizeOfImage =
          SectionAlign(p->optrs.debugSection->VirtualAddress +
                       p->optrs.debugSection->SizeOfRawData);
        p->optrs.optHdr->SizeOfInitializedData +=
          p->optrs.debugSection->SizeOfRawData;
    }

    // calculate the new file size

    if (p->optrs.optHdr != NULL) {
        flen = FileAlign(PointerToDebugData);
    } else {
        flen = PointerToDebugData;
    }

    // finally, update the eof pointer and close the file

    UnmapViewOfFile( p->optrs.fptr );

    if (!SetFilePointer( p->optrs.hFile, flen, 0, FILE_BEGIN )) {
        FatalError( ERR_FILE_PTRS );
    }

    if (!SetEndOfFile( p->optrs.hFile )) {
        FatalError( ERR_SET_EOF );
    }

    CloseHandle( p->optrs.hFile );

    // Exit -- we are done.

    return;
}



void
MungeDebugHeadersCoffToCv(
    PPOINTERS   p,
    BOOL        fAddCV
    )

/*++

Routine Description:


Arguments:

    p        - pointer to a POINTERS structure (see symcvt.h)

Return Value:

    void

--*/

{
    if (!fAddCV) {
        CV_DIR(&p->optrs) = COFF_DIR(&p->optrs);
        COFF_DIR(&p->optrs) = 0;
    } else {
        CV_DIR(&p->optrs) = &DbgDirSpare;
        *(COFF_DIR(&p->optrs)) = *(COFF_DIR(&p->iptrs));
    };

    *CV_DIR(&p->optrs) = *(COFF_DIR(&p->iptrs));
    CV_DIR(&p->optrs)->Type = IMAGE_DEBUG_TYPE_CODEVIEW;
    CV_DIR(&p->optrs)->SizeOfData =  p->pCvStart.size;
    p->optrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_CODEVIEW] = p->pCvStart.ptr;

    return;
}



BOOL
MapOutputFile (
    PPOINTERS p,
    char *fname,
    int cb
    )

/*++

Routine Description:

    Maps the output file specified by the fname argument and saves the
    file handle & file pointer in the POINTERS structure.


Arguments:

    p        - pointer to a POINTERS structure (see symcvt.h)
    fname    - ascii string for the file name


Return Value:

    TRUE     - file mapped ok
    FALSE    - file could not be mapped

--*/

{
    BOOL    rval;
    HANDLE  hMap   = NULL;
    DWORD   oSize;

    rval = FALSE;

    p->optrs.hFile = CreateFileA( fname,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_ALWAYS,
                        0,
                        NULL );

    if (p->optrs.hFile == INVALID_HANDLE_VALUE) {
       goto exit;
    }

    oSize = p->iptrs.fsize;
    if (p->pCvStart.ptr != NULL) {
        oSize += p->pCvStart.size;
    }
    oSize += cb;
    oSize += p->iptrs.cDebugDir * sizeof(IMAGE_DEBUG_DIRECTORY);

    hMap = CreateFileMapping( p->optrs.hFile, NULL, PAGE_READWRITE,
                                0, oSize, NULL );

    if (hMap == NULL) {
       goto exit;
    }

    p->optrs.fptr = MapViewOfFile( hMap, FILE_MAP_WRITE, 0, 0, 0 );

    CloseHandle(hMap);

    if (p->optrs.fptr == NULL) {
       goto exit;
    }
    rval = TRUE;
exit:
    return rval;
}


BOOL
CalculateOutputFilePointers(
    PIMAGEPOINTERS pi,
    PIMAGEPOINTERS po
    )

/*++

Routine Description:

    This function calculates the output file pointers based on the
    input file pointers.  The same address is used but they are all
    re-based off the output file's file pointer.

Arguments:

    p        - pointer to a IMAGEPOINTERS structure (see symcvt.h)


Return Value:

    TRUE     - pointers were created
    FALSE    - pointers could not be created

--*/
{
    int i;

    // fixup the pointers relative the fptr for the output file
    po->dosHdr       = (PIMAGE_DOS_HEADER)      AdjustPtr(pi->dosHdr);
    po->ntHdr        = (PIMAGE_NT_HEADERS)      AdjustPtr(pi->ntHdr);
    po->fileHdr      = (PIMAGE_FILE_HEADER)     AdjustPtr(pi->fileHdr);
    po->optHdr       = (PIMAGE_OPTIONAL_HEADER) AdjustPtr(pi->optHdr);
    po->sectionHdrs  = (PIMAGE_SECTION_HEADER)  AdjustPtr(pi->sectionHdrs);
    po->sepHdr       = (PIMAGE_SEPARATE_DEBUG_HEADER) AdjustPtr(pi->sepHdr);
    po->debugSection = (PIMAGE_SECTION_HEADER)  AdjustPtr(pi->debugSection);
    po->AllSymbols   = (PIMAGE_SYMBOL)          AdjustPtr(pi->AllSymbols);
    po->stringTable  = (PUCHAR)                 AdjustPtr(pi->stringTable);

    // move the data from the input file to the output file
    memcpy( po->fptr, pi->fptr, pi->fsize );

    po->cDebugDir = pi->cDebugDir;
    po->rgDebugDir = malloc(po->cDebugDir * sizeof(po->rgDebugDir[0]));
    memset(po->rgDebugDir, 0, po->cDebugDir * sizeof(po->rgDebugDir[0]));

    for (i=0; i<po->cDebugDir; i++) {
        po->rgDebugDir[i] = (PIMAGE_DEBUG_DIRECTORY) AdjustPtr(pi->rgDebugDir[i]);
    }
    po->rgpbDebugSave = pi->rgpbDebugSave;

    return TRUE;
}


void
FatalError(
    int  idMsg,
    ...
    )
/*++

Routine Description:

    Prints a message string to stderr and then exits.

Arguments:

    s        - message string to be printed

Return Value:

    void

--*/

{
    va_list marker;
    char    rgchFormat[256];
    char    rgch[256];

    LoadStringA(GetModuleHandle(NULL), idMsg, rgchFormat, sizeof(rgchFormat));

    va_start(marker, idMsg);
    vsprintf(rgch, rgchFormat, marker);
    va_end(marker);

    fprintf(stderr, "%s\n", rgch);

    exit(1);
}


void
ComputeChecksum(
    char *szExeFile
    )

/*++

Routine Description:

    Computes a new checksum for the image by calling imagehlp.dll

Arguments:

    szExeFile - exe file name


Return Value:

    void

--*/

{
    DWORD              dwHeaderSum = 0;
    DWORD              dwCheckSum = 0;
    HANDLE             hFile;
    DWORD              cb;
    IMAGE_DOS_HEADER   dosHdr;
    IMAGE_NT_HEADERS   ntHdr;

    if (MapFileAndCheckSumA(szExeFile, &dwHeaderSum, &dwCheckSum) != CHECKSUM_SUCCESS) {
        FatalError( ERR_CHECKSUM_CALC );
    }

    hFile = CreateFileA( szExeFile,
                        GENERIC_READ | GENERIC_WRITE,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL
                      );

    // seek to the beginning of the file
    SetFilePointer( hFile, 0, 0, FILE_BEGIN );

    // read in the dos header
    if ((ReadFile(hFile, &dosHdr, sizeof(dosHdr), &cb, 0) == FALSE) || (cb != sizeof(dosHdr))) {
        FatalError( ERR_CHECKSUM_CALC );
    }

    // read in the pe header
    if ((dosHdr.e_magic != IMAGE_DOS_SIGNATURE) ||
        (SetFilePointer(hFile, dosHdr.e_lfanew, 0, FILE_BEGIN) == -1L)) {
        FatalError( ERR_CHECKSUM_CALC );
    }

    // read in the nt header
    if ((!ReadFile(hFile, &ntHdr, sizeof(ntHdr), &cb, 0)) || (cb != sizeof(ntHdr))) {
        FatalError( ERR_CHECKSUM_CALC );
    }

    if (SetFilePointer(hFile, dosHdr.e_lfanew, 0, FILE_BEGIN) == -1L) {
        FatalError( ERR_CHECKSUM_CALC );
    }

    ntHdr.OptionalHeader.CheckSum = dwCheckSum;

    if (!WriteFile(hFile, &ntHdr, sizeof(ntHdr), &cb, NULL)) {
        FatalError( ERR_CHECKSUM_CALC );
    }

    CloseHandle(hFile);
    return;
}


void
MungeExeName(
    PPOINTERS   p,
    char *      szExeName
    )
/*++

Routine Description:

    description-of-function.

Arguments:

    argument-name - Supplies | Returns description of argument.
    .
    .

Return Value:

    None.

--*/

{
    PIMAGE_DEBUG_MISC   pMiscIn;
    PIMAGE_DEBUG_MISC   pMiscOut;
    int                 cb;
    int                 i;

    for (i=0; i<p->iptrs.cDebugDir; i++) {
        if (p->optrs.rgDebugDir[i] != 0) {
            *(p->optrs.rgDebugDir[i]) = *(p->iptrs.rgDebugDir[i]);
        }
    }

    pMiscIn = (PIMAGE_DEBUG_MISC)
      p->iptrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_MISC];

    if (p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC] == NULL) {
        p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC] = &DbgDirSpare;
        memset(&DbgDirSpare, 0, sizeof(DbgDirSpare));
    }

    pMiscOut = (PIMAGE_DEBUG_MISC)
      p->optrs.rgpbDebugSave[IMAGE_DEBUG_TYPE_MISC] =
      malloc(p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData +
             strlen(szExeName));
    cb = p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData;

    while ( cb > 0 ) {
        if (pMiscIn->DataType == IMAGE_DEBUG_MISC_EXENAME) {
            pMiscOut->DataType = IMAGE_DEBUG_MISC_EXENAME;
            pMiscOut->Length = (sizeof(IMAGE_DEBUG_MISC) +
                                strlen(szExeName) + 3) & ~3;
            pMiscOut->Unicode = FALSE;
            strcpy(&pMiscOut->Data[0], szExeName);
            szExeName = NULL;
        } else {
            memcpy(pMiscOut, pMiscIn, pMiscIn->Length);
        }

        p->optrs.rgDebugDir[IMAGE_DEBUG_TYPE_MISC]->SizeOfData +=
          (pMiscOut->Length - pMiscIn->Length);

        cb -= pMiscIn->Length;
        pMiscIn = (PIMAGE_DEBUG_MISC) (((char *) pMiscIn) + pMiscIn->Length);
        pMiscOut = (PIMAGE_DEBUG_MISC) (((char *) pMiscOut) + pMiscOut->Length);
    }

    if (szExeName) {
        pMiscOut->DataType = IMAGE_DEBUG_MISC_EXENAME;
        pMiscOut->Length = (sizeof(IMAGE_DEBUG_MISC) +
                            strlen(szExeName) + 3) & ~3;
        pMiscOut->Unicode = FALSE;
        strcpy(&pMiscOut->Data[0], szExeName);
    }

    return;
}


/***    DoCoffToCv
 *
 *
 */

void DoCoffToCv(
    char * szInput,
    char * szOutput,
    BOOL fAddCV
    )
{
    POINTERS    p;

    // Do default checking

    if (szOutput == NULL) {
        szOutput = szInput;
    }

    // Open the input file name and setup the pointers into the file

    if (!MapInputFile( &p, NULL, szInput )) {
        FatalError( ERR_OPEN_INPUT_FILE, szInput );
    }

    // Now, if we thing we are playing with PE exes then we need
    // to setup the pointers into the map file

    if (!CalculateNtImagePointers( &p.iptrs )) {
        FatalError( ERR_INVALID_PE, szInput );
    }

    // We are about to try and do the coff to cv symbol conversion.
    //
    // Verify that the operation is legal.
    //
    // 1.  We need to have coff debug information to start with
    // 2.  If the debug info is not mapped then we must not
    //     be trying to add CodeView info.

    if ((p.iptrs.AllSymbols == NULL) || (COFF_DIR(&p.iptrs) == NULL)) {
        FatalError( ERR_NO_COFF );
    }

    if (fAddCV && (p.iptrs.debugSection == 0) && (p.iptrs.sepHdr == NULL)) {
        FatalError( ERR_NOT_MAPPED );
    }

    // Now go out an preform the acutal conversion.

    if (!ConvertCoffToCv( &p )) {
        FatalError( ERR_COFF_TO_CV );
    }

    // Read in any additional debug information in the file

    ReadDebugInfo(&p);

    // Open the output file and adjust the pointers so that we are ok.

    if (!MapOutputFile( &p, szOutput, 0 )) {
        FatalError( ERR_MAP_FILE, szOutput );
    }

    CalculateOutputFilePointers( &p.iptrs, &p.optrs );

    // Munge the various debug information structures to preform the correct
    // operations

    MungeDebugHeadersCoffToCv( &p, fAddCV );

    // Free our handles on the input file

    UnMapInputFile(&p);

    // Write out the debug information to the end of the exe

    WriteDebugInfo( &p, fAddCV );

    // and finally compute the checksum

    if (p.iptrs.fileHdr != NULL) {
        ComputeChecksum( szOutput );
    }

    return;
}

/***    DoSymToCv
 *
 */

void
DoSymToCv(
    char * szInput,
    char * szOutput,
    char * szDbg,
    char * szSym
    )
{
    POINTERS    p;
    HANDLE      hFile;
    DWORD       cb;
    OFSTRUCT    ofs;

    // Open the input file name and setup the pointers into the file

    if (!MapInputFile( &p, NULL, szSym )) {
        FatalError(ERR_OPEN_INPUT_FILE, szSym);
    }

    // Now preform the desired operation

    if ((szOutput == NULL) && (szDbg == NULL)) {
        szOutput = szInput;
    }

    ConvertSymToCv( &p );

    if (szOutput) {
        if (szOutput != szInput) {
            if (OpenFile(szInput, &ofs, OF_EXIST) == 0) {
                FatalError(ERR_OPEN_INPUT_FILE, szInput);
            }
            if (CopyFileA(szInput, szOutput, FALSE) == 0) {
                FatalError(ERR_OPEN_WRITE_FILE, szOutput);
            }
        }
        hFile = CreateFileA(szOutput, GENERIC_WRITE, 0, NULL,
                           CREATE_ALWAYS,
                           FILE_ATTRIBUTE_NORMAL,  NULL);
        if (hFile == INVALID_HANDLE_VALUE) {
            FatalError(ERR_OPEN_WRITE_FILE, szOutput);
        }
        SetFilePointer(hFile, 0, 0, FILE_END);
    } else if (szDbg) {
        hFile = CreateFileA(szDbg, GENERIC_WRITE, 0, NULL,
                               OPEN_ALWAYS,
                               FILE_ATTRIBUTE_NORMAL,  NULL);
        if (hFile == INVALID_HANDLE_VALUE) {
            FatalError(ERR_OPEN_WRITE_FILE, szDbg);
        }
    }

    WriteFile(hFile, p.pCvStart.ptr, p.pCvStart.size, &cb, NULL);
    CloseHandle(hFile);

    return;
}


void
DoNameChange(
    char * szInput,
    char * szOutput,
    char * szNewName
    )
{
    POINTERS    p;

    // Open the input file name and setup the pointers into the file

    if (!MapInputFile( &p, NULL, szInput )) {
        FatalError(ERR_OPEN_INPUT_FILE, szInput);
    }

    // Now, if we thing we are playing with PE exes then we need
    // to setup the pointers into the map file

    if (!CalculateNtImagePointers( &p.iptrs )) {
        FatalError(ERR_INVALID_PE, szInput);
    }

    //  Now preform the desired operation

    if (szOutput == NULL) {
        szOutput = szInput;
    }

    if (szNewName == NULL) {
        szNewName = szOutput;
    }

    if (p.iptrs.sepHdr != NULL) {
        FatalError(ERR_EDIT_DBG_FILE);
    }

    // Read in all of the debug information

    ReadDebugInfo(&p);

    // Open the output file and adjust the pointers.

    if (!MapOutputFile(&p, szOutput,
                       sizeof(szNewName) * 2 + sizeof(IMAGE_DEBUG_MISC))) {
        FatalError(ERR_MAP_FILE, szOutput);
    }

    CalculateOutputFilePointers(&p.iptrs, &p.optrs);

    // Munge the name of the file

    MungeExeName(&p, szNewName);

    // Close the input file

    UnMapInputFile(&p);

    // Write out the debug information to the end of the exe

    WriteDebugInfo(&p, FALSE);

    // and finally compute the checksum

    if (p.iptrs.fileHdr != NULL) {
        ComputeChecksum( szOutput );
    }

    return;
}


void
DoStrip(
    char * szInput,
    char * szOutput
    )
{
    char OutputFile[_MAX_PATH];

    // Make sure we only have the path to the output file (it will always be
    //  named filename.DBG)

    if (szOutput != NULL) {
        CopyFileA(szInput, szOutput, FALSE);
    }

    SplitSymbols(szOutput, NULL, OutputFile, SPLITSYM_EXTRACT_ALL);

    // Always delete the output file.

    DeleteFileA(OutputFile);

    return;
}


void
DoExtract(
    char * szInput,
    char * szOutput,
    char * szDbgFile
    )
{
    char OutputFile[_MAX_PATH];
    char szExt[_MAX_EXT];
    char szFileName[_MAX_FNAME];

    if (szOutput != NULL) {
        CopyFileA(szInput, szOutput, FALSE);
        szInput = _strdup(szOutput);
        _splitpath(szOutput, NULL, NULL, szFileName, szExt);
        *(szOutput + strlen(szOutput) - strlen(szFileName) - strlen(szExt)) = '\0';
    }

    SplitSymbols(szInput, szOutput, OutputFile, 0);

    CopyFileA(szDbgFile, OutputFile, TRUE);

    if (szOutput) {
        free(szInput);
    }

    return;
}