/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    hwwiz.c

Abstract:

    Implements a upgwiz wizard for obtaining hardware information.

Author:

    Jim Schmidt (jimschm)  05-Oct-1998

Revision History:

    <alias> <date> <comments>

--*/

#include "pch.h"
#include "..\inc\dgdll.h"


DATATYPE g_DataTypes[] = {
    {UPGWIZ_VERSION,
        "PNP Device Should Be Compatible",
        "You specify the PNP device or devices that were incorrectly reported as incompatible.",
        0
    },

    {UPGWIZ_VERSION,
        "PNP Device Should Be Incompatible",
        "You specify the PNP device or devices that need to be reported as incompatible.",
        0
    },

    {UPGWIZ_VERSION,
        "PNP Device Has Loss of Functionality",
        "A device is compatible, but some functionality is lost.",
        0,
        DTF_REQUIRE_TEXT|DTF_ONE_SELECTION,
        1024,
        NULL,
        "&Text For Incompatibility:"
    },

    {UPGWIZ_VERSION,
        "Compatible Windows 3.1 Driver",
        "Use this to completely suppress the generic Win3.1 driver warning for a .386 file.",
        0,
        DTF_REQUIRE_DESCRIPTION|DTF_ONE_SELECTION,
        1024,
        "&Name of Device that Driver Controls:"
    },

    {UPGWIZ_VERSION,
        "Incompatible Windows 3.1 Driver",
        "Use this to give a better problem description to a driver that causes the generic Win3.1 driver warning.",
        0,
        DTF_REQUIRE_TEXT|DTF_REQUIRE_DESCRIPTION|DTF_ONE_SELECTION,
        1024,
        "&Name of Device that Driver Controls:",
        "&Describe The Problem:"
    },

    {UPGWIZ_VERSION,
        "Compatible TWAIN Data Sources",
        "Removes the incompatible warning caused by an unknown TWAIN data source."
    },

    {UPGWIZ_VERSION,
        "Compatible Joysticks",
        "Removes the incompatible warning caused by an unknown joysticks."
    }

};


GROWBUFFER g_DataObjects = GROWBUF_INIT;
POOLHANDLE g_DataObjectPool;

typedef struct {
    PCSTR Description;
    PCSTR FullPath;
} TWAINPARAM, *PTWAINPARAM;

BOOL
pGeneratePnpOutput (
    IN      POUTPUTARGS Args,
    IN      HANDLE File
    );

BOOL
pGenerateWin31DriverOutput (
    IN      POUTPUTARGS Args,
    IN      HANDLE File
    );

BOOL
pGenerateTwainOutput (
    IN      POUTPUTARGS Args,
    IN      HANDLE File
    );

BOOL
pGenerateJoystickOutput (
    IN      POUTPUTARGS Args,
    IN      HANDLE File
    );


HINSTANCE g_OurInst;

BOOL
Init (
    VOID
    )
{
#ifndef UPGWIZ4FLOPPY
    return InitToolMode (g_OurInst);
#else
    return TRUE;
#endif
}

VOID
Terminate (
    VOID
    )
{
    //
    // Local cleanup
    //

    FreeGrowBuffer (&g_DataObjects);

    if (g_DataObjectPool) {
        PoolMemDestroyPool (g_DataObjectPool);
    }

#ifndef UPGWIZ4FLOPPY
    TerminateToolMode (g_OurInst);
#endif
}


BOOL
WINAPI
DllMain (
    IN      HINSTANCE hInstance,
    IN      DWORD dwReason,
    IN      LPVOID lpReserved
    )
{
    if (dwReason == DLL_PROCESS_DETACH) {
        MYASSERT (g_OurInst == hInstance);
        Terminate();
    }

    g_OurInst = hInstance;

    return TRUE;
}


UINT
GiveVersion (
    VOID
    )
{
    Init();

    return UPGWIZ_VERSION;
}


