/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    icontool.c

Abstract:

    Extracts icons in a variety of ways to test the icon extraction code.

Author:

    Jim Schmidt (jimschm)   22-Apr-1998

Revision History:

    <alias> <date> <comments>

--*/

#include "pch.h"


INT g_ErrorLevel;

VOID
pLaunchCompareDlg (
    IN      PCTSTR FileName1,
    IN      PCTSTR FileName2
    );

UINT
pRemoveIcons (
    IN      PICON_EXTRACT_CONTEXT Context,
    IN      UINT Start,
    IN      UINT End
    );

UINT
pCopyIconRange (
    IN      PICON_EXTRACT_CONTEXT Context,
    IN      PCTSTR IconFile,
    IN      UINT Start,
    IN      UINT End
    );

//
// This routine is in migutil\icons.c
//

BOOL
pOpenIconImageA (
    IN OUT  PICON_EXTRACT_CONTEXTA Context,
    IN      PCSTR FileToOpen,
    OUT     PBOOL IsIco,                        OPTIONAL
    OUT     PBOOL Is16Bit                       OPTIONAL
    );

BOOL
pOpenIconImageW (
    IN OUT  PICON_EXTRACT_CONTEXTW Context,
    IN      PCWSTR FileToOpen,
    OUT     PBOOL IsIco,                        OPTIONAL
    OUT     PBOOL Is16Bit                       OPTIONAL
    );

#ifdef UNICODE

#define pOpenIconImage pOpenIconImageW

#else

#define pOpenIconImage pOpenIconImageA

#endif

typedef enum {
    NONE,
    CREATE_ICO,
    EXTRACT,
    EXTRACT_ALL,
    IMPLANT,
    EXTRACT_ONE,
    LIST,
    COMPARE,
    MAKE_UNIQUE,
    KILL
} MODE;



VOID
HelpAndExit (
    VOID
    )
{
    printf ("Command line syntax:\n\n"
            "icontool -a [-r] <pe-file>\n"
            "icontool -c <pe-file-1> <pe-file-2>\n"
            "icontool -d [i:<index>] <in-file> <migicons.dat>\n"
            "icontool -e[:range] <in-file> <migicons.dat>\n"
            "icontool [-i:<index>] <in-file> <ico-file>\n"
            "icontool -k <pe-file> <index range>\n"
            "icontool -l[i] [-q] [-n] [-p:<path>] <in-file> [<in-file>] [...]\n"
            "icontool -u <pe-file>\n"
            "icontool -x <migicons.dat> <pe-file>\n"
            "\n"
            "<in-file>      Specifies the file to extract an icon from (.ICO, NE or PE)\n"
            "<ico-file>     Specifies the file to save the icon to (.ICO only)\n"
            "<pe-file>      Specifies the file to save the icon(s) to (PE only)\n"
            "<migicons.dat> Specifies the path to migicons.dat (note: same as what\n"
            "               is generated by WINNT32)\n"
            "-a             Scans all files for icons\n"
            "-c             Compare pe-file-1 to pe-file-2 visually\n"
            "-d             Extracts icon from a file into dat file\n"
            "-e             Extracts all icons from <in-file>, or a range of icons. The\n"
            "               range must be simple (as in these three examples: 152 or 1-4\n"
            "               or 10-)\n"
            "-i             Specifies the index or string ID of the icon to extract, default\n"
            "               is 0\n"
            "-k             Kill icon where <index range> is in the form of 1,4-5,10\n"
            "-l             Lists icon resource names\n"
            "-li            Produces output for [Compatible Icon Indexes]\n"
            "-n             No header (used with -li only)\n"
            "-p             Specifies a path to compare the same-named <in-file>, used to\n"
            "               produce [Compatible Icon Index] that have binaries with icons\n"
            "               in common.\n"
            "-q             Quiet (no error output)\n"
            "-r             Enables recursion\n"
            "-u             Makes all icons unique, removes duplicates\n"
            "-x             Extracts icons from a dat file into a pe file\n"
            );

    exit(0);
}

BOOL
pRemoveDuplicateIcons (
    IN      PCTSTR SourcePeFile,
    IN      PCTSTR DestPeFile
    );

BOOL
pGetMinAndMax (
    IN      PICON_EXTRACT_CONTEXT Context,
    OUT     PUINT Min,
    OUT     PUINT Max
    );

HANDLE g_hHeap;
HINSTANCE g_hInst;


BOOL
pIsErrorOk (
    DWORD Error
    )
{
    if (Error != ERROR_SUCCESS &&
        Error != ERROR_FILE_INVALID &&
        Error != ERROR_ACCESS_DENIED &&
        Error != ERROR_BAD_FORMAT &&
        Error != ERROR_RESOURCE_TYPE_NOT_FOUND &&
        Error != ERROR_SHARING_VIOLATION &&
        Error != ERROR_NO_MORE_FILES &&
        Error != ERROR_RESOURCE_DATA_NOT_FOUND &&
        Error != ERROR_NOACCESS &&
        Error != ERROR_INVALID_EXE_SIGNATURE &&
        Error != ERROR_CANT_ACCESS_FILE
        ) {
        return TRUE;
    }

    return FALSE;
}


BOOL
WINAPI
MigUtil_Entry (
    HINSTANCE hinstDLL,
    DWORD fdwReason,
    LPVOID lpvReserved
    );

VOID
Init (
    VOID
    )
{
    MigUtil_Entry (g_hInst, DLL_PROCESS_ATTACH, NULL);
}

VOID
Terminate (
    VOID
    )
{
    MigUtil_Entry (g_hInst, DLL_PROCESS_DETACH, NULL);
}


INT
__cdecl
_tmain (
    INT argc,
    TCHAR *argv[]
    )
{
    PCTSTR IconFile = NULL;
    PCTSTR DestFile = NULL;
    PCTSTR IconId = NULL;
    INT IconIndex = 0;
    INT i;
    GROWBUFFER Buf = GROWBUF_INIT;
    GROWBUFFER Buf2 = GROWBUF_INIT;
    MODE Mode = NONE;
    BOOL RecurseMode = FALSE;
    INT Id = 1;
    ICON_EXTRACT_CONTEXT Context;
    WORD Icon;
    DWORD Error;
    UINT Count;
    BOOL UseIconIndex = TRUE;
    TCHAR IconIndexStr[128];
    MULTISZ_ENUM MultiSz;
    PCTSTR IconList;
    PCTSTR IconList2;
    BOOL InfOutput = FALSE;
    INT ResourceId;
    UINT Column;
    UINT Indent;
    TCHAR Buffer[2048];
    MULTISZ_ENUM LookAhead;
    INT Range;
    PCTSTR LastValidString;
    BOOL NeedsHeader = TRUE;
    GROWBUFFER FileList = GROWBUF_INIT;
    MULTISZ_ENUM FileListEnum;
    FILE_ENUM FileEnum;
    UINT Files = 0;
    TCHAR RootPath[MAX_TCHAR_PATH];
    PCTSTR comparePath = NULL;
    PCTSTR FileName;
    PCTSTR RootPathPtr;
    BOOL Quiet = FALSE;
    PCTSTR NextNum;
    UINT Start = 0;
    UINT End = 0xFFFF;
    UINT Min;
    UINT Max;
    TCHAR workPath[MAX_TCHAR_PATH];
    PCTSTR fileSpec;
    MULTISZ_ENUM compareEnum;
    BOOL match;
    BOOL noHeader = FALSE;

    g_hHeap = GetProcessHeap();
    g_hInst = GetModuleHandle (NULL);

    Init();

    for (i = 1 ; i < argc ; i++) {

        if (argv[i][0] == TEXT('-') || argv[i][0] == TEXT('/')) {
            switch (_totlower (argv[i][1])) {

            case TEXT('x'):
                if (Mode != NONE) {
                    HelpAndExit();
                }

                Mode = IMPLANT;
                break;

            case TEXT('d'):
                if (Mode != NONE) {
                    HelpAndExit();
                }

                Mode = EXTRACT_ONE;
                break;

            case TEXT('l'):
                if (Mode != NONE) {
                    HelpAndExit();
                }

                if (_totlower (argv[i][2]) == TEXT('i')) {
                    InfOutput = TRUE;
                } else if (argv[i][2]) {
                    HelpAndExit();
                }

                Mode = LIST;
                break;

            case TEXT('n'):
                if (noHeader) {
                    HelpAndExit();
                }

                noHeader = TRUE;
                break;

            case TEXT('c'):
                if (Mode != NONE) {
                    HelpAndExit();
                }

                Mode = COMPARE;
                break;

            case TEXT('u'):
                if (Mode != NONE) {
                    HelpAndExit();
                }

                Mode = MAKE_UNIQUE;
                break;

            case TEXT('p'):
                if (comparePath) {
                    HelpAndExit();
                }

                if (argv[i][2] == ':') {
                    comparePath = &argv[i][3];
                } else if (i + 1 < argc) {
                    i++;
                    comparePath = argv[i];
                } else {
                    HelpAndExit();
                }
                break;

            case TEXT('i'):
                if (argv[i][2] == ':') {
                    IconId = &argv[i][3];
                } else if (i + 1 < argc) {
                    i++;
                    IconId= argv[i];
                } else {
                    HelpAndExit();
                }

                IconIndex = _ttoi (IconId);
                while (_istspace ((TCHAR)_tcsnextc (IconId))) {
                    IconId = _tcsinc (IconId);
                }

                if (*IconId) {
                    if (!IconIndex && iscsymf(_tcsnextc (IconId))) {
                        UseIconIndex = FALSE;
                    }
                }

                break;

            case TEXT('k'):
                if (Mode != NONE) {
                    HelpAndExit();
                }

                Mode = KILL;
                break;

            case TEXT('a'):
                if (Mode != NONE) {
                    HelpAndExit();
                }

                Mode = EXTRACT_ALL;
                break;

            case TEXT('q'):
                if (Quiet) {
                    HelpAndExit();
                }

                Quiet = TRUE;
                break;

            case TEXT('e'):
                if (Mode != NONE) {
                    HelpAndExit();
                }

                if (argv[i][2] == ':') {
                    NextNum = &argv[i][3];

                    if (_tcsnextc (NextNum) != TEXT('-')) {
                        Start = _tcstoul (NextNum, (PTSTR *) (&NextNum), 10);
                    }

                    if (_tcsnextc (NextNum) == TEXT('-')) {
                        NextNum = _tcsinc (NextNum);

                        if (*NextNum) {
                            End = _tcstoul (NextNum, (PTSTR *) (&NextNum), 10);
                        }
                    } else {
                        End = Start;
                    }

                    if (*NextNum) {
                        HelpAndExit();
                    }
                }

                Mode = EXTRACT;
                break;

            case TEXT('r'):
                if (RecurseMode) {
                    HelpAndExit();
                }

                RecurseMode = TRUE;
                break;

            default:
                HelpAndExit();
            }
        }

        else {

            Files++;
            MultiSzAppend (&FileList, argv[i]);

            if (IconFile) {
                DestFile = argv[i];
            }

            else {
                IconFile = argv[i];
            }

        }
    }

    MultiSzAppend (&FileList, S_EMPTY);

    //
    // Enforce syntax
    //

    if (Quiet && Mode != LIST) {
        HelpAndExit();
    }

    if (Mode == EXTRACT_ALL && DestFile) {
        HelpAndExit();
    }

    if (Mode == EXTRACT_ALL || Mode == LIST) {
        DestFile = IconFile;
    }

    if (Mode == MAKE_UNIQUE) {
        if (!DestFile) {
            DestFile = IconFile;
        }
    }

    if (!DestFile) {
        HelpAndExit();
    }

    if (RecurseMode && Mode != EXTRACT_ALL) {
        HelpAndExit();
    }

    if (Files > 2 && Mode != LIST) {
        HelpAndExit();
    }

    if ((comparePath || noHeader) && Mode != LIST) {
        HelpAndExit();
    }

    if (UseIconIndex) {
        IconId = (PCTSTR) (WORD) IconIndex;
        wsprintf (IconIndexStr, TEXT("%i"), IconIndex);
    } else {
        StringCopy (IconIndexStr, IconId);
    }

    if (Mode == KILL) {

        fprintf (stderr, "Beginning icon processing\n");

        if (!BeginIconExtraction (&Context, IconFile)) {
            fprintf (stderr, "Can't begin icon extraction\n");
            return 0;
        }

        //
        // Get the min and max IDs
        //

        if (!pGetMinAndMax (&Context, &Min, &Max)) {
            fprintf (stderr, "Can't kill icons without min/max info\n");
            return 0;
        }

        //
        // Evaluate the range and remove the icons
        //

        NextNum = DestFile;
        Count = 0;

        fprintf (stderr, "Removing icons\n");

        for (;;) {
            NextNum = SkipSpace (NextNum);

            if (!(*NextNum)) {
                break;
            }

            if (_tcsnextc (NextNum) == TEXT('-')) {
                Start = Min;
            } else {
                Start = _tcstoul (NextNum, (PTSTR *) (&NextNum), 10);
                NextNum = SkipSpace (NextNum);
            }

            if (_tcsnextc (NextNum) == TEXT('-')) {
                NextNum = SkipSpace (_tcsinc (NextNum));

                if (_tcsnextc (NextNum) == TEXT(',') || !(*NextNum)) {
                    End = Max;
                } else {
                    End = _tcstoul (NextNum, (PTSTR *) (&NextNum), 10);
                    NextNum = SkipSpace (NextNum);
                }
            } else {
                End = Start;
            }

            Count += pRemoveIcons (&Context, Start, End);

            if (_tcsnextc (NextNum) == TEXT(',')) {
                NextNum = _tcsinc (NextNum);
            } else {
                break;
            }
        }

        fprintf (stderr, "Saving\n");

        if (EndIconExtraction (&Context)) {
            printf ("Icons removed: %u\n", Count);
        }

        return 0;
    }

    else if (Mode == IMPLANT) {
        //
        // Use IconFile as a source to generate a PE file of icons
        //

        if (!BeginIconExtraction (&Context, DestFile)) {
            fprintf (stderr, "Can't begin icon extraction\n");
            return 0;
        }

        if (!OpenIconImageFile (&Context, IconFile, FALSE)) {
            _ftprintf (stderr, TEXT("Can't open %s\n"), IconFile);
            EndIconExtraction (&Context);
            return 0;
        }

        Count = 0;
        while (CopyIcon (&Context, NULL, NULL, 0)) {
            Count++;
        }

        if (EndIconExtraction (&Context)) {
            printf ("Icons extracted: %u\n", Count);
        }
    }

    else if (Mode == EXTRACT || Mode == EXTRACT_ONE) {
        //
        // Use IconFile as a source to generate a PE file of icons
        //

        if (!BeginIconExtraction (&Context, NULL)) {
            fprintf (stderr, "Can't begin icon extraction\n");
            return 0;
        }

        if (!OpenIconImageFile (&Context, DestFile, TRUE)) {
            _ftprintf (stderr, TEXT("Can't create %s\n"), DestFile);
            EndIconExtraction (&Context);
            return 0;
        }

        if (Mode == EXTRACT) {

            //
            // If no range is specified, use the CopyAllIcons api
            //

            if (Start == 0 && End == 0xFFFF) {
                if (!CopyAllIcons (&Context, IconFile)) {
                    _ftprintf (stderr, TEXT("Can't copy all icons from %s, error %u\n"), IconFile, GetLastError());
                } else {
                    _tprintf (TEXT("Extracted all icons from %s\n"), IconFile);
                }
            }

            //
            // If a range is specified, get the names and copy them
            // if they are in the range.
            //

            else {
                Count = pCopyIconRange (&Context, IconFile, Start, End);
                _tprintf (TEXT("Icons extracted: %u\n"), Count);
            }


        } else {
            if (!CopyIcon (&Context, IconFile, NULL, IconIndex)) {
                _ftprintf (stderr, TEXT("Can't copy %s [%i], error %u\n"), IconFile, IconIndex, GetLastError());
            } else {
                _tprintf (TEXT("Extracted %s [%i]\n"), IconFile, IconIndex);
            }
        }

        EndIconExtraction (&Context);
    }

    else if (Mode == MAKE_UNIQUE) {

        pRemoveDuplicateIcons (IconFile, DestFile);

    }

    else if (Mode == LIST) {

        EnumFirstMultiSz (&FileListEnum, (PCTSTR) FileList.Buf);

        do {
            //
            // Separate the path and file pattern
            //

            FileName = GetFileNameFromPath (FileListEnum.CurrentString);

            if (FileName == FileListEnum.CurrentString) {
                RootPathPtr = TEXT(".");
            } else {
                _tcssafecpyab (
                    RootPath,
                    FileListEnum.CurrentString,
                    FileName,
                    sizeof (RootPath) / sizeof (RootPath[0])
                    );
                RootPathPtr = RootPath;

                if (CharCount (RootPath) > 3) {
                    RemoveWackAtEnd (RootPath);
                }
            }

            //
            // Process all files specified
            //

            if (EnumFirstFile (&FileEnum, RootPathPtr, FileName)) {
                do {

                    if (FileEnum.Directory) {
                        continue;
                    }

                    //
                    // Process IconFile
                    //

                    IconFile = FileEnum.FullPath;
                    IconList = ExtractIconNamesFromFile (IconFile, &Buf);

                    Count = 0;

                    if (comparePath) {
                        //
                        // Prepare a list of resources in a binary located
                        // in an alternate path
                        //

                        fileSpec = GetFileNameFromPath (IconFile);
                        StringCopy (workPath, comparePath);
                        StringCopy (AppendWack (workPath), fileSpec);

                        IconList2 = ExtractIconNamesFromFile (workPath, &Buf2);
                    } else {
                        IconList2 = IconList;
                    }

                    if (IconList && IconList2) {

                        if (!InfOutput) {

                            //
                            // Simple output
                            //

                            if (EnumFirstMultiSz (&MultiSz, IconList)) {

                                do {
                                    if (IconList != IconList2) {
                                        match = FALSE;
                                        if (EnumFirstMultiSz (&compareEnum, IconList2)) {
                                            do {

                                                if (StringMatch (MultiSz.CurrentString, compareEnum.CurrentString)) {
                                                    match = TRUE;
                                                    break;
                                                }

                                            } while (EnumNextMultiSz (&compareEnum));
                                        }

                                        if (!match) {
                                            continue;
                                        }
                                    }

                                    if (!Count) {
                                        if (IconList != IconList2) {
                                            _tprintf (TEXT("Icon resources in both %s and %s:\n\n"), IconFile, workPath);
                                        } else {
                                            _tprintf (TEXT("Icon resources in %s:\n\n"), IconFile);
                                        }
                                    }

                                    _tprintf (TEXT("  %s\n"), MultiSz.CurrentString);
                                    Count++;
                                } while (EnumNextMultiSz (&MultiSz));

                                if (Count) {
                                    _tprintf (TEXT("\n"));
                                }
                            }

                            if (IconList != IconList2) {
                                if (EnumFirstMultiSz (&MultiSz, IconList)) {

                                    Count = 0;

                                    do {
                                        match = FALSE;
                                        if (EnumFirstMultiSz (&compareEnum, IconList2)) {
                                            do {

                                                if (StringMatch (MultiSz.CurrentString, compareEnum.CurrentString)) {
                                                    match = TRUE;
                                                    break;
                                                }

                                            } while (EnumNextMultiSz (&compareEnum));
                                        }

                                        if (match) {
                                            continue;
                                        }

                                        if (!Count) {
                                            _tprintf (TEXT("Icon resources in only in %s:\n\n"), IconFile);
                                        }

                                        _tprintf (TEXT("  %s\n"), MultiSz.CurrentString);
                                        Count++;
                                    } while (EnumNextMultiSz (&MultiSz));

                                    if (Count) {
                                        _tprintf (TEXT("\n"));
                                    }
                                }

                                if (EnumFirstMultiSz (&MultiSz, IconList2)) {

                                    Count = 0;

                                    do {
                                        match = FALSE;
                                        if (EnumFirstMultiSz (&compareEnum, IconList)) {
                                            do {

                                                if (StringMatch (MultiSz.CurrentString, compareEnum.CurrentString)) {
                                                    match = TRUE;
                                                    break;
                                                }

                                            } while (EnumNextMultiSz (&compareEnum));
                                        }

                                        if (match) {
                                            continue;
                                        }

                                        if (!Count) {
                                            _tprintf (TEXT("Icon resources in only in %s:\n\n"), workPath);
                                        }

                                        _tprintf (TEXT("  %s\n"), MultiSz.CurrentString);
                                        Count++;
                                    } while (EnumNextMultiSz (&MultiSz));

                                    if (Count) {
                                        _tprintf (TEXT("\n"));
                                    }
                                }

                                Count = 1;
                            }

                        } else {

                            //
                            // INF output
                            //

                            //
                            // Count the numeric resources
                            //

                            if (EnumFirstMultiSz (&MultiSz, IconList)) {
                                do {
                                    if (IconList != IconList2) {
                                        match = FALSE;
                                        if (EnumFirstMultiSz (&compareEnum, IconList2)) {
                                            do {

                                                if (StringMatch (MultiSz.CurrentString, compareEnum.CurrentString)) {
                                                    match = TRUE;
                                                    break;
                                                }

                                            } while (EnumNextMultiSz (&compareEnum));
                                        }

                                        if (!match) {
                                            continue;
                                        }
                                    }

                                    if (_tcsnextc (MultiSz.CurrentString) == TEXT('#')) {
                                        Count++;
                                    }
                                } while (EnumNextMultiSz (&MultiSz));
                            }

                            //
                            // If at least one numeric resource, print it
                            //

                            if (Count) {

                                if (NeedsHeader) {
                                    if (!noHeader) {
                                        _tprintf (TEXT("[%s]\n"), S_KNOWN_GOOD_ICON_MODULES);
                                    }

                                    NeedsHeader = FALSE;
                                }

                                wsprintf (Buffer, TEXT("%s="), GetFileNameFromPath (IconFile));
                                Indent = TcharCount (Buffer);

                                wsprintf (GetEndOfString (Buffer), TEXT("%u"), Count);
                                Column = TcharCount (Buffer);

                                _tprintf (TEXT("%s"), Buffer);

                                if (EnumFirstMultiSz (&MultiSz, IconList)) {
                                    do {
                                        if (IconList != IconList2) {
                                            match = FALSE;
                                            if (EnumFirstMultiSz (&compareEnum, IconList2)) {
                                                do {

                                                    if (StringMatch (MultiSz.CurrentString, compareEnum.CurrentString)) {
                                                        match = TRUE;
                                                        break;
                                                    }

                                                } while (EnumNextMultiSz (&compareEnum));
                                            }

                                            if (!match) {
                                                continue;
                                            }
                                        }

                                        if (_tcsnextc (MultiSz.CurrentString) == TEXT('#')) {

                                            ResourceId = _ttoi (_tcsinc (MultiSz.CurrentString));

                                            //
                                            // Determine if this is a range
                                            //

                                            Range = ResourceId;

                                            EnumFirstMultiSz (&LookAhead, MultiSz.CurrentString);
                                            LastValidString = MultiSz.CurrentString;

                                            while (EnumNextMultiSz (&LookAhead)) {

                                                if (_tcsnextc (LookAhead.CurrentString) != TEXT('#')) {
                                                    continue;
                                                }

                                                i = _ttoi (_tcsinc (LookAhead.CurrentString));
                                                if (Range + 1 == i) {
                                                    Range = i;
                                                    LastValidString = LookAhead.CurrentString;
                                                } else {
                                                    break;
                                                }
                                            }

                                            //
                                            // Add separator
                                            //

                                            _tprintf (TEXT(","));
                                            Column++;

                                            if (Range > ResourceId + 1) {
                                                wsprintf (Buffer, TEXT("%u-%u"), ResourceId, Range);
                                                MultiSz.CurrentString = LastValidString;
                                            } else {
                                                wsprintf (Buffer, TEXT("%u"), ResourceId);
                                            }

                                            if (TcharCount (Buffer) + Column > 78) {
                                                _tprintf (TEXT("\\\n"));

                                                for (Column = 0 ; Column < Indent ; Column++) {
                                                    _tprintf (TEXT(" "));
                                                }
                                            }

                                            Column += TcharCount (Buffer);
                                            _tprintf (TEXT("%s"), Buffer);
                                        }

                                    } while (EnumNextMultiSz (&MultiSz));
                                }

                                _tprintf (TEXT("\n"));
                            }
                        }
                    }

                    if (!Count && !Quiet) {
                        _ftprintf (stderr, TEXT("No icons in %s\n"), IconFile);
                    }

                    if (Count) {
                        g_ErrorLevel = 1;
                    }

                } while (EnumNextFile (&FileEnum));
            }

        } while (EnumNextMultiSz (&FileListEnum));
    }

    else if (Mode == EXTRACT_ONE) {
        //
        // Extract one icon
        //

        if (ExtractIconImageFromFile (IconFile, IconId, &Buf)) {
            if (WriteIconImageArrayToIcoFile (DestFile, &Buf)) {
                _ftprintf (stderr, TEXT("Icon %s from %s written successfully to %s.\n"), IconIndexStr, IconFile, DestFile);
            } else {
                _ftprintf (stderr, TEXT("Can't write icon to %s (error %u)\n"), DestFile, GetLastError());
            }
        } else {
            _ftprintf (stderr, TEXT("Can't extract icon %s from %s (error %u)\n"), IconIndexStr, IconFile, GetLastError());
        }
    }

    else if (Mode == EXTRACT_ALL) {
        //
        // Extract default icon of every file
        //

        if (!BeginIconExtraction (&Context, DestFile)) {
            fprintf (stderr, "Can't begin icon extraction\n");
            return 0;
        }

        if (RecurseMode) {
            TREE_ENUM e;

            if (EnumFirstFileInTree (&e, TEXT("."), NULL, FALSE)) {
                do {
                    if (StringIMatch (e.FullPath, DestFile)) {
                        continue;
                    }

                    Icon = Context.IconId;

                    if (!CopyAllIcons (&Context, e.FullPath)) {
                        Error = GetLastError();

                        if (pIsErrorOk (Error)) {
                            _ftprintf (stderr, TEXT("Can't copy icons from %s, error %u\n"), e.FullPath, Error);
                            break;
                        }
                    } else {
                        _tprintf (TEXT("%u: %s\n"), Icon, e.FullPath);
                    }
                } while (EnumNextFileInTree (&e));
            }

        } else {
            WIN32_FIND_DATA fd;
            HANDLE FindHandle;
            TCHAR FullPath[MAX_TCHAR_PATH];
            PTSTR p;

            GetCurrentDirectory (MAX_TCHAR_PATH, FullPath);
            p = AppendWack (FullPath);
            StringCopy (p, TEXT("*.*"));

            FindHandle = FindFirstFile (FullPath, &fd);
            if (FindHandle != INVALID_HANDLE_VALUE) {
                do {
                    if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                        continue;
                    }

                    StringCopy (p, fd.cFileName);
                    if (StringIMatch (FullPath, DestFile)) {
                        continue;
                    }

                    Icon = Context.IconId;

                    if (!CopyAllIcons (&Context, fd.cFileName)) {
                        Error = GetLastError();

                        if (pIsErrorOk (Error)) {
                            _ftprintf (stderr, TEXT("Can't copy icons from %s, error %u\n"), fd.cFileName, Error);
                            break;
                        }
                    } else {
                        _tprintf (TEXT("%u: %s\n"), Icon, fd.cFileName);
                    }

                } while (FindNextFile (FindHandle, &fd));

                FindClose (FindHandle);
            }
        }

        if (EndIconExtraction (&Context)) {
            printf ("Icons extracted.\n");
        }
    }

    else if (Mode == COMPARE) {
        pLaunchCompareDlg (IconFile, DestFile);
    }

    FreeGrowBuffer (&Buf);
    FreeGrowBuffer (&Buf2);
    FreeGrowBuffer (&FileList);

    Terminate();

    return g_ErrorLevel;
}