PDATATYPE
GiveDataTypeList (
    OUT     PUINT Count
    )
{
    UINT u;

    *Count = sizeof (g_DataTypes) / sizeof (g_DataTypes[0]);

    for (u = 0 ; u < *Count ; u++) {
        g_DataTypes[u].DataTypeId = u;
    }

    return g_DataTypes;
}


PDATAOBJECT
GiveDataObjectList (
    IN      UINT DataTypeId,
    OUT     PUINT Count
    )
{
    HARDWARE_ENUM e;
    PDATAOBJECT Data;
    HINF Inf;
    TCHAR Path[MAX_TCHAR_PATH];
    TCHAR FullPath[MAX_TCHAR_PATH];
    INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
    PCTSTR DriverPath;
    PCTSTR DriverFile;
    TWAINDATASOURCE_ENUM te;
    JOYSTICK_ENUM je;
    TWAINPARAM TwainParam;

    g_DataObjectPool = PoolMemInitNamedPool ("Data Objects");

    if (DataTypeId < 3) {
        //
        // Enumerate the PNP devices
        //

        if (EnumFirstHardware (&e, ENUM_ALL_DEVICES, ENUM_WANT_DEV_FIELDS)) {
            do {
                if (!e.Driver || !e.DeviceDesc || !e.InstanceId) {
                    continue;
                }

                Data = (PDATAOBJECT) GrowBuffer (&g_DataObjects, sizeof (DATAOBJECT));

                Data->Version = UPGWIZ_VERSION;
                Data->NameOrPath = PoolMemDuplicateString (g_DataObjectPool, e.DeviceDesc);
                Data->Flags = 0;
                Data->DllParam = PoolMemDuplicateString (g_DataObjectPool, e.FullKey);

            } while (EnumNextHardware (&e));
        }

    } else if (DataTypeId >= 3 && DataTypeId < 5) {
        //
        // Enumerate the .386 candidates
        //

        wsprintf (Path, TEXT("%s\\system.ini"), g_WinDir);

        Inf = InfOpenInfFile (Path);
        if (Inf != INVALID_HANDLE_VALUE) {
            if (InfFindFirstLine (Inf, TEXT("386Enh"), NULL, &is)) {
                do {
                    DriverPath = InfGetStringField (&is, 1);
                    if (DriverPath) {
                        //
                        // Determine if device driver is known
                        //

                        if (_tcsnextc (DriverPath) != TEXT('*')) {
                            DriverFile = GetFileNameFromPath (DriverPath);

                            if (!_tcschr (DriverPath, TEXT(':'))) {
                                if (!SearchPath (
                                        NULL,
                                        DriverFile,
                                        NULL,
                                        MAX_TCHAR_PATH,
                                        FullPath,
                                        NULL
                                        )) {
                                    _tcssafecpy (FullPath, DriverPath, MAX_TCHAR_PATH);
                                }
                            } else {
                                _tcssafecpy (FullPath, DriverPath, MAX_TCHAR_PATH);
                            }

                            if (!_tcschr (FullPath, TEXT(':'))) {
                                continue;
                            }

                            Data = (PDATAOBJECT) GrowBuffer (&g_DataObjects, sizeof (DATAOBJECT));

                            Data->Version = UPGWIZ_VERSION;
                            Data->NameOrPath = PoolMemDuplicateString (g_DataObjectPool, DriverFile);
                            Data->Flags = 0;
                            Data->DllParam = PoolMemDuplicateString (g_DataObjectPool, FullPath);
                        }
                    }

                } while (InfFindNextLine (&is));
            }

            InfCloseInfFile (Inf);
            InfCleanUpInfStruct (&is);
        }

    } else if (DataTypeId == 5) {
        //
        // Enumerate the TWAIN devices
        //

        if (EnumFirstTwainDataSource (&te)) {
            do {
                Data = (PDATAOBJECT) GrowBuffer (&g_DataObjects, sizeof (DATAOBJECT));

                Data->Version = UPGWIZ_VERSION;
                Data->NameOrPath = PoolMemDuplicateString (g_DataObjectPool, te.DisplayName);
                Data->Flags = 0;

                TwainParam.Description = PoolMemDuplicateString (g_DataObjectPool, te.DisplayName);
                TwainParam.FullPath = PoolMemDuplicateString (g_DataObjectPool, te.DataSourceModule);

                Data->DllParam = PoolMemGetAlignedMemory (g_DataObjectPool, sizeof (TWAINPARAM));

                CopyMemory (Data->DllParam, &TwainParam, sizeof (TWAINPARAM));

            } while (EnumNextTwainDataSource (&te));
        }

    } else if (DataTypeId == 6) {
        //
        // Enumerate the Joystick
        //

        if (EnumFirstJoystick (&je)) {
            do {

                if (!_mbschr (je.JoystickDriver, ':')) {
                    if (!SearchPath (
                            NULL,
                            je.JoystickDriver,
                            NULL,
                            MAX_TCHAR_PATH,
                            Path,
                            NULL
                            )) {
                        continue;
                    }
                } else {
                    StringCopy (Path, je.JoystickDriver);
                }

                Data = (PDATAOBJECT) GrowBuffer (&g_DataObjects, sizeof (DATAOBJECT));

                Data->Version = UPGWIZ_VERSION;
                Data->NameOrPath = PoolMemDuplicateString (g_DataObjectPool, je.JoystickName);
                Data->Flags = 0;

                TwainParam.Description = PoolMemDuplicateString (g_DataObjectPool, je.JoystickName);
                TwainParam.FullPath = PoolMemDuplicateString (g_DataObjectPool, Path);

                Data->DllParam = PoolMemGetAlignedMemory (g_DataObjectPool, sizeof (TWAINPARAM));

                CopyMemory (Data->DllParam, &TwainParam, sizeof (TWAINPARAM));

            } while (EnumNextJoystick (&je));
        }
    }

    *Count = g_DataObjects.End / sizeof (DATAOBJECT);

    return (PDATAOBJECT) g_DataObjects.Buf;
}