BOOL
pSetIconInWindow (
    IN      HWND IconWnd1,
    IN      HWND IconWnd2,
    IN      PCTSTR FileName1,
    IN      PCTSTR FileName2,
    IN      INT ResourceId
    )
{
    HANDLE Instance1;
    HANDLE Instance2;
    BOOL b = FALSE;
    HICON Icon1 = NULL;
    HICON Icon2 = NULL;

    Instance1 = LoadLibraryEx (FileName1, NULL, LOAD_LIBRARY_AS_DATAFILE);
    if (Instance1) {
        //
        // Get the icon
        //

        Icon1 = LoadIcon (Instance1, MAKEINTRESOURCE (ResourceId));
    }

    Instance2 = LoadLibraryEx (FileName2, NULL, LOAD_LIBRARY_AS_DATAFILE);
    if (Instance2) {
        //
        // Get the icon
        //

        Icon2 = LoadIcon (Instance2, MAKEINTRESOURCE (ResourceId));
    }

    if (Icon1 && Icon2) {
        SendMessage (IconWnd1, STM_SETICON, (WPARAM) Icon1, 0);
        SendMessage (IconWnd2, STM_SETICON, (WPARAM) Icon2, 0);

        b = TRUE;
    }

    if (Instance1) {
        FreeLibrary (Instance1);
    }

    if (Instance2) {
        FreeLibrary (Instance2);
    }

    return b;
}