BOOL
OldGenerateOutput (
    IN      POUTPUTARGS Args
    )
{
    return TRUE;
}

//#if 0

BOOL
pWritePnpIdRule (
    IN      HANDLE File,
    IN      PHARDWARE_ENUM EnumPtr,
    IN      PCSTR Description           OPTIONAL
    )
{
    CHAR Path[MAX_MBCHAR_PATH];
    CHAR DriverKey[MAX_REGISTRY_KEY];
    HKEY Key = NULL;
    BOOL b = FALSE;
    PCSTR InfPath = NULL;
    WIN32_FIND_DATA fd;
    HANDLE Find = INVALID_HANDLE_VALUE;
    CHAR Buf[2048];
    CHAR WinDir[MAX_MBCHAR_PATH];
    CHAR StringSectKey[256];

    GetWindowsDirectory (WinDir, MAX_MBCHAR_PATH);

    if (Description && *Description == 0) {
        Description = NULL;
    }

    //
    // Get the driver
    //

    __try {
        if (!EnumPtr->Driver || !EnumPtr->DeviceDesc || !EnumPtr->InstanceId) {
            DEBUGMSG ((DBG_WHOOPS, "Enum field missing; should have been screened out of object list"));
            __leave;
        }

        wsprintf (DriverKey, "HKLM\\System\\CurrentControlSet\\Services\\Class\\%s", EnumPtr->Driver);

        Key = OpenRegKeyStr (DriverKey);
        if (!Key) {
            DEBUGMSG ((DBG_WHOOPS, "Can't open %s", DriverKey));
            __leave;
        }

        InfPath = GetRegValueString (Key, "InfPath");
        if (!InfPath || *InfPath == 0) {
            DEBUGMSG ((DBG_WHOOPS, "No InfPath in %s", DriverKey));
            MessageBox (NULL, "Selected device does not have an INF path.  The INF path is required.", NULL, MB_OK);
            __leave;
        }

        if (!_mbschr (InfPath, '\\')) {
            wsprintf (Path, "%s\\inf\\%s", WinDir, InfPath);
        } else {
            StringCopy (Path, InfPath);
        }

        Find = FindFirstFile (Path, &fd);
        if (Find == INVALID_HANDLE_VALUE) {
            DEBUGMSG ((DBG_WHOOPS, "Can't find %s", Path));
            __leave;
        }

        if (!Description) {
            wsprintf (Buf, "%s,,", EnumPtr->DeviceDesc);
        } else {
            GenerateUniqueStringSectKey ("DV", StringSectKey);
            wsprintf (Buf, "%s, %%%s%%,", EnumPtr->DeviceDesc, StringSectKey);
        }

        if (!WizardWriteColumn (File, Buf, 45)) {
            __leave;
        }

        wsprintf (Buf, "%s,", fd.cFileName);
        if (!WizardWriteColumn (File, Buf, 15)) {
            __leave;
        }

        wsprintf (Buf, "FILESIZE(%u),", fd.nFileSizeLow);
        if (!WizardWriteRealString (File, Buf)) {
            __leave;
        }

        if (!WizardWriteRealString (File, " PNPID(")) {
            __leave;
        }

        if (!WizardWriteQuotedString (File, EnumPtr->InstanceId)) {
            __leave;
        }

        if (!WizardWriteRealString (File, ")\r\n")) {
            __leave;
        }

        if (Description) {
            if (!WizardWriteRealString (File, "[Strings]\r\n")) {
                __leave;
            }

            WriteStringSectKey (File, StringSectKey, Description);
        }

        b = TRUE;
    }
    __finally {
        if (Key) {
            CloseRegKey (Key);
        }

        if (InfPath) {
            MemFree (g_hHeap, 0, InfPath);
        }

        if (Find != INVALID_HANDLE_VALUE) {
            FindClose (Find);
        }
    }

    return b;
}


BOOL
GenerateOutput (
    IN      POUTPUTARGS Args
    )
{
    BOOL b = FALSE;
    HANDLE File;
    CHAR Path[MAX_MBCHAR_PATH];

    switch (Args->DataTypeId) {

    case 0:
        wsprintf (Path, "%s\\comphw.txt", Args->OutboundDir);
        break;

    case 1:
        wsprintf (Path, "%s\\incomphw.txt", Args->OutboundDir);
        break;

    case 2:
        wsprintf (Path, "%s\\hwfnloss.inx", Args->OutboundDir);
        break;

    case 3:
    case 4:
        wsprintf (Path, "%s\\win31drv.inx", Args->OutboundDir);
        break;

    case 5:
        wsprintf (Path, "%s\\twain.inx", Args->OutboundDir);
        break;

    case 6:
        wsprintf (Path, "%s\\joystick.inx", Args->OutboundDir);
        break;

    default:
        wsprintf (Path, "%s\\unknown.txt", Args->OutboundDir);
        break;
    }

    printf ("Saving data to %s\n\n", Path);

    File = CreateFile (
                Path,
                GENERIC_WRITE,
                0,
                NULL,
                OPEN_ALWAYS,
                FILE_ATTRIBUTE_NORMAL,
                NULL
                );

    if (File == INVALID_HANDLE_VALUE) {
        printf ("Can't open file for output.\n");
        return FALSE;
    }

    __try {
        SetFilePointer (File, 0, NULL, FILE_END);

        //
        // Write [Identification] for all .inx files
        //

        switch (Args->DataTypeId) {

        case 0:
        case 1:
            break;

        default:
            if (!WizardWriteRealString (File, "[Identification]\r\n")) {
                __leave;
            }
            break;
        }

        //
        // Write user name and date/time
        //

        if (!WriteHeader (File)) {
            __leave;
        }

        //
        // Generate output depending on the type
        //

        switch (Args->DataTypeId) {

        case 0:
        case 1:
        case 2:
            b = pGeneratePnpOutput (Args, File);
            break;

        case 3:
        case 4:
            b = pGenerateWin31DriverOutput (Args, File);
            break;

        case 5:
            b = pGenerateTwainOutput (Args, File);
            break;

        case 6:
            b = pGenerateJoystickOutput (Args, File);
            break;
        }

        //
        // Write a final blank line
        //

        b = b & WizardWriteRealString (File, "\r\n");
    }
    __finally {
        CloseHandle (File);
    }

    return b;
}