typedef struct {
    GROWBUFFER IdArray;
    UINT CurrentId;
    UINT IdCount;
    PCTSTR FileName1;
    PCTSTR FileName2;
} DLGARGS, *PDLGARGS;


#define WMX_UPDATE_ICONS        (WM_APP+1)

BOOL
CALLBACK
IconCompareDlgProc (
    HWND hdlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    static PDLGARGS Args;
    TCHAR Number[32];
    INT ResourceId;

    switch (uMsg) {

    case WM_INITDIALOG:
        Args = (PDLGARGS) lParam;

        SetDlgItemText (hdlg, IDC_FILE_NAME1, Args->FileName1);
        SetDlgItemText (hdlg, IDC_FILE_NAME2, Args->FileName2);
        SendMessage (hdlg, WMX_UPDATE_ICONS, 0, 0);

        switch (g_ErrorLevel) {

        case 0:
            ResourceId = IDC_NO_MATCH;
            break;

        case 1:
            ResourceId = IDC_PARTIAL;
            break;

        case 2:
            ResourceId = IDC_MATCH;
            break;
        }

        CheckDlgButton (hdlg, ResourceId, BST_CHECKED);

        break;

    case WMX_UPDATE_ICONS:

        ResourceId = (INT) (*((PDWORD) Args->IdArray.Buf + Args->CurrentId));

        pSetIconInWindow (
            GetDlgItem (hdlg, IDC_ICON1),
            GetDlgItem (hdlg, IDC_ICON2),
            Args->FileName1,
            Args->FileName2,
            ResourceId
            );

        wsprintf (Number, TEXT("%i"), ResourceId);
        SetDlgItemText (hdlg, IDC_RESOURCE_ID, Number);

        break;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
        case IDCANCEL:
            if (IsDlgButtonChecked (hdlg, IDC_NO_MATCH) == BST_CHECKED) {
                g_ErrorLevel = 0;
            } else if (IsDlgButtonChecked (hdlg, IDC_PARTIAL) == BST_CHECKED) {
                g_ErrorLevel = 1;
            } else if (IsDlgButtonChecked (hdlg, IDC_MATCH) == BST_CHECKED) {
                g_ErrorLevel = 2;
            }

            EndDialog (hdlg, 0);
            break;

        case IDC_NEXT:
            if (Args->CurrentId < (Args->IdCount - 1)) {
                Args->CurrentId++;
                SendMessage (hdlg, WMX_UPDATE_ICONS, 0, 0);
            }
            break;

        case IDC_PREV:
            if (Args->CurrentId > 0) {
                Args->CurrentId--;
                SendMessage (hdlg, WMX_UPDATE_ICONS, 0, 0);
            }
            break;
        }
        break;
    }

    return FALSE;
}


VOID
pLaunchCompareDlg (
    IN      PCTSTR FileName1,
    IN      PCTSTR FileName2
    )
{
    DLGARGS Args;
    PCTSTR IconList1;
    PCTSTR IconList2;
    GROWBUFFER Buf1 = GROWBUF_INIT;
    GROWBUFFER Buf2 = GROWBUF_INIT;
    MULTISZ_ENUM Enum1, Enum2;
    BOOL Match;

    //
    // Obtain the resource ID list from both FileName1 and FileName2.
    // Put the union in a grow buffer.
    //

    ZeroMemory (&Args, sizeof (Args));
    Args.FileName1 = FileName1;
    Args.FileName2 = FileName2;

    IconList1 = ExtractIconNamesFromFile (FileName1, &Buf1);
    IconList2 = ExtractIconNamesFromFile (FileName2, &Buf2);

    if (IconList1 && IconList2) {
        //
        // Enumerate list one, then scan list two for a match
        //

        if (EnumFirstMultiSz (&Enum1, IconList1)) {
            do {
                if (_tcsnextc (Enum1.CurrentString) != TEXT('#')) {
                    continue;
                }

                Match = FALSE;

                if (EnumFirstMultiSz (&Enum2, IconList2)) {
                    do {

                        if (StringMatch (Enum1.CurrentString, Enum2.CurrentString)) {
                            Match = TRUE;
                            break;
                        }

                    } while (EnumNextMultiSz (&Enum2));
                }

                if (Match) {
                    GrowBufAppendDword (
                        &Args.IdArray,
                        (DWORD) _ttoi (_tcsinc (Enum1.CurrentString))
                        );

                    Args.IdCount++;
                } else {
                    _ftprintf (
                        stderr,
                        TEXT("Resource ID %s is not in %s\n"),
                        Enum1.CurrentString,
                        FileName2
                        );
                }

            } while (EnumNextMultiSz (&Enum1));
        }

        //
        // Enumerate list two, then scan list one for a match
        //

        if (EnumFirstMultiSz (&Enum2, IconList2)) {
            do {
                if (_tcsnextc (Enum2.CurrentString) != TEXT('#')) {
                    continue;
                }

                Match = FALSE;

                if (EnumFirstMultiSz (&Enum1, IconList1)) {
                    do {

                        if (StringMatch (Enum1.CurrentString, Enum2.CurrentString)) {
                            Match = TRUE;
                            break;
                        }

                    } while (EnumNextMultiSz (&Enum1));
                }

                if (!Match) {
                    _ftprintf (
                        stderr,
                        TEXT("Resource ID %s is not in %s\n"),
                        Enum2.CurrentString,
                        FileName1
                        );
                }
            } while (EnumNextMultiSz (&Enum2));
        }

        //
        // Now present the dialog
        //

        if (Args.IdCount) {

            DialogBoxParam (
                g_hInst,
                MAKEINTRESOURCE(IDD_COMPARE),
                NULL,
                IconCompareDlgProc,
                (LPARAM) &Args
                );

        } else {
            _ftprintf (stderr, TEXT("No common icon resources found.\n"));
        }

    } else {
        if (!IconList1) {
            _ftprintf (stderr, TEXT("Can't get icon list from %s\n"), FileName1);
        }

        if (!IconList2) {
            _ftprintf (stderr, TEXT("Can't get icon list from %s\n"), FileName2);
        }
    }

    FreeGrowBuffer (&Buf1);
    FreeGrowBuffer (&Buf2);

}