BOOL
pGeneratePnpOutput (
    IN      POUTPUTARGS Args,
    IN      HANDLE File
    )
{
    PDATAOBJECT Data;
    UINT Count;
    UINT Pos;
    HARDWARE_ENUM e;
    BOOL b = FALSE;

    __try {
        Count = g_DataObjects.End / sizeof (DATAOBJECT);

        if (EnumFirstHardware (&e, ENUM_ALL_DEVICES, ENUM_WANT_DEV_FIELDS)) {
            do {
                Data = (PDATAOBJECT) g_DataObjects.Buf;

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

                    if (StringIMatch ((PCSTR) Data->DllParam, e.FullKey)) {

                        if (Data->Flags & DOF_SELECTED) {

                            if (Args->DataTypeId == 2) {
                                if (!WizardWriteRealString (File, "[MinorProblems]\r\n")) {
                                    __leave;
                                }

                                if (!pWritePnpIdRule (File, &e, Args->OptionalText)) {
                                    __leave;
                                }

                            } else {

                                if (!pWritePnpIdRule (File, &e, NULL)) {
                                    __leave;
                                }
                            }
                        }

                        break;
                    }

                    Data++;
                }

            } while (EnumNextHardware (&e));
        }

        b = TRUE;
    }
    __finally {
        if (!b) {
            AbortHardwareEnum (&e);
        }
    }

    return b;
}


BOOL
pGenerateWin31DriverOutput (
    IN      POUTPUTARGS Args,
    IN      HANDLE File
    )
{
    PDATAOBJECT Data;
    UINT Pos;
    UINT Count;
    BOOL b = FALSE;

    Data = (PDATAOBJECT) g_DataObjects.Buf;
    Count = g_DataObjects.End / sizeof (DATAOBJECT);

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

        if (Data->Flags & DOF_SELECTED) {

            b = WriteFileAttributes (
                    Args,
                    NULL,
                    File,
                    (PCSTR) Data->DllParam,
                    Args->OptionalText ? "[NonPnpDrivers]" : "[NonPnpDrivers_NoMessage]"
                    );

            break;
        }

        Data++;
    }

    return b;
}



BOOL
pGenerateTwainOutput (
    IN      POUTPUTARGS Args,
    IN      HANDLE File
    )
{
    PDATAOBJECT Data;
    UINT Pos;
    UINT Count;
    BOOL b = FALSE;
    PTWAINPARAM TwainParam;

    Data = (PDATAOBJECT) g_DataObjects.Buf;
    Count = g_DataObjects.End / sizeof (DATAOBJECT);

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

        if (Data->Flags & DOF_SELECTED) {

            TwainParam = (PTWAINPARAM) Data->DllParam;

            b = WriteFileAttributes (
                    Args,
                    TwainParam->Description,
                    File,
                    TwainParam->FullPath,
                    "[CompatibleFiles]"
                    );

            break;
        }

        Data++;
    }

    return b;
}


BOOL
pGenerateJoystickOutput (
    IN      POUTPUTARGS Args,
    IN      HANDLE File
    )
{
    PDATAOBJECT Data;
    UINT Pos;
    UINT Count;
    BOOL b = FALSE;
    PTWAINPARAM TwainParam;

    Data = (PDATAOBJECT) g_DataObjects.Buf;
    Count = g_DataObjects.End / sizeof (DATAOBJECT);

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

        if (Data->Flags & DOF_SELECTED) {

            TwainParam = (PTWAINPARAM) Data->DllParam;

            b = WriteFileAttributes (
                    Args,
                    TwainParam->Description,
                    File,
                    TwainParam->FullPath,
                    "[CompatibleFiles]"
                    );

            break;
        }

        Data++;
    }

    return b;
}

//#endif