BOOL
pCompareIconImages (
    IN      PCTSTR SourceFile,
    IN      PCTSTR Name1,
    IN      PCTSTR Name2
    )
{
    GROWBUFFER SrcBuf = GROWBUF_INIT;
    GROWBUFFER DestBuf = GROWBUF_INIT;
    BOOL b;

    b = ExtractIconImageFromFile (SourceFile, Name1, &SrcBuf);

    if (b) {
        b = ExtractIconImageFromFile (SourceFile, Name2, &DestBuf);
    }

    if (b) {
        if (SrcBuf.End != DestBuf.End) {
            b = FALSE;
        } else {
            b = (memcmp (SrcBuf.Buf, DestBuf.Buf, SrcBuf.End) == 0);
        }
    }

    FreeGrowBuffer (&SrcBuf);
    FreeGrowBuffer (&DestBuf);

    return b;
}


BOOL
pMakeNameIndex (
    IN      PCTSTR SourceFile,
    OUT     PGROWBUFFER IndexBuf,
    OUT     PGROWBUFFER IndexArray
    )
{
    PCTSTR IconList;
    MULTISZ_ENUM e;

    IndexBuf->End = 0;
    IndexArray->End = 0;

    IconList = ExtractIconNamesFromFile (SourceFile, IndexBuf);

    if (IconList) {
        //
        // Build an index array
        //

        if (EnumFirstMultiSz (&e, IconList)) {
            do {
                if (_tcsnextc (e.CurrentString) != TEXT('#')) {
                    //
                    // This is a named string
                    //

                    GrowBufAppendDword (IndexArray, (DWORD) e.CurrentString);

                } else {

                    //
                    // This is a 16-bit ID
                    //

                    GrowBufAppendDword (IndexArray, (DWORD) _ttoi (_tcsinc (e.CurrentString)));
                }
            } while (EnumNextMultiSz (&e));
        }

    } else {
        _ftprintf (stderr, TEXT("No icons found in %s\n"), SourceFile);
    }


    return IndexArray->End != 0;
}

#define BLANK_ID        0xFFFFFFFF

BOOL
pFindUniqueIcons (
    IN      PCTSTR SourceFile,
    OUT     PGROWBUFFER WorkBuffer,
    OUT     PGROWBUFFER UniqueIndexArray,
    OUT     PUINT OriginalMax
    )
{
    GROWBUFFER IndexArray = GROWBUF_INIT;
    BOOL b;
    UINT i, j;
    UINT Count;
    PDWORD IdPtr;

    UniqueIndexArray->End = 0;

    b = pMakeNameIndex (SourceFile, WorkBuffer, &IndexArray);

    if (b) {

        *OriginalMax = 0;

        Count = IndexArray.End / sizeof (DWORD);
        IdPtr = (PDWORD) IndexArray.Buf;

        for (i = 0 ; i < Count ; i++) {

            if (IdPtr[i] == BLANK_ID) {
                continue;
            }

            if (IdPtr[i] < 0x10000) {
                _ftprintf (stderr, TEXT("Processing ID %u\n"), IdPtr[i]);

                if (IdPtr[i] > *OriginalMax) {
                    *OriginalMax = IdPtr[i];
                }
            } else {
                _ftprintf (stderr, TEXT("Processing ID %s\n"), IdPtr[i]);
            }

            for (j = i + 1 ; j < Count ; j++) {

                if (IdPtr[j] == BLANK_ID) {
                    continue;
                }

                if (pCompareIconImages (SourceFile, (PCTSTR) (IdPtr[i]), (PCTSTR) (IdPtr[j]))) {
                    IdPtr[j] = BLANK_ID;
                }
            }
        }

        for (i = 0 ; i < Count ; i++) {
            if (IdPtr[i] != BLANK_ID) {
                GrowBufAppendDword (UniqueIndexArray, IdPtr[i]);
            }
        }
    }

    FreeGrowBuffer (&IndexArray);
    return b;
}


UINT
pRemoveIcons (
    IN      PICON_EXTRACT_CONTEXT Context,
    IN      UINT Start,
    IN      UINT End
    )
{
    UINT Count = 0;
    GROWBUFFER Names = GROWBUF_INIT;
    PCTSTR IconList;
    MULTISZ_ENUM e;
    UINT id;

    if (Start > End) {
        return 0;
    }

    if (Start == End) {
        _ftprintf (stderr, TEXT("Removing icon %u\n"), Start);
    } else {
        _ftprintf (stderr, TEXT("Removing icon range %u through %u\n"), Start, End);
    }

    pOpenIconImage (Context, Context->DestFile, NULL, NULL);

    IconList = ExtractIconNamesFromFileEx (
                    Context->ModuleName,
                    &Names,
                    Context->Module,
                    Context->Module16
                    );

    if (IconList) {
        if (EnumFirstMultiSz (&e, IconList)) {
            do {
                if (_tcsnextc (e.CurrentString) == TEXT('#')) {

                    id = (DWORD) _ttoi (_tcsinc (e.CurrentString));

                    if (id >= Start && id <= End) {
                        if (UpdateResource (
                                Context->Update,
                                RT_ICON,
                                MAKEINTRESOURCE(id),
                                MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
                                NULL,
                                0
                                )) {
                            Count++;
                        } else {
                            _ftprintf (stderr, TEXT("Can't remove icon ID %u; rc=%u\n"), id, GetLastError());
                        }
                    }
                }
            } while (EnumNextMultiSz (&e));
        }
    } else {
        _ftprintf (stderr, TEXT("Can't get icon names; rc=%u\n"), GetLastError());
    }

    FreeGrowBuffer (&Names);

    return Count;
}


BOOL
pRemoveDuplicateIcons (
    IN      PCTSTR SourcePeFile,
    IN      PCTSTR DestPeFile
    )
{
    GROWBUFFER WorkBuffer = GROWBUF_INIT;
    GROWBUFFER UniqueIndexArray = GROWBUF_INIT;
    ICON_EXTRACT_CONTEXT Context;
    UINT Count = 0;
    UINT u;
    PDWORD IconId;
    BOOL b;
    UINT OriginalMax;

    _ftprintf (stderr, TEXT("Finding unique icons\n"));
    b = pFindUniqueIcons (SourcePeFile, &WorkBuffer, &UniqueIndexArray, &OriginalMax);

    if (b) {
        b = BeginIconExtraction (&Context, DestPeFile);

        if (!b) {
            _ftprintf (stderr, TEXT("Can't save icons to %s\n"), DestPeFile);
        }
    }

    if (b) {

        _ftprintf (stderr, TEXT("Updating icon resources\n"));

        Count = UniqueIndexArray.End / sizeof (DWORD);
        IconId = (PDWORD) UniqueIndexArray.Buf;

        for (u = 0 ; u < Count ; u++) {
            b = CopyIcon (&Context, SourcePeFile, (PCTSTR) (IconId[u]), 0);
            if (!b) {
                if (IconId[u] < 0x10000) {
                    _ftprintf (stderr, TEXT("Can't copy icon %u\n"), IconId[u]);
                } else {
                    _ftprintf (stderr, TEXT("Can't copy icon %s\n"), IconId[u]);
                }

                break;
            }
        }
    }

    if (b) {

        pRemoveIcons (&Context, Count + 1, OriginalMax);

        b = EndIconExtraction (&Context);

        if (!b) {
            _ftprintf (stderr, TEXT("Can't safe icons\n"));
        }
    }

    if (b) {
        _ftprintf (stderr, TEXT("Final icon count: %u\n"), Count);
    }

    FreeGrowBuffer (&WorkBuffer);
    FreeGrowBuffer (&UniqueIndexArray);

    return b;
}


BOOL
pGetMinAndMax (
    IN      PICON_EXTRACT_CONTEXT Context,
    OUT     PUINT Min,
    OUT     PUINT Max
    )
{
    BOOL b = FALSE;
    GROWBUFFER Names = GROWBUF_INIT;
    PCTSTR IconList;
    UINT id;
    MULTISZ_ENUM e;

    *Min = (UINT) -1;
    *Max = 0;

    pOpenIconImage (Context, Context->DestFile, NULL, NULL);

    _ftprintf (stderr, TEXT("Getting min/max icon indexes from %s\n"), Context->ModuleName);

    IconList = ExtractIconNamesFromFileEx (
                    Context->ModuleName,
                    &Names,
                    Context->Module,
                    Context->Module16
                    );

    if (IconList) {
        if (EnumFirstMultiSz (&e, IconList)) {
            do {
                if (_tcsnextc (e.CurrentString) == TEXT('#')) {

                    id = (DWORD) _ttoi (_tcsinc (e.CurrentString));

                    if (id < *Min) {
                        *Min = id;
                    }

                    if (id > *Max) {
                        *Max = id;
                    }
                }
            } while (EnumNextMultiSz (&e));
        }

        b = TRUE;
    }

    FreeGrowBuffer (&Names);

    if (b) {
        _ftprintf (stderr, TEXT("Min: %u  Max: %u\n"), *Min, *Max);
    } else {
        _ftprintf (stderr, TEXT("Can't get min/max info, rc=%u\n"), GetLastError());
    }


    return b;
}


UINT
pCopyIconRange (
    IN      PICON_EXTRACT_CONTEXT Context,
    IN      PCTSTR IconFile,
    IN      UINT Start,
    IN      UINT End
    )
{
    UINT Count = 0;
    GROWBUFFER Names = GROWBUF_INIT;
    PCTSTR IconList;
    UINT id;
    MULTISZ_ENUM e;

    pOpenIconImage (Context, IconFile, NULL, NULL);

    if (Start == End) {
        _ftprintf (
            stderr,
            TEXT("Copying icon %u from %s to %s\n"),
            Start,
            Context->ModuleName,
            Context->IconImageFileName
            );
    } else {
        _ftprintf (
            stderr,
            TEXT("Copying icon range %u to %u from %s to %s\n"),
            Start,
            End,
            Context->ModuleName,
            Context->IconImageFileName
            );
    }

    IconList = ExtractIconNamesFromFileEx (
                    Context->ModuleName,
                    &Names,
                    Context->Module,
                    Context->Module16
                    );

    if (IconList) {
        if (EnumFirstMultiSz (&e, IconList)) {
            do {
                if (_tcsnextc (e.CurrentString) == TEXT('#')) {

                    id = (DWORD) _ttoi (_tcsinc (e.CurrentString));

                    if (id >= Start && id <= End) {
                        if (!CopyIcon (Context, IconFile, e.CurrentString, 0)) {
                            _ftprintf (stderr, TEXT("Can't copy %s [%i], error %u\n"), IconFile, id, GetLastError());
                        } else {
                            Count++;
                        }
                    }
                }
            } while (EnumNextMultiSz (&e));
        }
    } else {
        _ftprintf (stderr, TEXT("Can't get icon names from %s\n"), Context->ModuleName);
    }

    FreeGrowBuffer (&Names);

    return Count;
}





