/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    dynupdt.c

Abstract:

    The Dynamic Update feature of WINNT32.

Author:

    Ovidiu Temereanca (ovidiut) 02-Jul-2000

Revision History:

    <alias>  <date>      <comment>

--*/

#include "precomp.h"

//
// BUGBUG - comment functions
//

#define GUIDRVS_FIELD_CABNAME       1
#define GUIDRVS_FIELD_INFNAME       2
#define GUIDRVS_FIELD_DRIVERVER     3
#define GUIDRVS_FIELD_HARDWAREID    4

#define MAX_UPGCHK_ELAPSED_SECONDS  (30 * 60)

PDYNUPDT_STATUS g_DynUpdtStatus;

static WORD g_MapProductTypeToSuite[] = {
    0,                              // pro
    VER_SUITE_SMALLBUSINESS,        // srv
    VER_SUITE_ENTERPRISE,           // ads
    VER_SUITE_DATACENTER,           // dtc
    VER_SUITE_PERSONAL,             // per
    VER_SUITE_BLADE,                // bla
};

static BYTE g_MapProductTypeToPT[] = {
    VER_NT_WORKSTATION,
    VER_NT_SERVER,
    VER_NT_SERVER,
    VER_NT_SERVER,
    VER_NT_WORKSTATION,
    VER_NT_SERVER,
};

typedef
BOOL
(*PCOMPLOADFN) (
    IN      PCTSTR LibraryPath
    );

BOOL
DynUpdtDebugLog(
    IN Winnt32DebugLevel Level,
    IN LPCTSTR           Text,
    IN UINT              MessageId,
    ...
    )
{
    va_list arglist;
    BOOL b;
    TCHAR bigBuffer[1024];
    PCTSTR prefix;
    DWORD rc = GetLastError ();

    //
    // this param is never used momentarily
    //
    MYASSERT (Text);
    if (!Text) {
        return FALSE;
    }
    MYASSERT (!MessageId);

    if (Level <= Winnt32LogError) {
        prefix = TEXT("DUError: ");
    } else if (Level == Winnt32LogWarning) {
        prefix = TEXT("DUWarning: ");
    } else {
        prefix = TEXT("DUInfo: ");
    }

    _sntprintf (bigBuffer, sizeof (bigBuffer) / sizeof (TCHAR), TEXT("%s%s"), prefix, Text);

    va_start(arglist,MessageId);

    b = DebugLog2 (Level, bigBuffer, MessageId, arglist);

    va_end(arglist);

    SetLastError (rc);
    return b;
}


BOOL
pDoesFileExist (
    IN      PCTSTR FilePath
    )
{
    WIN32_FIND_DATA fd;

    return FileExists (FilePath, &fd) && !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
}


BOOL
pDoesDirectoryExist (
    IN      PCTSTR DirPath
    )
{
    WIN32_FIND_DATA fd;

    return FileExists (DirPath, &fd) && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
}


BOOL
pNonemptyFilePresent (
    IN      PCTSTR FilePath
    )
{
    WIN32_FIND_DATA fd;

    return FileExists (FilePath, &fd) &&
        fd.nFileSizeLow > 0 &&
        !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
}


BOOL
pGetTargetInfo (
    OUT     POSVERSIONINFOEX TargetVersion,     OPTIONAL
    OUT     PTSTR TargetPlatform,               OPTIONAL
    IN      DWORD TargetPlatformChars,          OPTIONAL
    OUT     PLCID LocaleID                      OPTIONAL
    )
{
    TCHAR buffer[256];
    UINT productType;

    //
    // get some data from the main inf
    //
    if (!FullInfName[0]) {
        if (!FindPathToWinnt32File (InfName, FullInfName, MAX_PATH)) {
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("pGetTargetInfo: FindPathToWinnt32File failed"),
                0
                );
            return FALSE;
        }
    }

    if (TargetVersion) {
        if (!GetPrivateProfileString (
                TEXT("Miscellaneous"),
                TEXT("ProductType"),
                TEXT(""),
                buffer,
                256,
                FullInfName
                )) {
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("%1 key in [%2] section is missing from %3; aborting operation"),
                0,
                TEXT("ProductType"),
                TEXT("Miscellaneous"),
                FullInfName
                );
            return FALSE;
        }
        if (buffer[0] < TEXT('0') || buffer[0] > TEXT('5') || buffer[1]) {
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("Invalid %1 value (%2) in %3"),
                0,
                TEXT("ProductType"),
                buffer,
                FullInfName
                );
            return FALSE;
        }

        productType = buffer[0] - TEXT('0');

        if (!GetPrivateProfileString (
                TEXT("Miscellaneous"),
                TEXT("ServicePack"),
                TEXT(""),
                buffer,
                256,
                FullInfName
                )) {
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("%1 key in [%2] section is missing from %3; aborting operation"),
                0,
                TEXT("ServicePack"),
                TEXT("Miscellaneous"),
                FullInfName
                );
            return FALSE;
        }

        if (_stscanf (
                buffer,
                TEXT("%hu.%hu"),
                &TargetVersion->wServicePackMajor,
                &TargetVersion->wServicePackMinor
                ) != 2) {
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("Invalid %1 value (%2) in %3"),
                0,
                TEXT("ServicePack"),
                buffer,
                FullInfName
                );
            return FALSE;
        }
        TargetVersion->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
        TargetVersion->dwMajorVersion = VER_PRODUCTMAJORVERSION;
        TargetVersion->dwMinorVersion = VER_PRODUCTMINORVERSION;
        TargetVersion->dwBuildNumber = VER_PRODUCTBUILD;
        TargetVersion->dwPlatformId = VER_PLATFORM_WIN32_NT;
        TargetVersion->wSuiteMask = g_MapProductTypeToSuite[productType];
        TargetVersion->wProductType = g_MapProductTypeToPT[productType];
    }

    if (TargetPlatform) {
        if (!GetPrivateProfileString (
                TEXT("Miscellaneous"),
                TEXT("DestinationPlatform"),
                TEXT(""),
                TargetPlatform,
                TargetPlatformChars,
                FullInfName
                )) {
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("%1 key in [%2] section is missing from %3; aborting operation"),
                0,
                TEXT("DestinationPlatform"),
                TEXT("Miscellaneous"),
                FullInfName
                );
            return FALSE;
        }
    }

    if (LocaleID) {
        MYASSERT (SourceNativeLangID);
        *LocaleID = SourceNativeLangID;
    }

    return TRUE;
}


BOOL
pInitializeSupport (
    IN      PCTSTR ComponentName,
    IN      PCOMPLOADFN LoadFn,
    IN      BOOL UseRegistryReplacement
    )
{
    TCHAR pathSupportLib[MAX_PATH];

    if (UseRegistryReplacement) {
        HKEY key;
        DWORD rc;
        BOOL b = FALSE;

        rc = RegOpenKey (
                HKEY_LOCAL_MACHINE,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\Winnt32\\5.1"),
                &key
                );
        if (rc == ERROR_SUCCESS) {
            DWORD size = 0;
            rc = RegQueryValueEx (key, ComponentName, NULL, NULL, NULL, &size);
            if (rc == ERROR_SUCCESS && size > 0) {
                PTSTR buf = MALLOC (size);
                if (buf) {
                    rc = RegQueryValueEx (key, ComponentName, NULL, NULL, (LPBYTE)buf, &size);
                    if (rc == ERROR_SUCCESS && (*LoadFn) (buf)) {
                        DynUpdtDebugLog (
                            Winnt32LogInformation,
                            TEXT("pInitializeSupport: using registry specified replacement file <%1>"),
                            0,
                            buf
                            );
                        b = TRUE;
                    }
                    FREE (buf);
                }
            }
            RegCloseKey (key);
        }

        if (b) {
            return TRUE;
        }
    }

    if (FindPathToWinnt32File (ComponentName, pathSupportLib, MAX_PATH)) {
        if ((*LoadFn) (pathSupportLib)) {
            return TRUE;
        }
    }
    DynUpdtDebugLog (
        Winnt32LogError,
        TEXT("pInitializeSupport: %1 could not be loaded or is corrupt"),
        0,
        ComponentName
        );
    return FALSE;
}

BOOL
pLoadHwdbLib (
    IN      PCTSTR LibraryPath
    )
{
    DWORD rc;

    //
    // Use WinVerifyTrust first?
    //

    g_DynUpdtStatus->HwdbLib = LoadLibraryEx (LibraryPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
    if (!g_DynUpdtStatus->HwdbLib) {
        return FALSE;
    }
    g_DynUpdtStatus->HwdbInitialize = (PHWDBINITIALIZE) GetProcAddress (g_DynUpdtStatus->HwdbLib, S_HWDBAPI_HWDBINITIALIZE);
    g_DynUpdtStatus->HwdbTerminate = (PHWDBTERMINATE) GetProcAddress (g_DynUpdtStatus->HwdbLib, S_HWDBAPI_HWDBTERMINATE);
    g_DynUpdtStatus->HwdbOpen = (PHWDBOPEN) GetProcAddress (g_DynUpdtStatus->HwdbLib, S_HWDBAPI_HWDBOPEN);
    g_DynUpdtStatus->HwdbClose = (PHWDBCLOSE) GetProcAddress (g_DynUpdtStatus->HwdbLib, S_HWDBAPI_HWDBCLOSE);
    g_DynUpdtStatus->HwdbAppendInfs = (PHWDBAPPENDINFS) GetProcAddress (g_DynUpdtStatus->HwdbLib, S_HWDBAPI_HWDBAPPENDINFS);
    g_DynUpdtStatus->HwdbFlush = (PHWDBFLUSH) GetProcAddress (g_DynUpdtStatus->HwdbLib, S_HWDBAPI_HWDBFLUSH);
    g_DynUpdtStatus->HwdbHasDriver = (PHWDBHASDRIVER) GetProcAddress (g_DynUpdtStatus->HwdbLib, S_HWDBAPI_HWDBHASDRIVER);
    g_DynUpdtStatus->HwdbHasAnyDriver = (PHWDBHASANYDRIVER) GetProcAddress (g_DynUpdtStatus->HwdbLib, S_HWDBAPI_HWDBHASANYDRIVER);

    if (!g_DynUpdtStatus->HwdbInitialize ||
        !g_DynUpdtStatus->HwdbTerminate ||
        !g_DynUpdtStatus->HwdbOpen ||
        !g_DynUpdtStatus->HwdbClose ||
        !g_DynUpdtStatus->HwdbAppendInfs ||
        !g_DynUpdtStatus->HwdbFlush ||
        !g_DynUpdtStatus->HwdbHasDriver ||
        !g_DynUpdtStatus->HwdbHasAnyDriver
        ) {
        g_DynUpdtStatus->HwdbInitialize = NULL;
        g_DynUpdtStatus->HwdbTerminate = NULL;
        g_DynUpdtStatus->HwdbOpen = NULL;
        g_DynUpdtStatus->HwdbClose = NULL;
        g_DynUpdtStatus->HwdbAppendInfs = NULL;
        g_DynUpdtStatus->HwdbFlush = NULL;
        g_DynUpdtStatus->HwdbHasDriver = NULL;
        g_DynUpdtStatus->HwdbHasAnyDriver = NULL;
        rc = GetLastError ();
        FreeLibrary (g_DynUpdtStatus->HwdbLib);
        g_DynUpdtStatus->HwdbLib = NULL;
        SetLastError (rc);
        return FALSE;
    }

    return TRUE;
}

BOOL
pLoadDuLib (
    IN      PCTSTR LibraryPath
    )
{
    DWORD rc;

    //
    // BUGBUG - Use WinVerifyTrust first?
    //

    g_DynUpdtStatus->DuLib = LoadLibraryEx (LibraryPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
    if (!g_DynUpdtStatus->DuLib) {
        return FALSE;
    }
    g_DynUpdtStatus->DuIsSupported = (PDUISSUPPORTED) GetProcAddress (g_DynUpdtStatus->DuLib, API_DU_ISSUPPORTED);
    g_DynUpdtStatus->DuInitialize = (PDUINITIALIZE) GetProcAddress (g_DynUpdtStatus->DuLib, API_DU_INITIALIZE);
    g_DynUpdtStatus->DuDoDetection = (PDUDODETECTION) GetProcAddress (g_DynUpdtStatus->DuLib, API_DU_DODETECTION);
    g_DynUpdtStatus->DuQueryUnsupDrvs = (PDUQUERYUNSUPDRVS) GetProcAddress (g_DynUpdtStatus->DuLib, API_DU_QUERYUNSUPDRVS);
    g_DynUpdtStatus->DuBeginDownload = (PDUBEGINDOWNLOAD) GetProcAddress (g_DynUpdtStatus->DuLib, API_DU_BEGINDOWNLOAD);
    g_DynUpdtStatus->DuAbortDownload = (PDUABORTDOWNLOAD) GetProcAddress (g_DynUpdtStatus->DuLib, API_DU_ABORTDOWNLOAD);
    g_DynUpdtStatus->DuUninitialize = (PDUUNINITIALIZE) GetProcAddress (g_DynUpdtStatus->DuLib, API_DU_UNINITIALIZE);

    if (!g_DynUpdtStatus->DuIsSupported ||
        !g_DynUpdtStatus->DuInitialize ||
        !g_DynUpdtStatus->DuDoDetection ||
        !g_DynUpdtStatus->DuQueryUnsupDrvs ||
        !g_DynUpdtStatus->DuBeginDownload ||
        !g_DynUpdtStatus->DuAbortDownload ||
        !g_DynUpdtStatus->DuUninitialize
        ) {
        DynUpdtDebugLog (
            Winnt32LogError,
            TEXT("pLoadDuLib: %1 is missing one or more required entry points"),
            0,
            LibraryPath
            );
        g_DynUpdtStatus->DuIsSupported = NULL;
        g_DynUpdtStatus->DuInitialize = NULL;
        g_DynUpdtStatus->DuDoDetection = NULL;
        g_DynUpdtStatus->DuQueryUnsupDrvs = NULL;
        g_DynUpdtStatus->DuBeginDownload = NULL;
        g_DynUpdtStatus->DuAbortDownload = NULL;
        g_DynUpdtStatus->DuUninitialize = NULL;
        rc = GetLastError ();
        FreeLibrary (g_DynUpdtStatus->DuLib);
        g_DynUpdtStatus->DuLib = NULL;
        SetLastError (rc);
        return FALSE;
    }

    return TRUE;
}



#ifndef UNICODE

BOOL
pLoadWin9xDuSupport (
    VOID
    )
{
    if (!UpgradeSupport.DllModuleHandle) {
        return FALSE;
    }

    g_DynUpdtStatus->Win9xGetIncompDrvs = (PWIN9XGETINCOMPDRVS)
            GetProcAddress (UpgradeSupport.DllModuleHandle, "Win9xGetIncompDrvs");
    g_DynUpdtStatus->Win9xReleaseIncompDrvs = (PWIN9XRELEASEINCOMPDRVS)
            GetProcAddress (UpgradeSupport.DllModuleHandle, "Win9xReleaseIncompDrvs");
    if (!g_DynUpdtStatus->Win9xGetIncompDrvs) {
        DynUpdtDebugLog (
            Winnt32LogError,
            TEXT("Winnt32DuIsSupported: %1 is missing in the upgrade support module"),
            0,
            "Win9xGetIncompDrvs"
            );
        return FALSE;
    }

    return TRUE;
}

#endif


BOOL
pInitSupportLibs (
    VOID
    )
{
    return (Winnt32Restarted () || pInitializeSupport (S_DUCTRL_DLL, pLoadDuLib, TRUE)) &&
           pInitializeSupport (S_HWDB_DLL, pLoadHwdbLib, FALSE) &&
           g_DynUpdtStatus->HwdbInitialize (g_DynUpdtStatus->TempDir);
}


BOOL
pInitNtPnpDb (
    IN      BOOL AllowRebuild
    )
{
    TCHAR hwdbPath[MAX_PATH];
    BOOL b = TRUE;

    if (!FindPathToWinnt32File (S_HWCOMP_DAT, hwdbPath, MAX_PATH)) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("pInitNtPnpDb: %1 not found"), 0, S_HWCOMP_DAT);
        b = FALSE;
    }
    MYASSERT (g_DynUpdtStatus->HwdbInitialize);
    if (b && !g_DynUpdtStatus->HwdbInitialize (g_DynUpdtStatus->TempDir)) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("pInitNtPnpDb: HwdbInitialize(%1) FAILED"), 0, g_DynUpdtStatus->TempDir);
        b = FALSE;
    }
    MYASSERT (g_DynUpdtStatus->HwdbOpen);
    if (b) {
        g_DynUpdtStatus->HwdbDatabase = g_DynUpdtStatus->HwdbOpen (hwdbPath);
        if (!g_DynUpdtStatus->HwdbDatabase) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("pInitNtPnpDb: HwdbOpen(%1) FAILED"), 0, hwdbPath);
            b = FALSE;
        }
    }
    if (!b && AllowRebuild) {
        //
        // just rebuild the database in memory
        //
        g_DynUpdtStatus->HwdbDatabase = g_DynUpdtStatus->HwdbOpen (NULL);
        if (g_DynUpdtStatus->HwdbDatabase) {
            b = g_DynUpdtStatus->HwdbAppendInfs (g_DynUpdtStatus->HwdbDatabase, NativeSourcePaths[0], NULL, NULL);
            if (b) {
                DynUpdtDebugLog (Winnt32LogWarning, TEXT("pInitNtPnpDb: PnP database was successfully rebuilt"), 0);
                //
                // also try to save the database
                //
                if (g_DynUpdtStatus->HwdbFlush) {
                    BuildPath (hwdbPath, NativeSourcePaths[0], S_HWCOMP_DAT);
                    g_DynUpdtStatus->HwdbFlush (g_DynUpdtStatus->HwdbDatabase, hwdbPath);
                }
            }
        }
    }

    return b;
}


BOOL
IsNetConnectivityAvailable (
    VOID
    )
{
#ifdef UNICODE
    return TRUE;
#else
    BOOL (*pfnWin9xAnyNetDevicePresent) (VOID);

    if (UpgradeSupport.DllModuleHandle) {
        (FARPROC)pfnWin9xAnyNetDevicePresent = GetProcAddress (UpgradeSupport.DllModuleHandle, "Win9xAnyNetDevicePresent");
        if (pfnWin9xAnyNetDevicePresent) {
            return pfnWin9xAnyNetDevicePresent();
        }
    }
    return TRUE;
#endif
}


BOOL
DynamicUpdateIsSupported (
    IN      HWND ParentWnd
    )
{
    DWORD rc;

    if (g_DynUpdtStatus->Disabled) {
        return FALSE;
    }

    //
    // disable this for DTC
    //
    if (ProductFlavor == DATACENTER_PRODUCTTYPE) {
        return FALSE;
    }

    if (AnyBlockingCompatibilityItems ()) {
        //
        // no point in supporting DU; setup will stop anyway
        //
        return FALSE;
    }

    CleanUpOldLocalSources (ParentWnd);
    if (!InspectFilesystems (ParentWnd)) {
        DynUpdtDebugLog (Winnt32LogWarning, TEXT("InspectFilesystems blocks DU"), 0);
        return FALSE;
    }
    if (!EnoughMemory (ParentWnd, TRUE)) {
        return FALSE;
    }
    if (!FindLocalSourceAndCheckSpace (ParentWnd, TRUE, DYN_DISKSPACE_PADDING)) {
        DynUpdtDebugLog (Winnt32LogWarning, TEXT("Not enough disk space blocks DU"), 0);
        return FALSE;
    }

    if (!g_DynUpdtStatus->SupportQueried) {
        g_DynUpdtStatus->SupportQueried = TRUE;

        if (g_DynUpdtStatus->DynamicUpdatesSource[0]) {
            g_DynUpdtStatus->SupportPresent = TRUE;
        } else {
            g_DynUpdtStatus->SupportPresent =
                Winnt32DuIsSupported () &&
                IsNetConnectivityAvailable ();

            if (!g_DynUpdtStatus->SupportPresent) {
                rc = GetLastError ();
                DynamicUpdateUninitialize ();
                SetLastError (rc);
            }
        }
    }

    return g_DynUpdtStatus->SupportPresent;
}


BOOL
DynamicUpdateInitDownload (
    IN      HWND hNotifyWnd
    )
{
    DYNUPDT_INIT dynUpdtInit;

    MYASSERT (!g_DynUpdtStatus->Disabled);
    MYASSERT (!Winnt32Restarted ());

    MyDelnode (g_DynUpdtStatus->WorkingDir);
    BuildPath (g_DynUpdtStatus->TempDir, g_DynUpdtStatus->WorkingDir, S_SUBDIRNAME_TEMP);
    if (CreateMultiLevelDirectory (g_DynUpdtStatus->TempDir) != ERROR_SUCCESS) {
        DynUpdtDebugLog (
            Winnt32LogError,
            TEXT("DynamicUpdateInitDownload: CreateMultiLevelDirectory failed"),
            0
            );
        return FALSE;
    }
    if (!pGetTargetInfo (
            &g_DynUpdtStatus->TargetOsVersion,
            NULL,
            0,
            &g_DynUpdtStatus->TargetLCID
            )) {
        return FALSE;
    }
    dynUpdtInit.TargetOsVersion = &g_DynUpdtStatus->TargetOsVersion;
    dynUpdtInit.TargetPlatform = g_DynUpdtStatus->TargetPlatform;
    dynUpdtInit.TargetLCID = g_DynUpdtStatus->TargetLCID;
    dynUpdtInit.Upgrade = Upgrade;
    dynUpdtInit.SourceDirs = info.SourceDirectories;
    dynUpdtInit.SourceDirsCount = SourceCount;
    dynUpdtInit.Unattend = UnattendSwitchSpecified;
    dynUpdtInit.AnswerFile = UnattendedScriptFile;
    dynUpdtInit.ProgressWindow = hNotifyWnd;
    dynUpdtInit.DownloadRoot = g_DynUpdtStatus->WorkingDir;
    dynUpdtInit.TempDir = g_DynUpdtStatus->TempDir;
    return Winnt32DuInitialize (&dynUpdtInit);
}


BOOL
DynamicUpdateStart (
    OUT     PDWORD TotalEstimatedTime,
    OUT     PDWORD TotalEstimatedSize
    )
{
    if (g_DynUpdtStatus->Disabled) {
        return FALSE;
    }

    return Winnt32DuInitiateGetUpdates (TotalEstimatedTime, TotalEstimatedSize);
}

VOID
DynamicUpdateCancel (
    VOID
    )
{
    if (g_DynUpdtStatus->Disabled) {
        return;
    }

    Winnt32DuCancel ();
}


BOOL
DynamicUpdateProcessFiles (
    OUT     PBOOL StopSetup
    )
{
    if (g_DynUpdtStatus->Disabled) {
        return TRUE;
    }

    return Winnt32DuProcessFiles (StopSetup);
}

BOOL
DynamicUpdateWriteParams (
    IN      PCTSTR ParamsFile
    )
{
    return Winnt32DuWriteParams (ParamsFile);
}

VOID
DynamicUpdateUninitialize (
    VOID
    )
{
    if (g_DynUpdtStatus->Disabled) {
        return;
    }

    Winnt32DuUninitialize ();
}


BOOL
DynamicUpdatePrepareRestart (
    VOID
    )
{
    PCTSTR prevCmdLine;
    DWORD size;
#ifdef _X86_
    TCHAR reportNum[16];
#endif

    if (g_DynUpdtStatus->Disabled) {
        return FALSE;
    }

#define S_ARG_RESTART        TEXT("Restart")

    if (!UnattendedOperation) {
        //
        // build the restart answer file
        //
        BuildPath (g_DynUpdtStatus->RestartAnswerFile, g_DynUpdtStatus->WorkingDir, S_RESTART_TXT);

    #ifdef _X86_
        wsprintf (reportNum, TEXT("%u"), g_UpgradeReportMode);
    #endif

        //
        // write data to the restart answer file
        //
        if (!WritePrivateProfileString (
                WINNT_UNATTENDED,
                ISNT() ? WINNT_D_NTUPGRADE : WINNT_D_WIN95UPGRADE,
                Upgrade ? WINNT_A_YES : WINNT_A_NO,
                g_DynUpdtStatus->RestartAnswerFile
                ) ||
            ProductId[0] && !WritePrivateProfileString (
                WINNT_USERDATA,
                WINNT_US_PRODUCTKEY,
                ProductId,
                g_DynUpdtStatus->RestartAnswerFile
                ) ||
            !WritePrivateProfileString (
                WINNT_UNATTENDED,
                WINNT_U_DYNAMICUPDATESHARE,
                g_DynUpdtStatus->DynamicUpdatesSource,
                g_DynUpdtStatus->RestartAnswerFile
                ) ||
    #ifdef _X86_
            !WritePrivateProfileString (
                WINNT_UNATTENDED,
                WINNT_D_REPORTMODE,
                reportNum,
                g_DynUpdtStatus->RestartAnswerFile
                ) ||
    #endif
            (ForceNTFSConversion &&
            !WritePrivateProfileString (
                WINNT_UNATTENDED,
                TEXT("ForceNTFSConversion"),
                WINNT_A_YES,
                g_DynUpdtStatus->RestartAnswerFile
                )) ||
            !SaveAdvancedOptions (
                g_DynUpdtStatus->RestartAnswerFile
                ) ||
            !SaveLanguageOptions (
                g_DynUpdtStatus->RestartAnswerFile
                ) ||
            !SaveAccessibilityOptions (
                g_DynUpdtStatus->RestartAnswerFile
                )
                ) {
            return FALSE;
        }
    }

    prevCmdLine = GetCommandLine ();
    size = (lstrlen (prevCmdLine) + 1 + 1) * sizeof (TCHAR) + sizeof (S_ARG_RESTART) +
        (UnattendedOperation ? 0 : (1 + lstrlen (g_DynUpdtStatus->RestartAnswerFile)) * sizeof (TCHAR));
    g_DynUpdtStatus->RestartCmdLine = HeapAlloc (GetProcessHeap (), 0, size);
    if (!g_DynUpdtStatus->RestartCmdLine) {
        return FALSE;
    }
    wsprintf (
        g_DynUpdtStatus->RestartCmdLine,
        UnattendedOperation ? TEXT("%s /%s") : TEXT("%s /%s:%s"),
        prevCmdLine,
        S_ARG_RESTART,
        g_DynUpdtStatus->RestartAnswerFile
        );

    return TRUE;
}


BOOL
pComputeChecksum (
    IN      PCTSTR FileName,
    OUT     PDWORD Chksum
    )
{
    DWORD chksum, size, dwords, bytes;
    HANDLE hFile, hMap;
    PVOID viewBase;
    PDWORD base, limit;
    PBYTE base2;
    DWORD rc;

    rc = MapFileForRead (FileName, &size, &hFile, &hMap, &viewBase);
    if (rc != ERROR_SUCCESS) {
        SetLastError (rc);
        return FALSE;
    }

    dwords = size / sizeof (DWORD);
    base = (PDWORD)viewBase;
    limit = base + dwords;
    chksum = 0;
    while (base < limit) {
        chksum += *base;
        base++;
    }
    bytes = size % sizeof (DWORD);
    base2 = (PBYTE)base;
    while (bytes) {
        chksum += *base2;
        base2++;
        bytes--;
    }

    UnmapFile (hMap, viewBase);
    CloseHandle (hFile);

    *Chksum = chksum;
    return TRUE;
}


BOOL
pGetFiletimeStamps (
    IN      PCTSTR FileName,
    OUT     PFILETIME CreationTime,
    OUT     PFILETIME LastWriteTime
    )
{
    WIN32_FIND_DATA fd;
    HANDLE h;

    h = FindFirstFile (FileName, &fd);
    if (h == INVALID_HANDLE_VALUE) {
        return FALSE;
    }
    FindClose (h);
    *CreationTime = fd.ftCreationTime;
    *LastWriteTime = fd.ftLastWriteTime;
    return TRUE;
}

BOOL
pSaveLastDownloadInfo (
    VOID
    )
{
    SYSTEMTIME currentTime;
    DWORD chksum;
    FILETIME ftCreationTime;
    FILETIME ftLastWriteTime;
    ULONGLONG data[2];
    DWORD rc;
    HKEY key;
    TCHAR keyName[MAX_PATH];
    TCHAR filePath[MAX_PATH];
    PTSTR p;

    //
    // we always want to get to the CD dosnet.inf (in the same directory as winnt32.exe)
    //
    if (!GetModuleFileName (NULL, filePath, MAX_PATH)) {
        return FALSE;
    }
    p = _tcsrchr (filePath, TEXT('\\'));
    if (!p) {
        return FALSE;
    }
    lstrcpy (p + 1, InfName);

    GetCurrentWinnt32RegKey (keyName, MAX_PATH);
    ConcatenatePaths (keyName, WINNT_U_DYNAMICUPDATESHARE, MAX_PATH);

    rc = RegCreateKey (HKEY_LOCAL_MACHINE, keyName, &key);
    if (rc == ERROR_SUCCESS) {
        GetSystemTime (&currentTime);
        rc = RegSetValueEx (
                key,
                TEXT("LastDownloadTime"),
                0,
                REG_BINARY,
                (CONST BYTE *) (&currentTime),
                sizeof (currentTime)
                );

        if (rc == ERROR_SUCCESS) {
            rc = RegSetValueEx (
                    key,
                    TEXT(""),
                    0,
                    REG_SZ,
                    (CONST BYTE *) g_DynUpdtStatus->DynamicUpdatesSource,
                    (lstrlen (g_DynUpdtStatus->DynamicUpdatesSource) + 1) * sizeof (TCHAR)
                    );
        }

        if (rc == ERROR_SUCCESS) {

            if (pComputeChecksum (filePath, &chksum)) {
                rc = RegSetValueEx (
                        key,
                        TEXT("Checksum"),
                        0,
                        REG_DWORD,
                        (CONST BYTE *) (&chksum),
                        sizeof (chksum)
                        );
            }
        }

        if (rc == ERROR_SUCCESS) {
            if (pGetFiletimeStamps (filePath, &ftCreationTime, &ftLastWriteTime)) {
                data[0] = ((ULONGLONG)ftCreationTime.dwHighDateTime << 32) | (ULONGLONG)ftCreationTime.dwLowDateTime;
                data[1] = ((ULONGLONG)ftLastWriteTime.dwHighDateTime << 32 ) | (ULONGLONG)ftLastWriteTime.dwLowDateTime;
                rc = RegSetValueEx (
                        key,
                        TEXT("TimeStamp"),
                        0,
                        REG_BINARY,
                        (CONST BYTE *)data,
                        sizeof (data)
                        );
            }
        }

        RegCloseKey (key);
    }

    if (rc != ERROR_SUCCESS) {
        SetLastError (rc);
    }
    return rc == ERROR_SUCCESS;
}

BOOL
pGetRecentDUShare (
    IN      DWORD MaxElapsedSeconds
    )
{
    SYSTEMTIME lastDownload, currentTime;
    ULONGLONG lastDownloadIn100Ns, currentTimeIn100Ns;
    ULONGLONG difference;
    DWORD rc, size, type;
    HKEY key = NULL;
    BOOL b = FALSE;
    PTSTR duShare = NULL;
    TCHAR keyName[MAX_PATH];
    FILETIME ftCreationTime;
    FILETIME ftLastWriteTime;
    ULONGLONG data[2], storedData[2];
    DWORD chksum, storedChksum;
    TCHAR filePath[MAX_PATH];
    PTSTR p;

    if (!GetModuleFileName (NULL, filePath, MAX_PATH)) {
        return FALSE;
    }
    p = _tcsrchr (filePath, TEXT('\\'));
    if (!p) {
        return FALSE;
    }
    lstrcpy (p + 1, InfName);

    GetCurrentWinnt32RegKey (keyName, MAX_PATH);
    ConcatenatePaths (keyName, WINNT_U_DYNAMICUPDATESHARE, MAX_PATH);

    rc = RegOpenKeyEx (
            HKEY_LOCAL_MACHINE,
            keyName,
            0,
            KEY_READ,
            &key
            );

    if (rc == ERROR_SUCCESS) {
        size = sizeof (lastDownload);
        rc = RegQueryValueEx (
                key,
                TEXT("LastDownloadTime"),
                NULL,
                &type,
                (PBYTE) (&lastDownload),
                &size
                );
    }

    if (rc == ERROR_SUCCESS && type == REG_BINARY && size == sizeof (lastDownload)) {
        //
        // Compare current time to report time
        //

        GetSystemTime (&currentTime);

        lastDownloadIn100Ns = SystemTimeToFileTime64 (&lastDownload);
        currentTimeIn100Ns = SystemTimeToFileTime64 (&currentTime);

        if (currentTimeIn100Ns > lastDownloadIn100Ns) {
            //
            // Compute difference in seconds
            //
            difference = currentTimeIn100Ns - lastDownloadIn100Ns;
            difference /= (10 * 1000 * 1000);

            if (difference < MaxElapsedSeconds) {
                b = TRUE;
            }
        }
    }

    if (b) {
        b = FALSE;
        rc = RegQueryValueEx (
                key,
                TEXT(""),
                NULL,
                &type,
                NULL,
                &size
                );
        if (rc == ERROR_SUCCESS && type == REG_SZ && size > 0 && size <= sizeof (g_DynUpdtStatus->DynamicUpdatesSource)) {
            duShare = MALLOC (size);
            if (duShare) {
                rc = RegQueryValueEx (
                        key,
                        TEXT(""),
                        NULL,
                        NULL,
                        (LPBYTE)duShare,
                        &size
                        );
                if (rc == ERROR_SUCCESS && pDoesDirectoryExist (duShare)) {
                    b = TRUE;
                } else {
                    FREE (duShare);
                    duShare = NULL;
                }
            }
        }
    }

    if (b) {
        b = FALSE;
        if (pGetFiletimeStamps (filePath, &ftCreationTime, &ftLastWriteTime)) {
            rc = RegQueryValueEx (
                        key,
                        TEXT("TimeStamp"),
                        0,
                        &type,
                        (LPBYTE)storedData,
                        &size
                        );
            if (rc == ERROR_SUCCESS && type == REG_BINARY) {
                data[0] = ((ULONGLONG)ftCreationTime.dwHighDateTime << 32) | (ULONGLONG)ftCreationTime.dwLowDateTime;
                data[1] = ((ULONGLONG)ftLastWriteTime.dwHighDateTime << 32 ) | (ULONGLONG)ftLastWriteTime.dwLowDateTime;
                if (data[0] == storedData[0] && data[1] == storedData[1]) {
                    b = TRUE;
                }
            }
        }
    }

    if (b) {
        b = FALSE;
        if (pComputeChecksum (filePath, &chksum)) {
            rc = RegQueryValueEx (
                    key,
                    TEXT("Checksum"),
                    NULL,
                    &type,
                    (LPBYTE)&storedChksum,
                    &size
                    );
            if (rc == ERROR_SUCCESS && type == REG_DWORD && storedChksum == chksum) {
                b = TRUE;
            }
        }
    }

    if (!b && duShare) {
        FREE (duShare);
        duShare = NULL;
    }

    if (duShare) {
        MYASSERT (b);
        MYASSERT (!g_DynUpdtStatus->DynamicUpdatesSource[0]);
        lstrcpy (g_DynUpdtStatus->DynamicUpdatesSource, duShare);
        RemoveTrailingWack (g_DynUpdtStatus->DynamicUpdatesSource);
        g_DynUpdtStatus->UserSpecifiedUpdates = TRUE;
    }

    if (key) {
        RegCloseKey (key);
    }

    return b;
}


BOOL
DynamicUpdateInitialize (
    VOID
    )
{
    if (g_DynUpdtStatus->Disabled) {
        return TRUE;
    }

    if (!MyGetWindowsDirectory (g_DynUpdtStatus->WorkingDir, MAX_PATH)) {
        return FALSE;
    }
    ConcatenatePaths (g_DynUpdtStatus->WorkingDir, S_DOWNLOAD_ROOT, MAX_PATH);
    BuildPath (g_DynUpdtStatus->TempDir, g_DynUpdtStatus->WorkingDir, S_SUBDIRNAME_TEMP);

    if (!CheckUpgradeOnly && !g_DynUpdtStatus->UserSpecifiedUpdates) {
        if (pGetRecentDUShare (MAX_UPGCHK_ELAPSED_SECONDS)) {
            g_DynUpdtStatus->PreserveWorkingDir = TRUE;
            DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Using recent share %1"), 0, g_DynUpdtStatus->DynamicUpdatesSource);
        }
    }

    if (!g_DynUpdtStatus->PreserveWorkingDir && !Winnt32Restarted ()) {
        MyDelnode (g_DynUpdtStatus->WorkingDir);
    }

    if (g_DynUpdtStatus->UserSpecifiedUpdates) {
        BuildPath (g_DynUpdtStatus->DriversSource, g_DynUpdtStatus->DynamicUpdatesSource, S_SUBDIRNAME_DRIVERS);
    } else {
        BuildPath (g_DynUpdtStatus->DriversSource, g_DynUpdtStatus->WorkingDir, S_SUBDIRNAME_DRIVERS);
    }
    BuildPath (g_DynUpdtStatus->SelectedDrivers, g_DynUpdtStatus->WorkingDir, S_SUBDIRNAME_DRIVERS);
    if (Winnt32Restarted ()) {
        BuildPath (g_DynUpdtStatus->Winnt32Path, g_DynUpdtStatus->WorkingDir, S_SUBDIRNAME_WINNT32);
    } else {
        BuildPath (
            g_DynUpdtStatus->Winnt32Path,
            g_DynUpdtStatus->UserSpecifiedUpdates ?
                g_DynUpdtStatus->DynamicUpdatesSource :
                g_DynUpdtStatus->WorkingDir,
            S_SUBDIRNAME_WINNT32
            );
    }
    if (!pGetTargetInfo (
            NULL,
            g_DynUpdtStatus->TargetPlatform,
            sizeof (g_DynUpdtStatus->TargetPlatform) / sizeof (TCHAR),
            NULL
            )) {
        return FALSE;
    }
    return TRUE;
}


PTSTR
GetFileExtension (
    IN      PCTSTR FileSpec
    )
{
    PTSTR p;

    p = _tcsrchr (FileSpec, TEXT('.'));
    if (p && _tcschr (p, TEXT('\\'))) {
        p = NULL;
    }
    return p;
}


VOID
BuildSifName (
    IN      PCTSTR CabName,
    OUT     PTSTR SifName
    )
{
    PTSTR p;

    lstrcpy (SifName, CabName);
    p = GetFileExtension (SifName);
    if (!p) {
        p = _tcschr (SifName, 0);
    }
    lstrcpy (p, TEXT(".sif"));
}


BOOL
WINAPI
Winnt32QueryCallback (
    IN      DWORD SetupQueryId,
    IN      PVOID InData,
    IN      DWORD InDataSize,
    IN OUT  PVOID OutData,          OPTIONAL
    IN OUT  PDWORD OutDataSize
    )
{
    BOOL b = FALSE;
    BOOL bException = FALSE;

    switch (SetupQueryId) {
    case SETUPQUERYID_PNPID:
        {
            PPNPID_INFO p;
            PTSTR listPnpIds = NULL;

            if (!OutData ||
                !OutDataSize ||
                *OutDataSize < sizeof (PNPID_INFO)
                ) {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }
            if (!g_DynUpdtStatus->HwdbHasAnyDriver) {
                SetLastError (ERROR_INVALID_FUNCTION);
                break;
            }
            __try {
                p = (PPNPID_INFO)OutData;
                if (g_DynUpdtStatus->HwdbDatabase) {
#ifdef UNICODE
                    listPnpIds = MultiSzAnsiToUnicode ((PCSTR)InData);
#else
                    listPnpIds = (PSTR)InData;
#endif
                    p->Handled = (*g_DynUpdtStatus->HwdbHasAnyDriver) (
                                        g_DynUpdtStatus->HwdbDatabase,
                                        listPnpIds,
                                        &p->Unsupported
                                        );
                } else {
                    //
                    // disable all driver downloads by doing this
                    //
                    p->Handled = TRUE;
                }
            }
            __except (EXCEPTION_EXECUTE_HANDLER) {
                SetLastError (GetExceptionCode());
                DynUpdtDebugLog (Winnt32LogError, TEXT("Winnt32QueryCallback: HwdbHasAnyDriver threw an exception"), 0);
                bException = TRUE;
                p->Handled = TRUE;
            }

            if (bException) {
                __try {
                    //
                    // bad string passed back, or some internal error
                    // try to print the string
                    //
                    if (listPnpIds) {
                        PTSTR multisz = CreatePrintableString (listPnpIds);
                        DynUpdtDebugLog (Winnt32LogError, TEXT(" - The string was %1"), 0, multisz);
                        FREE (multisz);
                    }
                }
                __except (EXCEPTION_EXECUTE_HANDLER) {
                    DynUpdtDebugLog (Winnt32LogError, TEXT(" - Bad string"), 0);
                }
            }

#ifdef UNICODE
            if (listPnpIds) {
                FREE (listPnpIds);
            }
#endif

            b = TRUE;
        }
        break;

    case SETUPQUERYID_DOWNLOADDRIVER:
        {
            if (!OutData ||
                !OutDataSize ||
                *OutDataSize < sizeof (BOOL)
                ) {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }
            b = TRUE;
        }
        break;

    }

    return b;
}



BOOL
WINAPI
Winnt32DuIsSupported (
    VOID
    )
{
    BOOL b;

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Enter Winnt32DuIsSupported"), 0);

#ifndef UNICODE
    if (!pLoadWin9xDuSupport ()) {
        DynUpdtDebugLog (
            Winnt32LogWarning,
            TEXT("Winnt32DuIsSupported: %1 support module not loaded; no drivers will be downloaded"),
            0,
            "w95upg.dll"
            );
    }
#endif

    TRY {
        b = pInitSupportLibs () &&
            (Winnt32Restarted () || g_DynUpdtStatus->DuIsSupported ());
    }
    EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
        SetLastError (_exception_code ());
        DynUpdtDebugLog (Winnt32LogError, TEXT("Winnt32DuIsSupported: an exception occured"), 0);
    }
    END_EXCEPT

#ifndef UNICODE
    if (!b) {
        g_DynUpdtStatus->Win9xGetIncompDrvs = NULL;
        g_DynUpdtStatus->Win9xReleaseIncompDrvs = NULL;
    }
#endif

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Leave Winnt32DuIsSupported (%1!d!)"), 0, b);

    return b;
}


BOOL
WINAPI
Winnt32DuInitialize (
    IN      PDYNUPDT_INIT InitData
    )
{
    DWORD rc;
    BOOL b = FALSE;

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Enter Winnt32DuInitialize"), 0);

    __try {
        MYASSERT (InitData);
        MYASSERT (InitData->TempDir);
        MYASSERT (g_DynUpdtStatus->DuInitialize);
        MYASSERT (!Winnt32Restarted ());

        if (CreateMultiLevelDirectory (InitData->TempDir) != ERROR_SUCCESS) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Winnt32DuInitialize: CreateDirectory(%1) FAILED"), 0, InitData->TempDir);
            __leave;
        }

        //
        // initialize the Whistler PNP database
        //
        if (!g_DynUpdtStatus->HwdbDatabase) {
            //
            // ignore db load error
            //
            pInitNtPnpDb (FALSE);
        }

        TRY {
            //
            // if a download source already exists, no need to initialize the control
            // since no download will be really necessary
            //
            MYASSERT (!g_DynUpdtStatus->DynamicUpdatesSource[0]);
            g_DynUpdtStatus->Connection = (*g_DynUpdtStatus->DuInitialize) (
                                                InitData->DownloadRoot,
                                                InitData->TempDir,
                                                InitData->TargetOsVersion,
                                                InitData->TargetPlatform,
                                                InitData->TargetLCID,
                                                InitData->Unattend,
                                                InitData->Upgrade,
                                                Winnt32QueryCallback
                                                );
            if (g_DynUpdtStatus->Connection == INVALID_HANDLE_VALUE) {
                DynUpdtDebugLog (
                    Winnt32LogError,
                    TEXT("DuInitialize FAILED"),
                    0
                    );
                __leave;
            }
            g_DynUpdtStatus->ProgressWindow = InitData->ProgressWindow;
            b = TRUE;
        }
        EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
            SetLastError (_exception_code ());
            DynUpdtDebugLog (Winnt32LogError, TEXT("Winnt32DuInitialize: an exception occured"), 0);
        }
        END_EXCEPT

    }
    __finally {
        if (!b) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Winnt32DuInitialize FAILED"), 0);
        }
    }

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Leave Winnt32DuInitialize (%1!d!)"), 0, b);

    return b;
}


BOOL
WINAPI
Winnt32DuInitiateGetUpdates (
    OUT     PDWORD TotalEstimatedTime,
    OUT     PDWORD TotalEstimatedSize
	)
{
    BOOL b = FALSE;
#ifndef UNICODE
    PSTR* incompWin9xDrivers;
    PSTRINGLIST listEntry;
    PCSTR* q;
#endif

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Enter Winnt32DuInitiateGetUpdates"), 0);

    if (g_DynUpdtStatus->Connection != INVALID_HANDLE_VALUE &&
        g_DynUpdtStatus->DuDoDetection &&
        g_DynUpdtStatus->DuBeginDownload
        ) {

        TRY {

#ifdef UNICODE
            b = (*g_DynUpdtStatus->DuDoDetection) (g_DynUpdtStatus->Connection, TotalEstimatedTime, TotalEstimatedSize);
            if (!b) {
                DynUpdtDebugLog (
                    DynUpdtLogLevel,
                    TEXT("DuDoDetection returned FALSE; no files will be downloaded"),
                    0
                    );
            }
#else
            b = TRUE;
            g_DynUpdtStatus->IncompatibleDriversCount = 0;
            if (g_DynUpdtStatus->Win9xGetIncompDrvs) {
                //
                // let the upgrade module do detection on Win95
                //
                b = (*g_DynUpdtStatus->Win9xGetIncompDrvs) (&incompWin9xDrivers);
                if (b) {
                    b = (*g_DynUpdtStatus->DuQueryUnsupDrvs) (
                                                g_DynUpdtStatus->Connection,
                                                incompWin9xDrivers,
                                                TotalEstimatedTime,
                                                TotalEstimatedSize
                                                );
                    if (incompWin9xDrivers) {
                        for (q = incompWin9xDrivers; *q; q++) {
                            g_DynUpdtStatus->IncompatibleDriversCount++;
                        }
                    }
                    if (g_DynUpdtStatus->Win9xReleaseIncompDrvs) {
                        (*g_DynUpdtStatus->Win9xReleaseIncompDrvs) (incompWin9xDrivers);
                    }
                    if (!b) {
                        DynUpdtDebugLog (
                            DynUpdtLogLevel,
                            TEXT("DuQueryUnsupportedDrivers returned FALSE; no files will be downloaded"),
                            0
                            );
                    }
                } else {
                    DynUpdtDebugLog (
                        DynUpdtLogLevel,
                        TEXT("Win9xGetIncompDrvs returned FALSE; no files will be downloaded"),
                        0
                        );
                }
            }
#endif

            if (b) {
                b = (*g_DynUpdtStatus->DuBeginDownload) (g_DynUpdtStatus->Connection, g_DynUpdtStatus->ProgressWindow);
                if (!b) {
                    DynUpdtDebugLog (
                        DynUpdtLogLevel,
                        TEXT("DuBeginDownload returned FALSE; no files will be downloaded"),
                        0
                        );
                }
            }
        }
        EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
            SetLastError (_exception_code ());
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("Winnt32DuInitiateGetUpdates: an exception occured; no files will be downloaded"),
                0
                );
        }
        END_EXCEPT
    } else {
        SetLastError (ERROR_INVALID_FUNCTION);
    }

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Leave Winnt32DuInitiateGetUpdates (%1!d!)"), 0, b);

    return b;
}


VOID
WINAPI
Winnt32DuCancel (
    VOID
    )
{
    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Enter Winnt32DuCancel"), 0);

    TRY {
        if (g_DynUpdtStatus->Connection != INVALID_HANDLE_VALUE &&
            g_DynUpdtStatus->DuAbortDownload
            ) {
            (*g_DynUpdtStatus->DuAbortDownload) (g_DynUpdtStatus->Connection);
        }
        //
        // BUGBUG - is this definitive?
        //
    }
    EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
        SetLastError (_exception_code ());
        DynUpdtDebugLog (Winnt32LogError, TEXT("Winnt32DuCancel: an exception occured"), 0);
    }
    END_EXCEPT

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Leave Winnt32DuCancel"), 0);
}


BOOL
WINAPI
Winnt32DuProcessFiles (
    OUT     PBOOL StopSetup
    )
{
    BOOL b;

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Enter Winnt32DuProcessFiles"), 0);

    b = ProcessDownloadedFiles (StopSetup);

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Leave Winnt32DuProcessFiles (%1!d!)"), 0, b);

    return b;
}


BOOL
WINAPI
Winnt32DuWriteParams (
    IN      PCTSTR ParamsFile
    )
{
    PSDLIST p;
    PSTRINGLIST q;
    DWORD len1, len2;
    PTSTR pathList1 = NULL, pathList2 = NULL;
    PTSTR append1, append2;
    BOOL b = TRUE;

    if (!DynamicUpdateSuccessful ()) {
        DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Winnt32DuWriteParams: disabled because DU did not succeed"), 0);
        return TRUE;
    }

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Enter Winnt32DuWriteParams"), 0);

    //
    // store paths to all downloaded drivers in a key in the answer file,
    // so later on Textmode Setup (or GUI setup) will append this list to the OemPnPDriversPath
    //
    if (g_DynUpdtStatus->NewDriversList) {

        len1 = len2 = 0;
        for (p = g_DynUpdtStatus->NewDriversList; p; p = p->Next) {
            if (p->Data) {
                len1 += lstrlen (p->String) + 1;
            } else {
                len2 += lstrlen (p->String) + 1;
            }
        }

        if (len1) {
            pathList1 = (PTSTR) MALLOC (len1 * sizeof (TCHAR));
            if (!pathList1) {
                b = FALSE;
                goto exit;
            }
            *pathList1 = 0;
            append1 = pathList1;
        }
        if (len2) {
            pathList2 = (PTSTR) MALLOC (len2 * sizeof (TCHAR));
            if (!pathList2) {
                FREE (pathList1);
                b = FALSE;
                goto exit;
            }
            *pathList2 = 0;
            append2 = pathList2;
        }

        for (p = g_DynUpdtStatus->NewDriversList; p; p = p->Next) {
            if (p->Data) {
                if (append1 != pathList1) {
                    *append1++ = TEXT(',');
                }
                lstrcpy (append1, p->String);
                append1 = _tcschr (append1, 0);
            } else {
                if (append2 != pathList2) {
                    *append2++ = TEXT(',');
                }
                lstrcpy (append2, p->String);
                append2 = _tcschr (append2, 0);
            }
        }

        if (len1) {
            if (!WritePrivateProfileString (
                    WINNT_SETUPPARAMS,
                    WINNT_SP_DYNUPDTADDITIONALGUIDRIVERS,
                    pathList1,
                    ParamsFile
                    )) {
                b = FALSE;
            }
        }
        if (b && len2) {
            if (!WritePrivateProfileString (
                    WINNT_SETUPPARAMS,
                    WINNT_SP_DYNUPDTADDITIONALPOSTGUIDRIVERS,
                    pathList2,
                    ParamsFile
                    )) {
                b = FALSE;
            }
        }

        if (pathList1) {
            FREE (pathList1);
        }
        if (pathList2) {
            FREE (pathList2);
        }

        if (b && g_DynUpdtStatus->GuidrvsInfSource[0]) {
            if (!WritePrivateProfileString (
                    WINNT_SETUPPARAMS,
                    WINNT_SP_DYNUPDTDRIVERINFOFILE,
                    g_DynUpdtStatus->GuidrvsInfSource,
                    ParamsFile
                    )) {
                b = FALSE;
            }
        }
    }

    //
    // store paths to all downloaded BOOT drivers in a key in the answer file,
    // so later on Textmode Setup will append this to the boot drivers list
    //
    if (b && g_DynUpdtStatus->BootDriverPathList) {

        len1 = 0;
        for (q = g_DynUpdtStatus->BootDriverPathList; q; q = q->Next) {
            len1 += lstrlen (q->String) + 1;
        }

        pathList1 = (PTSTR) MALLOC (len1 * sizeof (TCHAR));
        if (!pathList1) {
            b = FALSE;
            goto exit;
        }

        *pathList1 = 0;
        append1 = pathList1;

        for (q = g_DynUpdtStatus->BootDriverPathList; q; q = q->Next) {
            if (append1 != pathList1) {
                *append1++ = TEXT(',');
            }
            lstrcpy (append1, q->String);
            append1 = _tcschr (append1, 0);
        }

        if (!WritePrivateProfileString (
                WINNT_SETUPPARAMS,
                WINNT_SP_DYNUPDTBOOTDRIVERPRESENT,
                WINNT_A_YES,
                ParamsFile
                ) ||
            !WritePrivateProfileString (
                WINNT_SETUPPARAMS,
                WINNT_SP_DYNUPDTBOOTDRIVERROOT,
                S_SUBDIRNAME_DRIVERS,
                ParamsFile
                ) ||
            !WritePrivateProfileString (
                WINNT_SETUPPARAMS,
                WINNT_SP_DYNUPDTBOOTDRIVERS,
                pathList1,
                ParamsFile
                )) {
            b = FALSE;
        }

        FREE (pathList1);
    }

    if (b && g_DynUpdtStatus->UpdatesCabTarget[0]) {
        TCHAR buffer[4*MAX_PATH];
        wsprintf (
            buffer,
            TEXT("\"%s\""),
            g_DynUpdtStatus->UpdatesCabTarget
            );
        b = WritePrivateProfileString (
                WINNT_SETUPPARAMS,
                WINNT_SP_UPDATEDSOURCES,
                buffer,
                ParamsFile
                );
    }

    //
    // new assemblies to be installed during GUI setup
    //
    if (b && g_DynUpdtStatus->DuasmsTarget[0]) {
        b = WritePrivateProfileString (
                WINNT_SETUPPARAMS,
                WINNT_SP_UPDATEDDUASMS,
                g_DynUpdtStatus->DuasmsTarget,
                ParamsFile
                );
    }

#ifdef _X86_

    //
    // last but not least, replace the Win9xupg NT side migration dll (w95upgnt.dll)
    // if a new one is available
    //
    if (b && Upgrade && !ISNT() && g_DynUpdtStatus->Winnt32Path[0]) {
        TCHAR source[MAX_PATH];
        TCHAR target[MAX_PATH];

        BuildPath (target, g_DynUpdtStatus->Winnt32Path, TEXT("w95upgnt.dll"));
        if (pDoesFileExist (target)) {
            //
            // check file versions first
            //
            BuildPath (source, NativeSourcePaths[0], TEXT("w95upgnt.dll"));
            if (!IsFileVersionLesser (target, source)) {
                if (_tcsnicmp (
                        g_DynUpdtStatus->Winnt32Path,
                        g_DynUpdtStatus->WorkingDir,
                        lstrlen (g_DynUpdtStatus->WorkingDir)
                        )) {
                    //
                    // copy the file in a local directory first
                    //
                    BuildPath (source, g_DynUpdtStatus->WorkingDir, TEXT("w95upgnt.dll"));
                    if (CopyFile (target, source, FALSE)) {
                        lstrcpy (target, source);
                    } else {
                        //
                        // failed to copy the NT-side upgrade module!
                        // fail the upgrade
                        //
                        DynUpdtDebugLog (
                            Winnt32LogSevereError,
                            TEXT("Failed to copy replacement %1 to %2; upgrade aborted"),
                            0,
                            target,
                            source
                            );
                        b = FALSE;
                    }
                }
                if (b) {
                    b = WritePrivateProfileString (
                            WINNT_WIN95UPG_95_DIR,
                            WINNT_WIN95UPG_NTKEY,
                            target,
                            ParamsFile
                            );
                    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Will use replacement %1 on the NT side of migration"), 0, target);
                }
            }
        }
    }

#endif

    if (b) {
        if (g_DynUpdtStatus->WorkingDir[0]) {
            b = WritePrivateProfileString (
                    WINNT_SETUPPARAMS,
                    WINNT_SP_DYNUPDTWORKINGDIR,
                    g_DynUpdtStatus->WorkingDir,
                    ParamsFile
                    );
        }
    }

    if (b) {
        //
        // flush it to disk
        //
        WritePrivateProfileString (NULL, NULL, NULL, ParamsFile);
    }

exit:
    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Leave Winnt32DuWriteParams (%1!d!)"), 0, b);

    return b;
}


VOID
WINAPI
Winnt32DuUninitialize (
    VOID
    )
{
    DWORD i;
    TCHAR pathPss[MAX_PATH];
    PTSTR p;

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Enter Winnt32DuUninitialize"), 0);

    TRY {
        if (g_DynUpdtStatus->Connection != INVALID_HANDLE_VALUE) {
            if (g_DynUpdtStatus->DuUninitialize) {
                (*g_DynUpdtStatus->DuUninitialize) (g_DynUpdtStatus->Connection);
            }
            g_DynUpdtStatus->Connection = INVALID_HANDLE_VALUE;
        }

        g_DynUpdtStatus->DuIsSupported = NULL;
        g_DynUpdtStatus->ProgressWindow = NULL;
        g_DynUpdtStatus->DuInitialize = NULL;
        g_DynUpdtStatus->DuDoDetection = NULL;
        g_DynUpdtStatus->DuQueryUnsupDrvs = NULL;
        g_DynUpdtStatus->DuBeginDownload = NULL;
        g_DynUpdtStatus->DuAbortDownload = NULL;
        g_DynUpdtStatus->DuUninitialize = NULL;

        if (g_DynUpdtStatus->DuLib) {
            FreeLibrary (g_DynUpdtStatus->DuLib);
            g_DynUpdtStatus->DuLib = NULL;
        }

        if (g_DynUpdtStatus->HwdbDatabase) {
            g_DynUpdtStatus->HwdbClose (g_DynUpdtStatus->HwdbDatabase);
            g_DynUpdtStatus->HwdbDatabase = NULL;
        }

        if (g_DynUpdtStatus->HwdbTerminate) {
            (*g_DynUpdtStatus->HwdbTerminate) ();
        }

        g_DynUpdtStatus->HwdbInitialize = NULL;
        g_DynUpdtStatus->HwdbTerminate = NULL;
        g_DynUpdtStatus->HwdbOpen = NULL;
        g_DynUpdtStatus->HwdbClose = NULL;
        g_DynUpdtStatus->HwdbAppendInfs = NULL;
        g_DynUpdtStatus->HwdbFlush = NULL;
        g_DynUpdtStatus->HwdbHasDriver = NULL;
        g_DynUpdtStatus->HwdbHasAnyDriver = NULL;

        if (g_DynUpdtStatus->HwdbLib) {
            FreeLibrary (g_DynUpdtStatus->HwdbLib);
            g_DynUpdtStatus->HwdbLib = NULL;
        }

#ifndef UNICODE
        g_DynUpdtStatus->Win9xGetIncompDrvs = NULL;
        g_DynUpdtStatus->Win9xReleaseIncompDrvs = NULL;
#endif

        if (g_DynUpdtStatus->WorkingDir[0]) {
            p = _tcsrchr (g_DynUpdtStatus->WorkingDir, TEXT('\\'));
            if (!p) {
                p = g_DynUpdtStatus->WorkingDir;
            }
            lstrcpyn (pathPss, g_DynUpdtStatus->WorkingDir, (INT)(p - g_DynUpdtStatus->WorkingDir + 2));
            lstrcat (pathPss, TEXT("setup.pss"));
            CreateMultiLevelDirectory (pathPss);
            lstrcat (pathPss, p);
            MyDelnode (pathPss);
        }

        if (!DynamicUpdateSuccessful ()) {
            if (g_DynUpdtStatus->WorkingDir[0]) {
                //
                // rename this directory to make sure no module uses any DU files
                //
                MYASSERT (pathPss[0]);
                if (!MoveFile (g_DynUpdtStatus->WorkingDir, pathPss)) {
                    DynUpdtDebugLog (
                        Winnt32LogError,
                        TEXT("Winnt32DuUninitialize: MoveFile %1 -> %2 failed"),
                        0,
                        g_DynUpdtStatus->WorkingDir,
                        pathPss
                        );
                    MyDelnode (g_DynUpdtStatus->WorkingDir);
                }
            }
        }

    }
    EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
        SetLastError (_exception_code ());
        DynUpdtDebugLog (Winnt32LogError, TEXT("Winnt32DuUninitialize: an exception occured"), 0);
    }
    END_EXCEPT

    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Leave Winnt32DuUninitialize"), 0);
}


BOOL
pAddMissingPrinterDrivers (
    IN  OUT PSTRINGLIST* List
    )
{
    DWORD nBytesNeeded = 0;
    DWORD nDriverRetrieved = 0;
    DWORD rc;
    PDRIVER_INFO_6 buffer = NULL;
    DWORD index;
    PCTSTR printerPnpId;
    PSTRINGLIST p;
    BOOL unsupported;
    BOOL b = FALSE;

    if (!EnumPrinterDrivers (
            NULL,
            NULL,
            6,
            NULL,
            0,
            &nBytesNeeded,
            &nDriverRetrieved
            )) {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
            goto exit;
        }
    }

    if (nBytesNeeded) {
        buffer = (PDRIVER_INFO_6) MALLOC (nBytesNeeded);
        if (!buffer) {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            goto exit;
        }
        //
        // get printer driver information
        //
        if (!EnumPrinterDrivers (
                NULL,
                NULL,
                6,
                (LPBYTE)buffer,
                nBytesNeeded,
                &nBytesNeeded,
                &nDriverRetrieved
                )) {
            if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
                goto exit;
            }
        }
        for (index = 0; index < nDriverRetrieved; index++) {
            printerPnpId = buffer[index].pszHardwareID;
            if (!printerPnpId) {
                continue;
            }
            if (g_DynUpdtStatus->HwdbHasDriver (
                    g_DynUpdtStatus->HwdbDatabase,
                    printerPnpId,
                    &unsupported
                    )) {
                continue;
            }
            //
            // not an in-box driver
            //
            p = (PSTRINGLIST) MALLOC (sizeof (STRINGLIST));
            if (!p) {
                SetLastError (ERROR_NOT_ENOUGH_MEMORY);
                goto exit;
            }
            p->String = MALLOC ((lstrlen (printerPnpId) + 2) * sizeof (TCHAR));
            if (!p->String) {
                FREE (p);
                SetLastError (ERROR_NOT_ENOUGH_MEMORY);
                goto exit;
            }
            wsprintf (p->String, TEXT("%s%c"), printerPnpId, TEXT('\0'));
            p->Next = NULL;
            if (!InsertList ((PGENERIC_LIST*)List, (PGENERIC_LIST)p)) {
                DeleteStringCell (p);
                SetLastError (ERROR_NOT_ENOUGH_MEMORY);
                goto exit;
            }
        }
    }
    b = TRUE;

exit:
    rc = GetLastError();
    if (buffer) {
        FREE (buffer);
    }
    SetLastError(rc);

    return b;
}

HDEVINFO
(WINAPI* SetupapiDiGetClassDevs) (
    IN CONST GUID *ClassGuid,  OPTIONAL
    IN PCWSTR      Enumerator, OPTIONAL
    IN HWND        hwndParent, OPTIONAL
    IN DWORD       Flags
    );

BOOL
(WINAPI* SetupapiDiGetDeviceRegistryProperty) (
    IN  HDEVINFO         DeviceInfoSet,
    IN  PSP_DEVINFO_DATA DeviceInfoData,
    IN  DWORD            Property,
    OUT PDWORD           PropertyRegDataType, OPTIONAL
    OUT PBYTE            PropertyBuffer,
    IN  DWORD            PropertyBufferSize,
    OUT PDWORD           RequiredSize         OPTIONAL
    );

BOOL
(WINAPI* SetupapiDiEnumDeviceInfo) (
    IN  HDEVINFO         DeviceInfoSet,
    IN  DWORD            MemberIndex,
    OUT PSP_DEVINFO_DATA DeviceInfoData
    );

BOOL
(WINAPI* SetupapiDiDestroyDeviceInfoList) (
    IN HDEVINFO DeviceInfoSet
    );


#ifdef UNICODE

PSTRINGLIST
BuildMissingPnpIdList (
    VOID
    )
{
    HDEVINFO hDeviceInfoSet;
    INT nIndex = 0;
    SP_DEVINFO_DATA DeviceInfoData;
    PTSTR buffer = NULL;
    ULONG uHwidSize, uCompatidSize;
    DWORD rc;
    BOOL unsupported;
    PSTRINGLIST p;
    PSTRINGLIST list = NULL;
    HMODULE hSetupapi;
    BOOL b;
    BOOL bException;

    if (OsVersion.dwMajorVersion <= 4) {
        return list;
    }

    hSetupapi = LoadLibrary (TEXT("setupapi.dll"));
    if (!hSetupapi) {
        return list;
    }
    //
    // get the entry points
    //
    (FARPROC)SetupapiDiEnumDeviceInfo = GetProcAddress (hSetupapi, "SetupDiEnumDeviceInfo");
    (FARPROC)SetupapiDiDestroyDeviceInfoList = GetProcAddress (hSetupapi, "SetupDiDestroyDeviceInfoList");
    (FARPROC)SetupapiDiGetClassDevs = GetProcAddress (hSetupapi, "SetupDiGetClassDevsW");
    (FARPROC)SetupapiDiGetDeviceRegistryProperty = GetProcAddress (hSetupapi, "SetupDiGetDeviceRegistryPropertyW");

    if (!SetupapiDiEnumDeviceInfo ||
        !SetupapiDiDestroyDeviceInfoList ||
        !SetupapiDiGetClassDevs ||
        !SetupapiDiGetDeviceRegistryProperty
        ) {
        FreeLibrary (hSetupapi);
        return list;
    }

    hDeviceInfoSet = SetupapiDiGetClassDevs (NULL, NULL, NULL, DIGCF_ALLCLASSES);
    if (hDeviceInfoSet == INVALID_HANDLE_VALUE) {
        return list;
    }

    DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
    while (SetupapiDiEnumDeviceInfo (hDeviceInfoSet, nIndex++, &DeviceInfoData)) {
        uHwidSize = uCompatidSize = 0;

        if (!SetupapiDiGetDeviceRegistryProperty (
                hDeviceInfoSet,
                &DeviceInfoData,
                SPDRP_HARDWAREID,
                NULL,
                NULL,
                0,
                &uHwidSize
                )) {
            if (GetLastError() != ERROR_INSUFFICIENT_BUFFER &&
                GetLastError() != ERROR_INVALID_DATA
                ) {
                goto exit;
            }
        }
        if (!SetupapiDiGetDeviceRegistryProperty (
                hDeviceInfoSet,
                &DeviceInfoData,
                SPDRP_COMPATIBLEIDS,
                NULL,
                NULL,
                0,
                &uCompatidSize
                )) {
            if (GetLastError() != ERROR_INSUFFICIENT_BUFFER &&
                GetLastError() != ERROR_INVALID_DATA
                ) {
                goto exit;
            }
        }
        //
        // allocate memory for the multi-sz buffer
        //
        if (!uHwidSize && !uCompatidSize) {
            continue;
        }
        buffer = (PTSTR) MALLOC ((uHwidSize + uCompatidSize) * sizeof (TCHAR));
        if (!buffer) {
            SetLastError (ERROR_NOT_ENOUGH_MEMORY);
            goto exit;
        }
        //
        // get the hardware id and compatible id
        //
        if (uHwidSize) {
            if (!SetupapiDiGetDeviceRegistryProperty(
                    hDeviceInfoSet,
                    &DeviceInfoData,
                    SPDRP_HARDWAREID,
                    NULL,
                    (PBYTE)buffer,
                    uHwidSize,
                    NULL
                    )) {
                goto exit;
            }
        }		
        if (uCompatidSize) {
            if (!SetupapiDiGetDeviceRegistryProperty(
                    hDeviceInfoSet,
                    &DeviceInfoData,
                    SPDRP_COMPATIBLEIDS,
                    NULL,
                    (PBYTE)&buffer[uHwidSize / sizeof (TCHAR) - 1],
                    uCompatidSize,
                    NULL
                    )) {
                goto exit;
            }
        }
        //
        // check if there is an inbox driver for this device
        //
        bException = FALSE;
        __try {
            b = g_DynUpdtStatus->HwdbHasAnyDriver (
                    g_DynUpdtStatus->HwdbDatabase,
                    buffer,
                    &unsupported
                    );
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            SetLastError (GetExceptionCode());
            DynUpdtDebugLog (Winnt32LogError, TEXT("BuildMissingPnpIdList: HwdbHasAnyDriver threw an exception"), 0);
            bException = TRUE;
            b = TRUE;
        }

        if (bException) {
            __try {
                //
                // bad string passed back, or some internal error
                // try to print the string
                //
                PTSTR multisz = CreatePrintableString (buffer);
                DynUpdtDebugLog (Winnt32LogError, TEXT(" - The string was %1"), 0, multisz);
                FREE (multisz);
            }
            __except (EXCEPTION_EXECUTE_HANDLER) {
                DynUpdtDebugLog (Winnt32LogError, TEXT(" - Bad string"), 0);
            }
        }

        if (b) {
            FREE (buffer);
            buffer = NULL;
            continue;
        }
        //
        // no inbox driver - add it to the list
        //
        p = (PSTRINGLIST) MALLOC (sizeof (STRINGLIST));
        if (!p) {
            SetLastError (ERROR_NOT_ENOUGH_MEMORY);
            goto exit;
        }
        p->String = buffer;
        p->Next = NULL;
        buffer = NULL;
        if (!InsertList ((PGENERIC_LIST*)&list, (PGENERIC_LIST)p)) {
            DeleteStringCell (p);
            DeleteStringList (list);
            list = NULL;
            SetLastError (ERROR_NOT_ENOUGH_MEMORY);
            goto exit;
        }
    }

    if (GetLastError() == ERROR_NO_MORE_ITEMS) {
        SetLastError (ERROR_SUCCESS);
    }

exit:
    rc = GetLastError();
    SetupapiDiDestroyDeviceInfoList(hDeviceInfoSet);
    FreeLibrary (hSetupapi);
    if (buffer) {
        FREE (buffer);
    }
    if (rc == ERROR_SUCCESS) {
        //
        // get printer drivers
        //
        if (ISNT()) {
            if (!pAddMissingPrinterDrivers (&list)) {
                rc = GetLastError();
                DeleteStringList (list);
                list = NULL;
            }
        }
    }

    SetLastError(rc);

    return list;
}

#endif

BOOL
pHwdbHasAnyMissingDrivers (
    IN      HANDLE Hwdb,
    IN      PSTRINGLIST MissingPnpIds
    )
{
    PSTRINGLIST p;
    BOOL unsupported;
    BOOL b = FALSE;

    for (p = MissingPnpIds; p; p = p->Next) {

        BOOL bException = FALSE;

        __try {
            if (g_DynUpdtStatus->HwdbHasAnyDriver (Hwdb, p->String, &unsupported)) {
                b = TRUE;
                break;
            }
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            SetLastError (GetExceptionCode());
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("pHwdbHasAnyMissingDrivers: HwdbHasAnyDriver threw an exception"),
                0
                );
            bException = TRUE;
        }
        if (bException) {
            __try {
                //
                // bad string passed back, or some internal error
                // try to print the string
                //
                PTSTR multisz = CreatePrintableString (p->String);
                DynUpdtDebugLog (Winnt32LogError, TEXT(" - The string was %1"), 0, multisz);
                FREE (multisz);
            }
            __except (EXCEPTION_EXECUTE_HANDLER) {
                DynUpdtDebugLog (Winnt32LogError, TEXT(" - Bad string"), 0);
            }
        }
    }

    return b;
}


typedef struct {
    PCTSTR BaseDir;
    PCTSTR Filename;
} CONTEXT_EXTRACTFILEINDIR, *PCONTEXT_EXTRACTFILEINDIR;

UINT
pExtractFileInDir (
    IN PVOID Context,
    IN UINT  Code,
    IN UINT_PTR Param1,
    IN UINT_PTR Param2
    )
{
    if (g_DynUpdtStatus->Cancelled) {
        return ERROR_CANCELLED;
    }

    switch (Code) {
    case SPFILENOTIFY_FILEINCABINET:
        {
            PFILE_IN_CABINET_INFO FileInCabInfo = (PFILE_IN_CABINET_INFO)Param1;
            PCONTEXT_EXTRACTFILEINDIR ctx = (PCONTEXT_EXTRACTFILEINDIR)Context;
            PTSTR p;

            if (lstrcmpi (FileInCabInfo->NameInCabinet, ctx->Filename)) {
                return FILEOP_SKIP;
            }
            BuildPath (FileInCabInfo->FullTargetName, ctx->BaseDir, FileInCabInfo->NameInCabinet);
            if (_tcschr (FileInCabInfo->NameInCabinet, TEXT('\\'))) {
                //
                // target file is in a subdir; first create it
                //
                p = _tcsrchr (FileInCabInfo->FullTargetName, TEXT('\\'));
                if (p) {
                    *p = 0;
                }
                if (CreateMultiLevelDirectory (FileInCabInfo->FullTargetName) != ERROR_SUCCESS) {
                    return FILEOP_ABORT;
                }
                if (p) {
                    *p = TEXT('\\');
                }
            }
            return FILEOP_DOIT;
        }
    case SPFILENOTIFY_NEEDNEWCABINET:
        {
            PCABINET_INFO CabInfo = (PCABINET_INFO)Param1;
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("pExtractFileInDir: NeedNewCabinet %1\\%2 on %3 (SetId=%4!u!;CabinetNumber=%5!u!)"),
                0,
                CabInfo->CabinetPath,
                CabInfo->CabinetFile,
                CabInfo->DiskName,
                CabInfo->SetId,
                CabInfo->CabinetNumber
                );
            return ERROR_FILE_NOT_FOUND;
        }
    }

    return NO_ERROR;
}


UINT
pExpandCabInDir (
    IN PVOID Context,
    IN UINT  Code,
    IN UINT_PTR Param1,
    IN UINT_PTR Param2
    )
{
    if (g_DynUpdtStatus->Cancelled) {
        return ERROR_CANCELLED;
    }

    switch (Code) {
    case SPFILENOTIFY_FILEINCABINET:
        {
            PFILE_IN_CABINET_INFO FileInCabInfo = (PFILE_IN_CABINET_INFO)Param1;
            PTSTR p;

            BuildPath (FileInCabInfo->FullTargetName, (PCTSTR)Context, FileInCabInfo->NameInCabinet);
            if (_tcschr (FileInCabInfo->NameInCabinet, TEXT('\\'))) {
                //
                // target file is in a subdir; first create it
                //
                p = _tcsrchr (FileInCabInfo->FullTargetName, TEXT('\\'));
                if (p) {
                    *p = 0;
                }
                if (CreateMultiLevelDirectory (FileInCabInfo->FullTargetName) != ERROR_SUCCESS) {
                    return FILEOP_ABORT;
                }
                if (p) {
                    *p = TEXT('\\');
                }
            }
            return FILEOP_DOIT;
        }
    case SPFILENOTIFY_NEEDNEWCABINET:
        {
            PCABINET_INFO CabInfo = (PCABINET_INFO)Param1;
            DynUpdtDebugLog (
                Winnt32LogError,
                TEXT("pExpandCabInDir: NeedNewCabinet %1\\%2 on %3 (SetId=%4!u!;CabinetNumber=%5!u!)"),
                0,
                CabInfo->CabinetPath,
                CabInfo->CabinetFile,
                CabInfo->DiskName,
                CabInfo->SetId,
                CabInfo->CabinetNumber
                );
            return ERROR_FILE_NOT_FOUND;
        }
    }

    return NO_ERROR;
}


BOOL
pGetAutoSubdirName (
    IN      PCTSTR FilePath,
    OUT     PTSTR DirName
    )
{
    PTSTR p, q;

    lstrcpy (DirName, FilePath);
    p = _tcsrchr (DirName, TEXT('.'));
    q = _tcsrchr (DirName, TEXT('\\'));
    if (!p || (q && p < q)) {
        return FALSE;
    }
    *p = 0;
    return TRUE;
}

BOOL
pAddLibrariesForCompToCopyQueue (
    IN      PCTSTR ModuleName,
    IN      PCTSTR Subdir,
    IN      PCTSTR BaseDir,
    IN      HSPFILEQ SetupQueue
    )
{
    static struct {
        PCTSTR SubDir;
        PCTSTR LibsMultiSz;
    } g_SubdirReqLibs [] = {
        TEXT("win9xupg"), TEXT("setupapi.dll\0cfgmgr32.dll\0msvcrt.dll\0cabinet.dll\0imagehlp.dll\0"),
        TEXT("winntupg"), TEXT("setupapi.dll\0cfgmgr32.dll\0msvcrt.dll\0"),
    };

    INT i;
    TCHAR dst[MAX_PATH];
    TCHAR src[MAX_PATH];
    TCHAR sourceFile[MAX_PATH];
    PTSTR p, q;
    PCTSTR fileName;

    for (i = 0; i < SIZEOFARRAY (g_SubdirReqLibs); i++) {
        if (Subdir == g_SubdirReqLibs[i].SubDir ||
            Subdir && !lstrcmpi (Subdir, g_SubdirReqLibs[i].SubDir)
            ) {
            break;
        }
    }
    if (i >= SIZEOFARRAY (g_SubdirReqLibs)) {
        return TRUE;
    }

    //
    // prepare src and dest path
    //
    if (!GetModuleFileName (NULL, src, MAX_PATH)) {
        return FALSE;
    }
    p = _tcsrchr (src, TEXT('\\'));
    if (!p) {
        return FALSE;
    }
    *p = 0;
    q = dst + wsprintf (dst, TEXT("%s"), BaseDir);
    if (Subdir) {
        p = p + wsprintf (p, TEXT("\\%s"), Subdir);
        q = q + wsprintf (q, TEXT("\\%s"), Subdir);
    }
    //
    // copy each source file
    //
    for (fileName = g_SubdirReqLibs[i].LibsMultiSz;
         *fileName;
         fileName = _tcschr (fileName, 0) + 1) {
        wsprintf (q, TEXT("\\%s"), fileName);
        //
        // check if file already exists at dest
        //
        if (!pDoesFileExist (dst)) {
            //
            // check if the source file actually exists
            //
            BuildPath (sourceFile, src, fileName);
            if (!pDoesFileExist (sourceFile)) {
                DynUpdtDebugLog (Winnt32LogError, TEXT("Source file %1 not found"), 0, sourceFile);
                return FALSE;
            }
            //
            // prepare source path and copy file
            //
            *q = 0;
            if (!SetupapiQueueCopy (
                    SetupQueue,
                    src,
                    NULL,
                    fileName,
                    NULL,
                    NULL,
                    dst,
                    NULL,
                    SP_COPY_SOURCEPATH_ABSOLUTE
                    )) {
                return FALSE;
            }
        }
    }

    return TRUE;
}


BOOL
pIsBootDriver (
    IN      PCTSTR DriverFilesDir
    )
{
    FILEPATTERN_ENUM e;
    BOOL b = FALSE;

    if (EnumFirstFilePattern (&e, DriverFilesDir, TEXT("txtsetup.oem"))) {
        b = !(e.FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
        AbortEnumFilePattern (&e);
    }
    return b;
}


BOOL
pIsExcluded (
    IN      HINF GuiDrvsInf,
    IN      PCTSTR PnPId,
    IN      PCTSTR ActualInfFilename,
    IN      PCTSTR SourceDir,
    IN      PCTSTR FullInfPath
    )
{
    TCHAR buffer[MAX_PATH];
    PTSTR packageName;
    INFCONTEXT ic;
    TCHAR field[MAX_PATH];
    TCHAR driverVer[MAX_PATH];

    if (!((GuiDrvsInf != INVALID_HANDLE_VALUE && PnPId && ActualInfFilename && SourceDir && FullInfPath))) {
        MYASSERT (FALSE);
        return FALSE;
    }

    lstrcpy (buffer, SourceDir);
    packageName = _tcsrchr (buffer, TEXT('\\'));
    if (!packageName) {
        return FALSE;
    }
    packageName++;

    if (SetupapiFindFirstLine (GuiDrvsInf, TEXT("ExcludedDrivers"), NULL, &ic)) {
        do {
            if (!SetupapiGetStringField (&ic, GUIDRVS_FIELD_CABNAME, field, MAX_PATH, NULL)) {
                continue;
            }
            if (lstrcmpi (field, packageName)) {
                continue;
            }
            if (!SetupapiGetStringField (&ic, GUIDRVS_FIELD_INFNAME, field, MAX_PATH, NULL)) {
                return TRUE;
            }
            if (lstrcmpi (field, ActualInfFilename)) {
                continue;
            }
            if (SetupapiGetStringField (&ic, GUIDRVS_FIELD_DRIVERVER, field, MAX_PATH, NULL)) {
                if (field[0] != TEXT('*')) {
                    //
                    // read the DriverVer value out of this INF
                    //
                    GetPrivateProfileString (
                            TEXT("Version"),
                            TEXT("DriverVer"),
                            TEXT(""),
                            driverVer,
                            MAX_PATH,
                            FullInfPath
                            );
                    if (lstrcmpi (field, driverVer)) {
                        continue;
                    }
                }
            }
            if (SetupapiGetStringField (&ic, GUIDRVS_FIELD_HARDWAREID, field, MAX_PATH, NULL) &&
                lstrcmpi (field, PnPId)
                ) {
                continue;
            }
            return TRUE;
        } while (SetupapiFindNextLine (&ic, &ic));
    }

    return FALSE;
}


BOOL
Winnt32HwdbAppendInfsCallback (
    IN      PVOID Context,
    IN      PCTSTR PnpId,
    IN      PCTSTR ActualInfFilename,
    IN      PCTSTR SourceDir,
    IN      PCTSTR FullInfPath
    )
{
    HINF hGuiDrvs = (HINF)Context;
    MYASSERT (hGuiDrvs != INVALID_HANDLE_VALUE);
    return !pIsExcluded (hGuiDrvs, PnpId, ActualInfFilename, SourceDir, FullInfPath);
}


BOOL
pBuildHwcompDat (
    IN      PCTSTR DriverDir,
    IN      HINF GuidrvsInf,            OPTIONAL
    IN      BOOL AlwaysRebuild,
    IN      BOOL AllowUI
    )
{
    HANDLE hDB;
    TCHAR datFile[MAX_PATH];
    BOOL b = TRUE;

    BuildPath (datFile, DriverDir, S_HWCOMP_DAT);

    if (AlwaysRebuild) {
        SetFileAttributes (datFile, FILE_ATTRIBUTE_NORMAL);
        DeleteFile (datFile);
    }

    if (pDoesFileExist (datFile)) {
        return TRUE;
    }

    hDB = g_DynUpdtStatus->HwdbOpen (NULL);
    if (!hDB) {
        return FALSE;
    }

    if (g_DynUpdtStatus->HwdbAppendInfs (
                            hDB,
                            DriverDir,
                            GuidrvsInf != INVALID_HANDLE_VALUE ? Winnt32HwdbAppendInfsCallback : NULL,
                            (PVOID)GuidrvsInf
                            )) {

        if (g_DynUpdtStatus->HwdbFlush (hDB, datFile)) {
            DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Successfully built precompiled hardware database %1"), 0, datFile);
        } else {
            if (AllowUI) {
                MessageBoxFromMessage (
                    g_DynUpdtStatus->ProgressWindow,
                    MSG_ERROR_WRITING_FILE,
                    FALSE,
                    AppTitleStringId,
                    MB_OK | MB_ICONERROR | MB_TASKMODAL,
                    GetLastError (),
                    datFile
                    );
            }
            b = FALSE;
        }
    } else {
        if (AllowUI) {
            MessageBoxFromMessage (
                g_DynUpdtStatus->ProgressWindow,
                MSG_ERROR_PROCESSING_DRIVER,
                FALSE,
                AppTitleStringId,
                MB_OK | MB_ICONERROR | MB_TASKMODAL,
                DriverDir
                );
        }
        b = FALSE;
    }

    g_DynUpdtStatus->HwdbClose (hDB);

    return b;
}


UINT
pWriteAnsiFilelistToFile (
    IN PVOID Context,
    IN UINT  Code,
    IN UINT_PTR Param1,
    IN UINT_PTR Param2
    )
{
    if (g_DynUpdtStatus->Cancelled) {
        return ERROR_CANCELLED;
    }

    switch (Code) {
    case SPFILENOTIFY_FILEINCABINET:
        {
            PFILE_IN_CABINET_INFO FileInCabInfo = (PFILE_IN_CABINET_INFO)Param1;
            CHAR ansi[MAX_PATH];
            DWORD size;
            DWORD bytes;
            PCTSTR p;

            MYASSERT (!_tcschr (FileInCabInfo->NameInCabinet, TEXT('\\')));
#ifdef UNICODE
            size = wsprintfA (ansi, "%ls\r\n", FileInCabInfo->NameInCabinet);
#else
            size = wsprintfA (ansi, "%s\r\n", FileInCabInfo->NameInCabinet);
#endif
            if (!WriteFile ((HANDLE)Context, ansi, size, &bytes, NULL) || bytes != size) {
                return FILEOP_ABORT;
            }
            return FILEOP_SKIP;
        }
    }
    return NO_ERROR;
}

BOOL
CreateFileListSif (
    IN      PCTSTR SifPath,
    IN      PCTSTR SectionName,
    IN      PCTSTR CabinetToScan
    )
{
    HANDLE sif;
    CHAR ansi[MAX_PATH];
    DWORD size;
    DWORD bytes;
    BOOL b = TRUE;

    sif = CreateFile (
                SifPath,
                GENERIC_WRITE,
                FILE_SHARE_READ,
                NULL,
                CREATE_ALWAYS,
                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
                NULL
                );

    if (sif == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

#ifdef UNICODE
    size = wsprintfA (ansi, "[%ls]\r\n", SectionName);
#else
    size = wsprintfA (ansi, "[%s]\r\n", SectionName);
#endif
    if (!WriteFile (sif, ansi, size, &bytes, NULL) || bytes != size) {
        b = FALSE;
        goto exit;
    }

    b = SetupapiCabinetRoutine (CabinetToScan, 0, pWriteAnsiFilelistToFile, (PVOID)sif);

exit:
    CloseHandle (sif);
    if (!b) {
        DeleteFile (SifPath);
    }

    return b;
}

BOOL
pIsExecutableModule (
    IN      PCTSTR ModulePath
    )
{
    PCTSTR p;

    p = GetFileExtension (ModulePath);
    return p && !lstrcmpi (p, TEXT(".dll"));
}

UINT
pCopyFilesCallback (
    IN      PVOID Context,      //context used by the callback routine
    IN      UINT Notification,  //notification sent to callback routine
    IN      UINT_PTR Param1,        //additional notification information
    IN      UINT_PTR Param2         //additional notification information
    )
{
    switch (Notification) {
    case SPFILENOTIFY_COPYERROR:
        return FILEOP_ABORT;

    case SPFILENOTIFY_STARTCOPY:
        //
        // try to avoid unnecessary setupapi warnings that the files are not signed
        // or even blocked because of this; try to copy the file ourselves first
        //
        {
            PFILEPATHS paths = (PFILEPATHS)Param1;

            if (CopyFile (paths->Source, paths->Target, FALSE)) {
                return FILEOP_SKIP;
            }
        }
        break;

    case SPFILENOTIFY_STARTQUEUE:
    case SPFILENOTIFY_STARTSUBQUEUE:
    case SPFILENOTIFY_ENDSUBQUEUE:
    case SPFILENOTIFY_ENDQUEUE:
        return !g_DynUpdtStatus->Cancelled;
    }
    return SetupapiDefaultQueueCallback (Context, Notification, Param1, Param2);
}


BOOL
pProcessWinnt32Files (
    IN      PCTSTR Winnt32Cab,
    IN      BOOL ClientInstall,
    OUT     PBOOL StopSetup
    )
{
    FILEPATTERNREC_ENUM e;
    TCHAR winnt32WorkingDir[MAX_PATH];
    TCHAR dst[MAX_PATH];
    PTSTR p, subdir;
    HSPFILEQ hq;
    BOOL bLoaded;
    BOOL bRestartRequired;
    BOOL bReloadMainInf = FALSE;
    TCHAR buffer[MAX_PATH];
    TCHAR origSubPath[MAX_PATH];
    TCHAR origSubPathCompressed[MAX_PATH];
    TCHAR origFileName[MAX_PATH];
    TCHAR origFilePath[MAX_PATH];
    TCHAR destFilePath[MAX_PATH];
    TCHAR version[100];
    DWORD i;
    DWORD attr;
    BOOL b = TRUE;

    *StopSetup = FALSE;
    if (!pNonemptyFilePresent (Winnt32Cab)) {
        if (!ClientInstall) {
            DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Package %1 is not present"), 0, Winnt32Cab);
        }
        return TRUE;
    } else {
        //
        // don't process the cabinet in client installation mode; just warn about that
        //
        if (ClientInstall) {
            //
            // user specified updates location, but they didn't run winnt32 /prepare now or before
            //
            MessageBoxFromMessage (
               g_DynUpdtStatus->ProgressWindow,
               MSG_MUST_PREPARE_SHARE,
               FALSE,
               AppTitleStringId,
               MB_OK | MB_ICONSTOP | MB_TASKMODAL,
               g_DynUpdtStatus->DynamicUpdatesSource
               );
            *StopSetup = TRUE;
            return FALSE;
        }
    }

    DynUpdtDebugLog (
        DynUpdtLogLevel,
        TEXT("Analyzing package %1..."),
        0,
        Winnt32Cab
        );

    //
    // expand it in the corresponding subdir
    //
    BuildPath (
        winnt32WorkingDir,
        g_DynUpdtStatus->PrepareWinnt32 ?
            g_DynUpdtStatus->DynamicUpdatesSource :
            g_DynUpdtStatus->WorkingDir,
        S_SUBDIRNAME_WINNT32
        );

    //
    // expand CAB in this dir
    //
    if (CreateMultiLevelDirectory (winnt32WorkingDir) != ERROR_SUCCESS) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to create dir %1"), 0, winnt32WorkingDir);
        return FALSE;
    }
    if (!(*SetupapiCabinetRoutine) (Winnt32Cab, 0, pExpandCabInDir, (PVOID)winnt32WorkingDir)) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to expand cabinet %1"), 0, Winnt32Cab);
        return FALSE;
    }

    //
    // ISSUE: the patching support is currently not available for platforms other than x86
    //
#ifdef _X86_
    //
    // now let's look for any patches
    //
    if (EnumFirstFilePatternRecursive (&e, winnt32WorkingDir, S_PATCH_FILE_EXT, 0)) {

        do {
            BOOL bDeleteTempFile = FALSE;

            if (g_DynUpdtStatus->Cancelled) {
                SetLastError (ERROR_CANCELLED);
                b = FALSE;
                break;
            }

            if (e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                continue;
            }
            DynUpdtDebugLog (
                DynUpdtLogLevel,
                TEXT("pProcessWinnt32Files: found patch %1"),
                0,
                e.FullPath
                );
            //
            // get the original file from the sources location
            // the filename is obtained cutting the ._p1 extension
            //
            lstrcpy (origFileName, e.FileName);
            p = GetFileExtension (origFileName);
            if (!p) {
                MYASSERT (FALSE);
                continue;
            }
            *p = 0;
            lstrcpy (origSubPath, e.SubPath);
            p = GetFileExtension (origSubPath);
            if (!p) {
                MYASSERT (FALSE);
                continue;
            }
            *p = 0;
            if (!GetModuleFileName (NULL, origFilePath, MAX_PATH) ||
                !(p = _tcsrchr (origFilePath, TEXT('\\')))) {
                b = FALSE;
                break;
            }
            *p = 0;
            ConcatenatePaths (origFilePath, origSubPath, MAX_PATH);
            //
            // now check if this file (in it's compressed form or not) actually exists
            //
            if (!pDoesFileExist (origFilePath)) {
                //
                // try the compressed form
                //
                p = _tcschr (origFilePath, 0);
                MYASSERT (p);
                if (!p) {
                    continue;
                }
                p = _tcsdec (origFilePath, p);
                MYASSERT (p);
                if (!p) {
                    continue;
                }
                *p = TEXT('_');
                if (!pDoesFileExist (origFilePath)) {
                    //
                    // the file might exist on the original installation share (like w95upgnt.dll etc)
                    // generate compressed form of the file
                    //
                    lstrcpy (origSubPathCompressed, origSubPath);
                    p = _tcschr (origSubPathCompressed, 0);
                    MYASSERT (p);
                    if (!p) {
                        continue;
                    }
                    p = _tcsdec (origSubPathCompressed, p);
                    MYASSERT (p);
                    if (!p) {
                        continue;
                    }
                    *p = TEXT('_');
                    //
                    // now search for it
                    //
                    b = FALSE;
                    for (i = 0; i < SourceCount; i++) {
                        lstrcpyn (origFilePath, NativeSourcePaths[i], SIZEOFARRAY(origFilePath));
                        ConcatenatePaths (origFilePath, origSubPathCompressed, SIZEOFARRAY(origFilePath));
                        attr = GetFileAttributes (origFilePath);
                        if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
                            b = TRUE;
                            break;
                        }
                    }
                    if (!b) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("pProcessWinnt32Files: Unable to find original file %1 to apply the patch"),
                            0,
                            origSubPath
                            );
                        break;
                    }
                }
                //
                // expand the file to the temp dir
                //
                BuildPath (buffer, g_DynUpdtStatus->TempDir, origSubPath);
                p = _tcsrchr (buffer, TEXT('\\'));
                MYASSERT (p);
                if (!p) {
                    continue;
                }
                *p = 0;
                if (CreateMultiLevelDirectory (buffer) != ERROR_SUCCESS) {
                    DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessWinnt32Files: Unable to create dir %1"), 0, buffer);
                    b = FALSE;
                    break;
                }
                if (!(*SetupapiCabinetRoutine) (origFilePath, 0, pExpandCabInDir, buffer)) {
                    DynUpdtDebugLog (
                        Winnt32LogError,
                        TEXT("pProcessWinnt32Files: Unable to expand original file %1 to dir %2"),
                        0,
                        origFilePath,
                        buffer
                        );
                    b = FALSE;
                    break;
                }
                *p = TEXT('\\');
                lstrcpy (origFilePath, buffer);
                bDeleteTempFile = TRUE;
            }
            BuildPath (destFilePath, winnt32WorkingDir, TEXT("$$temp$$.~~~"));
            //
            // now really apply the patch
            //
            if (!ApplyPatchToFile (e.FullPath, origFilePath, destFilePath, 0)) {
                DynUpdtDebugLog (
                    Winnt32LogError,
                    TEXT("pProcessWinnt32Files: ApplyPatchToFile failed to apply patch %1 to file %2"),
                    0,
                    e.FullPath,
                    origFilePath
                    );
                b = FALSE;
                break;
            }
            //
            // success! now move the file to the real destination
            //
            BuildPath (buffer, winnt32WorkingDir, origSubPath);
            p = _tcsrchr (buffer, TEXT('\\'));
            MYASSERT (p);
            if (!p) {
                continue;
            }
            *p = 0;
            if (CreateMultiLevelDirectory (buffer) != ERROR_SUCCESS) {
                DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessWinnt32Files: Unable to create dir %1"), 0, buffer);
                b = FALSE;
                break;
            }
            *p = TEXT('\\');
            SetFileAttributes (buffer, FILE_ATTRIBUTE_NORMAL);
            DeleteFile (buffer);
            if (!MoveFile (destFilePath, buffer)) {
                DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessWinnt32Files: Unable to move file %1 to final dest %2"), 0, destFilePath, buffer);
                b = FALSE;
                break;
            }
            if (!GetFileVersion (buffer, version)) {
                lstrcpy (version, TEXT("<unknown>"));
            }
            DynUpdtDebugLog (
                DynUpdtLogLevel,
                TEXT("pProcessWinnt32Files: successfully applied patch %1 to file %2; the new file %3 has version %4"),
                0,
                e.FullPath,
                origFilePath,
                buffer,
                version
                );
            //
            // now remove the patch file
            //
            SetFileAttributes (e.FullPath, FILE_ATTRIBUTE_NORMAL);
            DeleteFile (e.FullPath);
            if (bDeleteTempFile) {
                SetFileAttributes (origFilePath, FILE_ATTRIBUTE_NORMAL);
                DeleteFile (origFilePath);
            }
        } while (EnumNextFilePatternRecursive (&e));
        AbortEnumFilePatternRecursive (&e);

    }

#endif

    if (!b) {
        goto exit;
    }

    //
    // process new Winnt32 components
    //
    hq = SetupapiOpenFileQueue ();
    if (hq == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

    if (EnumFirstFilePatternRecursive (&e, winnt32WorkingDir, TEXT("*"), 0)) {

        do {
            if (g_DynUpdtStatus->Cancelled) {
                SetLastError (ERROR_CANCELLED);
                b = FALSE;
                break;
            }

            if (e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                continue;
            }

            if (!GetModuleFileName (NULL, dst, MAX_PATH) ||
                !(p = _tcsrchr (dst, TEXT('\\')))) {
                b = FALSE;
                break;
            }
            *p = 0;
            ConcatenatePaths (dst, e.SubPath, MAX_PATH);

            //
            // check file versions first
            //
            if (IsFileVersionLesser (e.FullPath, dst)) {
                continue;
            }

            //
            // if there's a file named winnt32.rst, force restart
            //
            if (!lstrcmpi (e.FileName, S_RESTART_FILENAME)) {
                if (!g_DynUpdtStatus->PrepareWinnt32) {
                    g_DynUpdtStatus->RestartWinnt32 = TRUE;
                    DynUpdtDebugLog (
                        DynUpdtLogLevel,
                        TEXT("File %1 present; winnt32 will restart"),
                        0,
                        e.FileName
                        );
                }
            } else {
                //
                // check if dosnet.inf is present; if it is, reset the global variables
                //
                if (!lstrcmpi (e.FileName, InfName) && FullInfName[0]) {
                    FullInfName[0] = 0;
                    bReloadMainInf = TRUE;
                }

                bLoaded = FALSE;
                bRestartRequired = Upgrade || !_tcschr (e.SubPath, TEXT('\\'));
                if (GetModuleHandle (e.FileName) != NULL) {
                    bLoaded = TRUE;
                    if (!g_DynUpdtStatus->PrepareWinnt32) {
                        //
                        // do NOT restart if it's NOT an upgrade and this is one of the upgrade modules
                        //
                        if (bRestartRequired) {
                            //
                            // need to restart winnt32 so the newly registered component can be used instead
                            //
                            g_DynUpdtStatus->RestartWinnt32 = TRUE;
                            DynUpdtDebugLog (
                                DynUpdtLogLevel,
                                TEXT("A newer version is available for %1; winnt32 will restart"),
                                0,
                                e.SubPath
                                );
                        }
                    }
                }
                if ((bLoaded || pIsExecutableModule (e.FullPath)) &&
                    (bRestartRequired || g_DynUpdtStatus->PrepareWinnt32)
                    ) {
                    //
                    // make all required libraries available for this module
                    //
                    p = _tcsrchr (e.SubPath, TEXT('\\'));
                    if (p) {
                        *p = 0;
                        subdir = e.SubPath;
                    } else {
                        subdir = NULL;
                    }
                    if (!pAddLibrariesForCompToCopyQueue (e.FileName, subdir, winnt32WorkingDir, hq)) {
                        b = FALSE;
                        break;
                    }
                    if (p) {
                        *p = TEXT('\\');
                    }
                }
            }
        } while (EnumNextFilePatternRecursive (&e));
        AbortEnumFilePatternRecursive (&e);

        if (b) {
            PVOID ctx;

            ctx = SetupapiInitDefaultQueueCallback (NULL);
            b = SetupapiCommitFileQueue (NULL, hq, pCopyFilesCallback, ctx);
            SetupapiTermDefaultQueueCallback (ctx);
            if (!b) {
                DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessWinnt32Files: SetupapiCommitFileQueue failed"), 0);
            }
        }
    }

    SetupapiCloseFileQueue (hq);

    if (b) {
        SetFileAttributes (Winnt32Cab, FILE_ATTRIBUTE_NORMAL);
        if (!DeleteFile (Winnt32Cab)) {
            DynUpdtDebugLog (Winnt32LogSevereError, TEXT("pProcessWinnt32Files: unable to delete file %1"), 0, Winnt32Cab);
            b = FALSE;
        }
    }

    if (b && bReloadMainInf) {
        b = FindPathToWinnt32File (InfName, FullInfName, MAX_PATH);
        MYASSERT (b);
        if (MainInf) {
            UnloadInfFile (MainInf);
            MainInf = NULL;
            b = LoadInfFile (FullInfName, TRUE, &MainInf) == NO_ERROR;
        }
    }

exit:

    return b;
}


BOOL
pProcessUpdates (
    IN      PCTSTR UpdatesCab,
    IN      BOOL ClientInstall,
    OUT     PBOOL StopSetup
    )
{
    TCHAR updatesSourceDir[MAX_PATH];
    TCHAR buffer[MAX_PATH];
    FILEPATTERNREC_ENUM e;
    TCHAR origSubPath[MAX_PATH];
    TCHAR origFileName[MAX_PATH];
    TCHAR origFilePath[MAX_PATH];
    TCHAR destFilePath[MAX_PATH];
    TCHAR version[100];
    PTSTR p;
    TCHAR updatesCabPath[MAX_PATH];
    HANDLE hCabContext;
    BOOL result;
    BOOL bPatchApplied = FALSE;
    HANDLE hCab;
    PSTRINGLIST listUpdatesFiles = NULL;
    PCTSTR cabPath;
    BOOL bCatalogFileFound;
    HANDLE hDiamond = NULL;
    BOOL b = TRUE;

    *StopSetup = FALSE;
    if (!pNonemptyFilePresent (UpdatesCab)) {
        if (!ClientInstall) {
            DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Package %1 is not present"), 0, UpdatesCab);
        }
        return TRUE;
    }

    DynUpdtDebugLog (
        DynUpdtLogLevel,
        TEXT("Analyzing package %1..."),
        0,
        UpdatesCab
        );

    if (ClientInstall) {
        BOOL bMustPrepare = FALSE;
        BuildSifName (UpdatesCab, destFilePath);
        if (!pDoesFileExist (destFilePath)) {
            bMustPrepare = TRUE;
        } else {
            BuildPath (updatesSourceDir, g_DynUpdtStatus->DynamicUpdatesSource, S_SUBDIRNAME_UPDATES);
            if (!DoesDirectoryExist (updatesSourceDir)) {
                bMustPrepare = TRUE;
            }
        }
        if (bMustPrepare) {

            //
            // user specified updates location, but they didn't run winnt32 /prepare now or before
            //
            MessageBoxFromMessage (
               g_DynUpdtStatus->ProgressWindow,
               MSG_MUST_PREPARE_SHARE,
               FALSE,
               AppTitleStringId,
               MB_OK | MB_ICONSTOP | MB_TASKMODAL,
               g_DynUpdtStatus->DynamicUpdatesSource
               );
            *StopSetup = TRUE;
            return FALSE;
        }
    } else {

        if (g_DynUpdtStatus->PrepareWinnt32) {
            BuildPath (updatesSourceDir, g_DynUpdtStatus->DynamicUpdatesSource, S_SUBDIRNAME_UPDATES);
        } else {
            BuildPath (updatesSourceDir, g_DynUpdtStatus->WorkingDir, S_SUBDIRNAME_UPDATES);
        }

        //
        // expand CAB in this dir
        // make sure dir is initially empty
        //
        MyDelnode (updatesSourceDir);
        if (CreateMultiLevelDirectory (updatesSourceDir) != ERROR_SUCCESS) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to create dir %1"), 0, updatesSourceDir);
            return FALSE;
        }
        if (!(*SetupapiCabinetRoutine) (UpdatesCab, 0, pExpandCabInDir, (PVOID)updatesSourceDir)) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to expand cabinet %1"), 0, UpdatesCab);
            return FALSE;
        }

        hDiamond = DiamondInitialize (g_DynUpdtStatus->TempDir);
        if (!hDiamond) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to initialize compression/decompression engine"), 0);
            return FALSE;
        }

        //
        // ISSUE: the patching support is currently not available for platforms other than x86
        //
#ifdef _X86_
        //
        // now let's look for any patches
        //
        if (EnumFirstFilePatternRecursive (&e, updatesSourceDir, S_PATCH_FILE_EXT, 0)) {
            //
            // load drvindex.inf in advance
            //
            TCHAR driverInfName[MAX_PATH];
            PVOID driverInfHandle;
            TCHAR driverCabName[MAX_PATH];
            TCHAR driverCabPath[MAX_PATH];

            if (!FindPathToInstallationFile (DRVINDEX_INF, driverInfName, MAX_PATH)) {
                DynUpdtDebugLog (
                    Winnt32LogError,
                    TEXT("pProcessUpdates: Unable to find %1"),
                    0,
                    DRVINDEX_INF
                    );
                AbortEnumFilePatternRecursive (&e);
                b = FALSE;
                goto exit;
            }
            if (LoadInfFile (driverInfName, FALSE, &driverInfHandle) != NO_ERROR) {
                DynUpdtDebugLog (
                    Winnt32LogError,
                    TEXT("pProcessUpdates: Unable to load %1"),
                    0,
                    driverInfName
                    );
                AbortEnumFilePatternRecursive (&e);
                b = FALSE;
                goto exit;
            }

            do {
                BOOL bDeleteTempFile = FALSE;

                if (g_DynUpdtStatus->Cancelled) {
                    SetLastError (ERROR_CANCELLED);
                    b = FALSE;
                    break;
                }

                if (e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    continue;
                }
                DynUpdtDebugLog (
                    DynUpdtLogLevel,
                    TEXT("pProcessUpdates: found patch %1"),
                    0,
                    e.FullPath
                    );
                //
                // get the original file from the sources location
                // the filename is obtained cutting the ._p1 extension
                //
                lstrcpy (origFileName, e.FileName);
                p = GetFileExtension (origFileName);
                if (!p) {
                    MYASSERT (FALSE);
                    continue;
                }
                *p = 0;
                lstrcpy (origSubPath, e.SubPath);
                p = GetFileExtension (origSubPath);
                if (!p) {
                    MYASSERT (FALSE);
                    continue;
                }
                *p = 0;
                BuildPath (origFilePath, SourcePaths[0], origSubPath);
                //
                // now check if this file (in it's compressed form or not) actually exists
                // note that the file may exist in driver.cab
                //
                if (InDriverCacheInf (driverInfHandle, origFileName, driverCabName, MAX_PATH)) {
                    CONTEXT_EXTRACTFILEINDIR ctx;
                    //
                    // extract the file to the temp dir
                    //
                    if (!driverCabName[0]) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("pProcessUpdates: cab name not found for %1 in %2"),
                            0,
                            origFileName,
                            driverInfName
                            );
                        b = FALSE;
                        break;
                    }
                    BuildPath (buffer, g_DynUpdtStatus->TempDir, origSubPath);
                    p = _tcsrchr (buffer, TEXT('\\'));
                    MYASSERT (p);
                    if (!p) {
                        continue;
                    }
                    *p = 0;
                    if (CreateMultiLevelDirectory (buffer) != ERROR_SUCCESS) {
                        DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpdates: Unable to create dir %1"), 0, buffer);
                        b = FALSE;
                        break;
                    }
                    ctx.BaseDir = buffer;
                    ctx.Filename = origFileName;
                    if (!FindPathToInstallationFile (driverCabName, driverCabPath, MAX_PATH)) {

                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("pProcessUpdates: Unable to find cabinet %1"),
                            0,
                            driverCabName
                            );
                        b = FALSE;
                        break;
                    }
                    if (!(*SetupapiCabinetRoutine) (driverCabPath, 0, pExtractFileInDir, &ctx)) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("pProcessUpdates: Unable to extract file %1 from %2 to %3"),
                            0,
                            origFileName,
                            driverCabName,
                            buffer
                            );
                        b = FALSE;
                        break;
                    }
                    *p = TEXT('\\');
                    lstrcpy (origFilePath, buffer);
                    bDeleteTempFile = TRUE;
                } else {
                    if (!pDoesFileExist (origFilePath)) {
                        //
                        // try the compressed form
                        //
                        p = _tcschr (origFilePath, 0);
                        MYASSERT (p);
                        if (!p) {
                            continue;
                        }
                        p = _tcsdec (origFilePath, p);
                        MYASSERT (p);
                        if (!p) {
                            continue;
                        }
                        *p = TEXT('_');
                        if (!pDoesFileExist (origFilePath)) {
                            DynUpdtDebugLog (
                                Winnt32LogError,
                                TEXT("pProcessUpdates: Unable to find original file %1 to apply the patch"),
                                0,
                                origSubPath
                                );
                            b = FALSE;
                            break;
                        }
                        //
                        // expand the file to the temp dir
                        //
                        BuildPath (buffer, g_DynUpdtStatus->TempDir, origSubPath);
                        p = _tcsrchr (buffer, TEXT('\\'));
                        MYASSERT (p);
                        if (!p) {
                            continue;
                        }
                        *p = 0;
                        if (CreateMultiLevelDirectory (buffer) != ERROR_SUCCESS) {
                            DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpdates: Unable to create dir %1"), 0, buffer);
                            b = FALSE;
                            break;
                        }
                        if (!(*SetupapiCabinetRoutine) (origFilePath, 0, pExpandCabInDir, buffer)) {
                            DynUpdtDebugLog (
                                Winnt32LogError,
                                TEXT("pProcessUpdates: Unable to expand original file %1 to dir %2"),
                                0,
                                origFilePath,
                                buffer
                                );
                            b = FALSE;
                            break;
                        }
                        *p = TEXT('\\');
                        lstrcpy (origFilePath, buffer);
                        bDeleteTempFile = TRUE;
                    }
                }
                BuildPath (destFilePath, updatesSourceDir, TEXT("$$temp$$.~~~"));
                //
                // now really apply the patch
                //
                if (!ApplyPatchToFile (e.FullPath, origFilePath, destFilePath, 0)) {
                    DynUpdtDebugLog (
                        Winnt32LogError,
                        TEXT("pProcessUpdates: ApplyPatchToFile failed to apply patch %1 to file %2"),
                        0,
                        e.FullPath,
                        origFilePath
                        );
                    b = FALSE;
                    break;
                }
                //
                // success! now move the file to the real destination
                //
                BuildPath (buffer, updatesSourceDir, origSubPath);
                p = _tcsrchr (buffer, TEXT('\\'));
                MYASSERT (p);
                if (!p) {
                    continue;
                }
                *p = 0;
                if (CreateMultiLevelDirectory (buffer) != ERROR_SUCCESS) {
                    DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpdates: Unable to create dir %1"), 0, buffer);
                    b = FALSE;
                    break;
                }
                *p = TEXT('\\');
                SetFileAttributes (buffer, FILE_ATTRIBUTE_NORMAL);
                DeleteFile (buffer);
                if (!MoveFile (destFilePath, buffer)) {
                    DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpdates: Unable to move file %1 to final dest %2"), 0, destFilePath, buffer);
                    b = FALSE;
                    break;
                }
                if (!GetFileVersion (buffer, version)) {
                    lstrcpy (version, TEXT("<unknown>"));
                }
                DynUpdtDebugLog (
                    DynUpdtLogLevel,
                    TEXT("pProcessUpdates: successfully applied patch %1 to file %2; the new file %3 has version %4"),
                    0,
                    e.FullPath,
                    origFilePath,
                    buffer,
                    version
                    );
                //
                // now remove the patch file
                //
                SetFileAttributes (e.FullPath, FILE_ATTRIBUTE_NORMAL);
                DeleteFile (e.FullPath);
                bPatchApplied = TRUE;
                if (bDeleteTempFile) {
                    SetFileAttributes (origFilePath, FILE_ATTRIBUTE_NORMAL);
                    DeleteFile (origFilePath);
                }
            } while (EnumNextFilePatternRecursive (&e));
            AbortEnumFilePatternRecursive (&e);

            UnloadInfFile (driverInfHandle);

            if (!b) {
                goto exit;
            }
        }
#endif

        //
        // build a new updates.cab that will contain the patched versions of files
        // and no relative paths
        //

        BuildPath (updatesCabPath, g_DynUpdtStatus->TempDir, S_CABNAME_UPDATES);
        SetFileAttributes (updatesCabPath, FILE_ATTRIBUTE_NORMAL);
        DeleteFile (updatesCabPath);

        hCabContext = DiamondStartNewCabinet (updatesCabPath);
        if (!hCabContext) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpdates: DiamondStartNewCabinet failed"), 0);
            b = FALSE;
            goto exit;
        }
        bCatalogFileFound = FALSE;
        if (EnumFirstFilePatternRecursive (&e, updatesSourceDir, TEXT("*"), 0)) {
            do {
                if (e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    continue;
                }
                hCab = hCabContext;
                cabPath = updatesCabPath;
                //
                // search for a previous file with the same name
                //
                if (FindStringCell (listUpdatesFiles, e.FileName, FALSE)) {
                    DynUpdtDebugLog (
                        Winnt32LogError,
                        TEXT("pProcessUpdates: found duplicate filename %1; aborting operation"),
                        0,
                        updatesCabPath
                        );
                    b = FALSE;
                    break;
                }
                if (!InsertList (
                        (PGENERIC_LIST*)&listUpdatesFiles,
                        (PGENERIC_LIST)CreateStringCell (e.FileName))
                        ) {
                    b = FALSE;
                    break;
                }
                b = DiamondAddFileToCabinet (hCab, e.FullPath, e.FileName);
                if (!b) {
                    DynUpdtDebugLog (
                        Winnt32LogError,
                        TEXT("pProcessUpdates: DiamondAddFileToCabinet(%1,%2) failed"),
                        0,
                        e.FullPath,
                        cabPath
                        );
                    break;
                }
                DynUpdtDebugLog (
                    DynUpdtLogLevel,
                    TEXT(" ... successfully added file %1 to %2"),
                    0,
                    e.FullPath,
                    cabPath
                    );

                p = GetFileExtension (e.FileName);
                if (p && !lstrcmpi (p, TEXT(".cat"))) {
                    bCatalogFileFound = TRUE;
                }

            } while (EnumNextFilePatternRecursive (&e));
            AbortEnumFilePatternRecursive (&e);
            if (!b) {
                goto exit;
            }
        }
        result = DiamondTerminateCabinet (hCabContext);
        if (!b) {
            DiamondTerminateCabinet (hCabContext);
            goto exit;
        }
        if (!result) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpdates: DiamondTerminateCabinet(%1) failed"), 0, updatesCabPath);
            b = FALSE;
            goto exit;
        }
        DynUpdtDebugLog (DynUpdtLogLevel, TEXT(" ... done"), 0);

        if (!bCatalogFileFound) {
            DynUpdtDebugLog (Winnt32LogWarning, TEXT("pProcessUpdates: no catalog found in package %1"), 0, UpdatesCab);
        }

        BuildPath (
            buffer,
            g_DynUpdtStatus->PrepareWinnt32 ? g_DynUpdtStatus->DynamicUpdatesSource : g_DynUpdtStatus->WorkingDir,
            S_CABNAME_UPDATES
            );
        if (!SetFileAttributes (buffer, FILE_ATTRIBUTE_NORMAL) ||
            !DeleteFile (buffer)) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpdates: Unable to remove file %1 in order to replace it"), 0, buffer);
            b = FALSE;
            goto exit;
        }
        SetFileAttributes (buffer, FILE_ATTRIBUTE_NORMAL);
        DeleteFile (buffer);
        if (!MoveFile (updatesCabPath, buffer)) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpdates: Unable to move file %1 to %2"), 0, updatesCabPath, buffer);
            b = FALSE;
            goto exit;
        }
        DynUpdtDebugLog (DynUpdtLogLevel, TEXT("pProcessUpdates: moved file %1 to %2"), 0, updatesCabPath, buffer);
        lstrcpy (updatesCabPath, buffer);

        BuildSifName (updatesCabPath, destFilePath);
        if (!CreateFileListSif (destFilePath, S_SECTIONNAME_UPDATES, updatesCabPath)) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to build file %1"), 0, destFilePath);
            b = FALSE;
            goto exit;
        }
        DynUpdtDebugLog (
            DynUpdtLogLevel,
            TEXT("pProcessUpdates: created %1 containing the list of files in %2"),
            0,
            destFilePath,
            updatesCabPath
            );
    }

    if (!g_DynUpdtStatus->PrepareWinnt32) {
        //
        // build the default path to updates.cab used by the rest of setup
        //
        MYASSERT (IsArc() ? LocalSourceWithPlatform[0] : LocalBootDirectory[0]);
        BuildPath (
            g_DynUpdtStatus->UpdatesCabTarget,
            IsArc() ? LocalSourceWithPlatform : LocalBootDirectory,
            S_CABNAME_UPDATES
            );
        //
        // remember current location of updates.cab
        //
        lstrcpy (g_DynUpdtStatus->UpdatesCabSource, UpdatesCab);
        //
        // the location of updated files for replacement
        //
        lstrcpy (g_DynUpdtStatus->UpdatesPath, updatesSourceDir);
        //
        // also check for the presence of a file that will cause winnt32 to build the ~LS directory
        //
        BuildPath (destFilePath, updatesSourceDir, S_MAKE_LS_FILENAME);
        if (pDoesFileExist (destFilePath)) {
            MakeLocalSource = TRUE;
        }
    }

exit:
    if (hDiamond) {
        DiamondTerminate (hDiamond);
    }
    if (listUpdatesFiles) {
        DeleteStringList (listUpdatesFiles);
    }

    if (!b && UpgradeAdvisorMode) {
        //
        // in UpgradeAdvisor mode we expect failures
        //
        DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Unable to process %1 in UpgradeAdvisor mode; ignoring error"), 0, UpdatesCab);
        g_DynUpdtStatus->ForceRemoveWorkingDir = TRUE;
        b = TRUE;
    }

    return b;
}


BOOL
pProcessDuasms (
    IN      PCTSTR DuasmsCab,
    IN      BOOL ClientInstall
    )
{
    FILEPATTERN_ENUM e;
    TCHAR duasmsLocalDir[MAX_PATH];
    TCHAR dirName[MAX_PATH];
    DWORD rc;
    HKEY key;
    PCTSTR strDuasmsRegKey;
    BOOL duasms = FALSE;
    BOOL b = TRUE;

    if (!pNonemptyFilePresent (DuasmsCab)) {
        if (!ClientInstall) {
            DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Package %1 is not present"), 0, DuasmsCab);
        }
        return TRUE;
    }

    if (g_DynUpdtStatus->PrepareWinnt32) {
        DynUpdtDebugLog (DynUpdtLogLevel, TEXT("pProcessDuasms: Skipping it due to /%1 switch"), 0, WINNT_U_DYNAMICUPDATESPREPARE);
        return TRUE;
    }

    DynUpdtDebugLog (
        DynUpdtLogLevel,
        TEXT("Analyzing package %1..."),
        0,
        DuasmsCab
        );

    BuildPath (duasmsLocalDir, g_DynUpdtStatus->WorkingDir, S_SUBDIRNAME_DUASMS);

    //
    // expand CAB in this dir
    //
    MyDelnode (duasmsLocalDir);
    if (CreateMultiLevelDirectory (duasmsLocalDir) != ERROR_SUCCESS) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to create dir %1"), 0, duasmsLocalDir);
        return FALSE;
    }
    if (!(*SetupapiCabinetRoutine) (DuasmsCab, 0, pExpandCabInDir, (PVOID)duasmsLocalDir)) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to expand cabinet %1"), 0, DuasmsCab);
        return FALSE;
    }

    MYASSERT (IsArc() ? LocalSourceWithPlatform[0] : LocalBootDirectory[0]);
    BuildPath (
        g_DynUpdtStatus->DuasmsTarget,
        IsArc() ? LocalSourceWithPlatform : LocalBootDirectory,
        S_SUBDIRNAME_DUASMS
        );
    //
    // remember current location of duasms folder
    //
    lstrcpy (g_DynUpdtStatus->DuasmsSource, duasmsLocalDir);

    return TRUE;
}


BOOL
pFindPackage (
    IN      HINF InfHandle,
    IN      PCTSTR Section,
    IN      PCTSTR CabName,
    OUT     PBOOL Partial
    )
{
    INFCONTEXT ic;
    TCHAR value[MAX_PATH];

    if (SetupapiFindFirstLine (InfHandle, Section, NULL, &ic)) {
        do {
            if (SetupapiGetStringField (&ic, GUIDRVS_FIELD_CABNAME, value, MAX_PATH, NULL) &&
                !lstrcmpi (value, CabName)
                ) {
                if (Partial) {
                    *Partial = SetupapiGetStringField (&ic, GUIDRVS_FIELD_INFNAME, value, MAX_PATH, NULL);
                }
                return TRUE;
            }
        } while (SetupapiFindNextLine (&ic, &ic));
    }
    return FALSE;
}

VOID
pSanitizeDriverCabName (
    IN      PTSTR CabName
    )
{
#define CRC_SUFFIX_LENGTH       40

    PTSTR p, q;
    DWORD len;
    //
    // cut an extension like _B842485F4D3B024E675653929B247BE9C685BBD7 from the cab name
    //
    p = GetFileExtension (CabName);
    if (p) {
        MYASSERT (*p == TEXT('.'));
        *p = 0;
        q = _tcsrchr (CabName, TEXT('_'));
        if (q) {
            q++;
            len = lstrlen (q);
            if (len == CRC_SUFFIX_LENGTH) {
                PTSTR s = q;
                TCHAR ch;
                while (ch = (TCHAR)_totlower (*s++)) {
                    if (!((ch >= TEXT('0') && ch <= TEXT('9')) ||
                          (ch >= TEXT('a') && ch <= TEXT('f')))
                        ) {
                        break;
                    }
                }
                if (!ch) {
                    //
                    // we found what we expect
                    //
                    *(q - 1) = TEXT('.');
                    lstrcpy (q, p + 1);
                    p = NULL;
                }
            }
        }
        if (p) {
            *p = TEXT('.');
        }
    }
}


BOOL
pIsDriverExcluded (
    IN      HINF InfHandle,
    IN      PCTSTR CabName
    )
{
    BOOL bPartial;

    if (!pFindPackage (InfHandle, S_SECTION_EXCLUDED_DRVS, CabName, &bPartial)) {
        return FALSE;
    }
    return !bPartial;
}


BOOL
pIsPrivateCabinet (
    IN      PCTSTR Filename
    )
{
    static PCTSTR privateCabNames[] = {
        S_CABNAME_IDENT,
        S_CABNAME_WSDUENG,
        S_CABNAME_UPDATES,
        S_CABNAME_UPGINFS,
        S_CABNAME_WINNT32,
        S_CABNAME_MIGDLLS,
        S_CABNAME_DUASMS,
    };

    INT i;

    for (i = 0; i < sizeof (privateCabNames) / sizeof (privateCabNames[0]); i++) {
        if (!lstrcmpi (Filename, privateCabNames[i])) {
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
pIsPrivateSubdir (
    IN      PCTSTR Subdir
    )
{
    static PCTSTR privateSubDirNames[] = {
        S_SUBDIRNAME_TEMP,
        S_SUBDIRNAME_DRIVERS,
        S_SUBDIRNAME_WINNT32,
        S_SUBDIRNAME_UPDATES,
        S_SUBDIRNAME_UPGINFS,
        S_SUBDIRNAME_MIGDLLS,
        S_SUBDIRNAME_DUASMS,
    };

    INT i;

    for (i = 0; i < sizeof (privateSubDirNames) / sizeof (privateSubDirNames[0]); i++) {
        if (!lstrcmpi (Subdir, privateSubDirNames[i])) {
            return TRUE;
        }
    }

    return FALSE;
}


BOOL
pFindValueInSectionAtFieldIndex (
    IN      HINF InfHandle,
    IN      PCTSTR Section,
    IN      DWORD FieldIndex,
    IN      PCTSTR FieldValue
    )
{
    INFCONTEXT ic;
    TCHAR value[MAX_PATH];

    if (SetupapiFindFirstLine (InfHandle, Section, NULL, &ic)) {
        do {
            if (SetupapiGetStringField (&ic, FieldIndex, value, MAX_PATH, NULL)) {
                if (lstrcmpi (FieldValue, value) == 0) {
                    return TRUE;
                }
            }
        } while (SetupapiFindNextLine (&ic, &ic));
    }
    return FALSE;
}


BOOL
pProcessNewdrvs (
    IN      PCTSTR NewdrvDir,
    IN      BOOL ClientInstall
    )

/*++

   All CABs in this dir except pIsPrivateCabinet() files are considered
   as containing new drivers. Each cab will be expanded in its own subdir (derived from cab filename)

--*/

{
    FILEPATTERN_ENUM e;
    FILEPATTERNREC_ENUM er;
    TCHAR dirName[MAX_PATH];
    TCHAR datFile[MAX_PATH];
    TCHAR relocDriverPath[MAX_PATH];
    PTSTR p;
    PSDLIST entry;
    HANDLE hDB;
    BOOL bCreateHwdb;
    BOOL bDriverNeeded;
    HINF infHandle;
    enum {
        CT_UNKNOWN,
        CT_GUI_APPROVED,
        CT_GUI_NOT_APPROVED
    } eContentType;
    BOOL bDriverIsGuiApproved;
    PSTRINGLIST missingPnpIds = NULL;
    PSTRINGLIST listEntry;
    BOOL bEntryFound;
    INFCONTEXT ic;
    TCHAR value[MAX_PATH];
    TCHAR sanitizedName[MAX_PATH];
    BOOL b = TRUE;

    __try {
        //
        // first open guidrvs.inf
        //
        BuildPath (datFile, g_DynUpdtStatus->DynamicUpdatesSource, S_GUI_DRIVERS_INF);
        infHandle = SetupapiOpenInfFile (datFile, NULL, INF_STYLE_WIN4, NULL);
        if (infHandle != INVALID_HANDLE_VALUE) {
            //
            // copy this file together with the drivers packages (if any)
            //
            lstrcpy (g_DynUpdtStatus->GuidrvsInfSource, datFile);
        } else {
            DynUpdtDebugLog (
                Winnt32LogWarning,
                TEXT("Could not open INF file %1 (rc=%2!u!)"),
                0,
                datFile,
                GetLastError ()
                );
        }
        //
        // look for CAB files and expand each one in its own subdir
        //
        if (!ClientInstall) {
            if (EnumFirstFilePatternRecursive (&er, NewdrvDir, TEXT("*.cab"), ECF_ENUM_SUBDIRS)) {
                do {
                    if (g_DynUpdtStatus->Cancelled) {
                        SetLastError (ERROR_CANCELLED);
                        b = FALSE;
                        break;
                    }

                    if (er.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                        if (pIsPrivateSubdir (er.SubPath)) {
                            er.ControlFlags |= ECF_ABORT_ENUM_DIR;
                        }
                        continue;
                    }
                    if (!er.FindData->nFileSizeLow) {
                        DynUpdtDebugLog (Winnt32LogWarning, TEXT("File %1 has size 0 and will be ignored"), 0, er.FullPath);
                        continue;
                    }

                    lstrcpy (sanitizedName, er.FileName);
                    pSanitizeDriverCabName (sanitizedName);
                    if (pIsPrivateCabinet (sanitizedName)) {
                        continue;
                    }

                    BuildPath (dirName, g_DynUpdtStatus->DriversSource, sanitizedName);
                    p = GetFileExtension (dirName);
                    if (!p) {
                        MYASSERT (FALSE);
                        continue;
                    }
                    *p = 0;
                    //
                    // is this an excluded driver?
                    //
                    lstrcpy (datFile, sanitizedName);
                    p = GetFileExtension (datFile);
                    if (!p) {
                        MYASSERT (FALSE);
                        continue;
                    }
                    *p = 0;
                    if (pIsDriverExcluded (infHandle, datFile)) {
                        DynUpdtDebugLog (
                            Winnt32LogWarning,
                            TEXT("Driver %1 is excluded from processing via %2"),
                            0,
                            sanitizedName,
                            g_DynUpdtStatus->GuidrvsInfSource
                            );
                        if (DoesDirectoryExist (dirName)) {
                            //
                            // make sure there's no hwcomp.dat in this folder
                            //
                            BuildPath (datFile, dirName, S_HWCOMP_DAT);
                            if (pDoesFileExist (datFile)) {
                                SetFileAttributes (datFile, FILE_ATTRIBUTE_NORMAL);
                                DeleteFile (datFile);
                            }
                        }
                        continue;
                    }

                    DynUpdtDebugLog (
                        DynUpdtLogLevel,
                        TEXT("Analyzing driver package %1..."),
                        0,
                        er.FullPath
                        );

                    if (DoesDirectoryExist (dirName)) {
                        DynUpdtDebugLog (
                            Winnt32LogWarning,
                            TEXT("Recreating existing driver %1"),
                            0,
                            dirName
                            );
                        MyDelnode (dirName);
                    }
                    if (CreateMultiLevelDirectory (dirName) != ERROR_SUCCESS) {
                        DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to create dir %1"), 0, dirName);
                        continue;
                    }
                    //
                    // expand CAB in this dir
                    //
                    if (!(*SetupapiCabinetRoutine) (er.FullPath, 0, pExpandCabInDir, dirName)) {
                        DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to expand cabinet %1"), 0, er.FullPath);
                        if (GetLastError () == ERROR_DISK_FULL) {
                            DynUpdtDebugLog (Winnt32LogSevereError, TEXT("Disk is full; aborting operation"), 0);
                            b = FALSE;
                            break;
                        }
                        continue;
                    }
                    if (g_DynUpdtStatus->PrepareWinnt32) {
                        //
                        // just rebuild the hardware database
                        //
                        if (!pBuildHwcompDat (dirName, infHandle, TRUE, TRUE)) {
                            DynUpdtDebugLog (
                                Winnt32LogError,
                                TEXT("Unable to build %1 (pBuildHwcompDat failed)"),
                                0,
                                dirName
                                );
                            continue;
                        }
                    }

                } while (EnumNextFilePatternRecursive (&er));
                AbortEnumFilePatternRecursive (&er);
            }
        }

        if (!b) {
            __leave;
        }

        if (!g_DynUpdtStatus->PrepareWinnt32 &&
            (!ISNT() || OsVersion.dwMajorVersion > 4)
            ) {
            //
            // look for driver dirs and analyze them
            //
            if (infHandle != INVALID_HANDLE_VALUE) {
                //
                // read the value of "DriversAreGuiApproved" key
                // 1. if set to "Yes" that means all drivers listed in the [Drivers] section are approved
                //    for installation in GUI setup; any other driver is not approved for installation in GUI setup
                // 2. if set to "No" all drivers listed are NOT good for installation in GUI setup; their install
                //    will be deferred post setup; any driver not listed in that section is good for GUI
                // 3. if not present or set to any other value, it is ignored and the section [Drivers] is ignored;
                //    all drivers will be installed post GUI setup
                //
                eContentType = CT_UNKNOWN;
                if (SetupapiFindFirstLine (infHandle, S_SECTION_VERSION, S_DRIVER_TYPE_KEY, &ic) &&
                    SetupapiGetStringField (&ic, 1, value, MAX_PATH, NULL)
                    ) {
                    if (!lstrcmpi (value, WINNT_A_YES)) {
                        eContentType = CT_GUI_APPROVED;
                    } else if (!lstrcmpi (value, WINNT_A_NO)) {
                        eContentType = CT_GUI_NOT_APPROVED;
                    }
                }
                if (eContentType != CT_UNKNOWN) {
                    DynUpdtDebugLog (
                        Winnt32LogInformation,
                        TEXT("Entries in section [%1] of %2 will be treated as drivers to %3 be installed during GUI setup"),
                        0,
                        S_SECTION_DRIVERS,
                        g_DynUpdtStatus->GuidrvsInfSource,
                        eContentType == CT_GUI_APPROVED ? TEXT("") : TEXT("NOT")
                        );
                } else {
                    DynUpdtDebugLog (
                        Winnt32LogWarning,
                        TEXT("Key %1 %5 in file %2 section [%3];")
                        TEXT(" entries in section [%4] will be ignored and all drivers will be installed post setup"),
                        0,
                        S_DRIVER_TYPE_KEY,
                        g_DynUpdtStatus->GuidrvsInfSource,
                        S_SECTION_VERSION,
                        S_SECTION_DRIVERS,
                        value ? TEXT("has an invalid value") : TEXT("is not present")
                        );
                }
            }

            if (EnumFirstFilePattern (&e, g_DynUpdtStatus->DriversSource, TEXT("*"))) {

                //
                // initialize the Whistler PNP database
                //
                if (!g_DynUpdtStatus->HwdbDatabase) {
                    //
                    // ignore db load error
                    //
                    pInitNtPnpDb (TRUE);
                }

                do {
                    if (g_DynUpdtStatus->Cancelled) {
                        SetLastError (ERROR_CANCELLED);
                        b = FALSE;
                        break;
                    }

                    if (!(e.FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
                        DynUpdtDebugLog (DynUpdtLogLevel, TEXT("File %1 is NOT a directory and will be ignored"), 0, e.FullPath);
                        continue;
                    }

                    //
                    // is this a needed driver?
                    //
                    bDriverNeeded = TRUE;
                    if (g_DynUpdtStatus->UserSpecifiedUpdates) {
                        //
                        // first build the list of missing drivers
                        //
                        if (!missingPnpIds) {
#ifdef UNICODE
                            missingPnpIds = BuildMissingPnpIdList ();
                            if (!missingPnpIds) {
                                DynUpdtDebugLog (
                                    Winnt32LogInformation,
                                    TEXT("No PNP device drivers are needed"),
                                    0
                                    );
                            }
#else
                            //
                            // let the upgrade module do driver detection on Win9x
                            //
                            if (pLoadWin9xDuSupport ()) {
                                PSTR* incompWin9xDrivers;
                                PCSTR* q;
                                if (g_DynUpdtStatus->Win9xGetIncompDrvs (&incompWin9xDrivers)) {
                                    //
                                    // convert the array returned by this function to a list style
                                    //
                                    g_DynUpdtStatus->IncompatibleDriversCount = 0;
                                    if (incompWin9xDrivers) {
                                        for (q = incompWin9xDrivers; *q; q++) {
                                            listEntry = (PSTRINGLIST) MALLOC (sizeof (STRINGLIST));
                                            if (listEntry) {
                                                listEntry->String = DupMultiSz (*q);
                                                if (!listEntry->String) {
                                                    break;
                                                }
                                                listEntry->Next = NULL;
                                                if (!InsertList ((PGENERIC_LIST*)&missingPnpIds, (PGENERIC_LIST)listEntry)) {
                                                    DeleteStringCell (listEntry);
                                                    break;
                                                }
                                                g_DynUpdtStatus->IncompatibleDriversCount++;
                                            }
                                        }
                                    }
                                    if (g_DynUpdtStatus->Win9xReleaseIncompDrvs) {
                                        g_DynUpdtStatus->Win9xReleaseIncompDrvs (incompWin9xDrivers);
                                    }
                                } else {
                                    DynUpdtDebugLog (
                                        DynUpdtLogLevel,
                                        TEXT("Win9xGetIncompDrvs returned FALSE; no drivers will be analyzed"),
                                        0
                                        );
                                }
                            }
#endif
                            if (!missingPnpIds) {
                                break;
                            }
                        }

                        bCreateHwdb = FALSE;
                        //
                        // use the existing hardware database
                        //
                        BuildPath (datFile, e.FullPath, S_HWCOMP_DAT);
                        if (!pDoesFileExist (datFile)) {
                            bCreateHwdb = TRUE;
                        }
                        hDB = g_DynUpdtStatus->HwdbOpen (bCreateHwdb ? NULL : datFile);
                        if (!hDB) {
                            if (bCreateHwdb) {
                                b = FALSE;
                                break;
                            }
                            DynUpdtDebugLog (
                                Winnt32LogError,
                                TEXT("Hardware database %1 is corrupt; contact your system administrator"),
                                0,
                                datFile
                                );
                            continue;
                        }

                        if (bCreateHwdb) {
                            if (!g_DynUpdtStatus->HwdbAppendInfs (
                                    hDB,
                                    e.FullPath,
                                    infHandle != INVALID_HANDLE_VALUE ? Winnt32HwdbAppendInfsCallback : NULL,
                                    (PVOID)infHandle
                                    )) {
                                DynUpdtDebugLog (
                                    Winnt32LogError,
                                    TEXT("Unable to build %1; contact your system administrator"),
                                    0,
                                    datFile
                                    );
                                g_DynUpdtStatus->HwdbClose (hDB);
                                continue;
                            }
                            //
                            // rebuild the default HW precompiled database
                            //
                            BuildPath (datFile, e.FullPath, S_HWCOMP_DAT);
                            SetFileAttributes (datFile, FILE_ATTRIBUTE_NORMAL);
                            DeleteFile (datFile);
                            if (!g_DynUpdtStatus->HwdbFlush (hDB, datFile)) {
                                DynUpdtDebugLog (
                                    Winnt32LogError,
                                    TEXT("Unable to build %1; contact your system administrator"),
                                    0,
                                    datFile
                                    );
                                g_DynUpdtStatus->HwdbClose (hDB);
                                continue;
                            }
                            DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Successfully built precompiled hardware database %1"), 0, datFile);
                        }

                        //
                        // check if this particular driver is among the ones that are needed
                        //
                        if (!pHwdbHasAnyMissingDrivers (hDB, missingPnpIds)) {
                            //
                            // this driver is not needed
                            //
                            bDriverNeeded = FALSE;
                        }

                        g_DynUpdtStatus->HwdbClose (hDB);
                    }

                    if (!bDriverNeeded) {
                        DynUpdtDebugLog (DynUpdtLogLevel, TEXT("No needed drivers found in package %1"), 0, e.FullPath);
                        continue;
                    }

                    //
                    // is this a boot driver or a regular one?
                    //
                    if (pIsBootDriver (e.FullPath)) {
                        //
                        // add this driver to the list of boot drivers
                        //
                        if (!InsertList (
                                (PGENERIC_LIST*)&g_DynUpdtStatus->BootDriverPathList,
                                (PGENERIC_LIST)CreateStringCell (e.FileName))
                                ) {
                            b = FALSE;
                            break;
                        }
                        DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Added driver %1 to the list of BOOT drivers"), 0, e.FullPath);
                    }
                    //
                    // all needed drivers will be copied under LocalBootDir to be protected
                    // from being deleted if user decides to remove the current OS partition
                    //
                    BuildPath (relocDriverPath, IsArc() ? LocalSourceWithPlatform : LocalBootDirectory, S_SUBDIRNAME_DRIVERS);
                    ConcatenatePaths (relocDriverPath, e.FileName, MAX_PATH);

                    //
                    // is this a GUI driver or not?
                    //
                    if (eContentType == CT_UNKNOWN) {
                        bDriverIsGuiApproved = FALSE;
                    } else {
                        if (pFindValueInSectionAtFieldIndex (infHandle, S_SECTION_EXCLUDED_DRVS, GUIDRVS_FIELD_CABNAME, e.FileName)) {
                            //
                            // we don't support "partially excluded" packages for the device install
                            // phase of GUI setup
                            //
                            DynUpdtDebugLog (
                                DynUpdtLogLevel,
                                TEXT("Driver %1 is partially excluded; it will be installed at the end of GUI setup"),
                                0,
                                e.FullPath
                                );
                            bDriverIsGuiApproved = FALSE;
                        } else {
                            BOOL bPartial;
                            bEntryFound = pFindPackage (infHandle, S_SECTION_DRIVERS, e.FileName, &bPartial);
                            bDriverIsGuiApproved = eContentType == CT_GUI_APPROVED && bEntryFound && !bPartial ||
                                                   eContentType == CT_GUI_NOT_APPROVED && !bEntryFound;
                        }
                    }

                    //
                    // always make sure there's a precompiled database hwcomp.dat
                    // to be used at the time setup will install these additional drivers
                    //
                    if (!pBuildHwcompDat (e.FullPath, infHandle, FALSE, FALSE)) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("Unable to build %1 (pBuildHwcompDat failed)"),
                            0,
                            datFile
                            );
                        continue;
                    }

                    entry = MALLOC (sizeof (SDLIST));
                    if (!entry) {
                        b = FALSE;
                        break;
                    }
                    entry->String = DupString (relocDriverPath);
                    if (!entry->String) {
                        FREE (entry);
                        b = FALSE;
                        break;
                    }
                    entry->Data = (DWORD_PTR)bDriverIsGuiApproved;
                    entry->Next = NULL;
                    if (!InsertList (
                            (PGENERIC_LIST*)&g_DynUpdtStatus->NewDriversList,
                            (PGENERIC_LIST)entry
                            )) {
                        FREE (entry);
                        b = FALSE;
                        break;
                    }

                    DynUpdtDebugLog (
                        DynUpdtLogLevel,
                        bDriverIsGuiApproved ?
                            TEXT("Driver %1 is approved for installation during GUI setup") :
                            TEXT("Driver %1 is NOT approved for installation during GUI setup; installation will be deferred post-setup"),
                        0,
                        e.FullPath
                        );

                    //
                    // copy locally this driver package (if from a share)
                    //
                    BuildPath (relocDriverPath, g_DynUpdtStatus->SelectedDrivers, e.FileName);
                    if (lstrcmpi (e.FullPath, relocDriverPath)) {
                        if (!CopyTree (e.FullPath, relocDriverPath)) {
                            DynUpdtDebugLog (
                                Winnt32LogError,
                                TEXT("Unable to copy driver %1 to %2"),
                                0,
                                e.FullPath,
                                relocDriverPath
                                );
                            b = FALSE;
                            break;
                        }
                        DynUpdtDebugLog (
                            Winnt32LogInformation,
                            TEXT("Driver %1 successfully copied to %2"),
                            0,
                            e.FullPath,
                            relocDriverPath
                            );
                    }

                } while (EnumNextFilePattern (&e));
                AbortEnumFilePattern (&e);
            } else {
                DynUpdtDebugLog (
                    DynUpdtLogLevel,
                    TEXT("No drivers found in %1"),
                    0,
                    g_DynUpdtStatus->DriversSource
                    );
            }

            if (!b) {
                __leave;
            }

            //
            // copy guidrvs.inf if present and any driver package that will be migrated over
            //
            if (g_DynUpdtStatus->GuidrvsInfSource[0] && g_DynUpdtStatus->NewDriversList) {
                BuildPath (datFile, g_DynUpdtStatus->SelectedDrivers, S_GUI_DRIVERS_INF);
                if (lstrcmpi (g_DynUpdtStatus->GuidrvsInfSource, datFile)) {
                    if (!CopyFile (g_DynUpdtStatus->GuidrvsInfSource, datFile, FALSE)) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("Failed to copy %1 to %2"),
                            0,
                            g_DynUpdtStatus->GuidrvsInfSource,
                            datFile
                            );
                        b = FALSE;
                    }
                }
                if (b) {
                    //
                    // update the location of guidrvs.inf after file copy will have been done
                    //
                    BuildPath (
                        g_DynUpdtStatus->GuidrvsInfSource,
                        IsArc() ? LocalSourceWithPlatform : LocalBootDirectory,
                        S_SUBDIRNAME_DRIVERS
                        );
                    ConcatenatePaths (g_DynUpdtStatus->GuidrvsInfSource, S_GUI_DRIVERS_INF, MAX_PATH);
                }
            }
        }
    }
    __finally {
        if (missingPnpIds) {
            DeleteStringList (missingPnpIds);
        }

        if (infHandle != INVALID_HANDLE_VALUE) {
            SetupapiCloseInfFile (infHandle);
        }
    }

    return b;
}


BOOL
pProcessUpginfs (
    IN      PCTSTR UpginfsCab,
    IN      BOOL ClientInstall
    )
{
    FILEPATTERNREC_ENUM e;
    TCHAR upginfsSourceDir[MAX_PATH];
    TCHAR upginfsDir[MAX_PATH];
    TCHAR upginfsFile[MAX_PATH];
    TCHAR origSubPath[MAX_PATH];
    TCHAR origFileName[MAX_PATH];
    TCHAR origFilePath[MAX_PATH];
    TCHAR destFilePath[MAX_PATH];
    TCHAR buffer[MAX_PATH];
    PTSTR p;
    BOOL b = TRUE;

    BuildPath (upginfsSourceDir, g_DynUpdtStatus->DynamicUpdatesSource, S_SUBDIRNAME_UPGINFS);
    if (ClientInstall) {
        if (!DoesDirectoryExist (upginfsSourceDir)) {
            return TRUE;
        }
    } else {

        //
        // expand it in the corresponding subdir
        //
        if (!pNonemptyFilePresent (UpginfsCab)) {
            DynUpdtDebugLog (DynUpdtLogLevel, TEXT("Package %1 is not present"), 0, UpginfsCab);
            return TRUE;
        }

        DynUpdtDebugLog (
            DynUpdtLogLevel,
            TEXT("Analyzing package %1..."),
            0,
            UpginfsCab
            );

        //
        // expand CAB in this dir
        // make sure dir is initially empty
        //
        MyDelnode (upginfsSourceDir);
        if (CreateMultiLevelDirectory (upginfsSourceDir) != ERROR_SUCCESS) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to create dir %1"), 0, upginfsSourceDir);
            return FALSE;
        }
        if (!(*SetupapiCabinetRoutine) (UpginfsCab, 0, pExpandCabInDir, (PVOID)upginfsSourceDir)) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to expand cabinet %1"), 0, UpginfsCab);
            return FALSE;
        }

        //
        // ISSUE: the patching support is currently not available for platforms other than x86
        //
#ifdef _X86_
        //
        // now let's look for any patches
        //
        if (EnumFirstFilePatternRecursive (&e, upginfsSourceDir, S_PATCH_FILE_EXT, 0)) {

            do {
                BOOL bDeleteTempFile = FALSE;

                if (g_DynUpdtStatus->Cancelled) {
                    SetLastError (ERROR_CANCELLED);
                    b = FALSE;
                    break;
                }

                if (e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    continue;
                }
                DynUpdtDebugLog (
                    DynUpdtLogLevel,
                    TEXT("pProcessUpginfs: found patch %1"),
                    0,
                    e.FullPath
                    );
                //
                // get the original file from the sources location
                // the filename is obtained cutting the ._p1 extension
                //
                lstrcpy (origFileName, e.FileName);
                p = GetFileExtension (origFileName);
                if (!p) {
                    MYASSERT (FALSE);
                    continue;
                }
                *p = 0;
                lstrcpy (origSubPath, e.SubPath);
                p = GetFileExtension (origSubPath);
                if (!p) {
                    MYASSERT (FALSE);
                    continue;
                }
                *p = 0;
                BuildPath (origFilePath, NativeSourcePaths[0], origSubPath);
                //
                // now check if this file (in it's compressed form or not) actually exists
                //
                if (!pDoesFileExist (origFilePath)) {
                    //
                    // try the compressed form
                    //
                    p = _tcschr (origFilePath, 0);
                    MYASSERT (p);
                    if (!p) {
                        continue;
                    }
                    p = _tcsdec (origFilePath, p);
                    MYASSERT (p);
                    if (!p) {
                        continue;
                    }
                    *p = TEXT('_');
                    if (!pDoesFileExist (origFilePath)) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("pProcessUpginfs: Unable to find original file %1 to apply the patch"),
                            0,
                            origSubPath
                            );
                        b = FALSE;
                        break;
                    }
                    //
                    // expand the file to the temp dir
                    //
                    BuildPath (buffer, g_DynUpdtStatus->TempDir, origSubPath);
                    p = _tcsrchr (buffer, TEXT('\\'));
                    MYASSERT (p);
                    if (!p) {
                        continue;
                    }
                    *p = 0;
                    if (CreateMultiLevelDirectory (buffer) != ERROR_SUCCESS) {
                        DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpginfs: Unable to create dir %1"), 0, buffer);
                        b = FALSE;
                        break;
                    }
                    if (!(*SetupapiCabinetRoutine) (origFilePath, 0, pExpandCabInDir, buffer)) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("pProcessUpginfs: Unable to expand original file %1 to dir %2"),
                            0,
                            origFilePath,
                            buffer
                            );
                        b = FALSE;
                        break;
                    }
                    *p = TEXT('\\');
                    lstrcpy (origFilePath, buffer);
                    bDeleteTempFile = TRUE;
                }
                BuildPath (destFilePath, upginfsSourceDir, TEXT("$$temp$$.~~~"));
                //
                // now really apply the patch
                //
                if (!ApplyPatchToFile (e.FullPath, origFilePath, destFilePath, 0)) {
                    DynUpdtDebugLog (
                        Winnt32LogError,
                        TEXT("pProcessUpginfs: ApplyPatchToFile failed to apply patch %1 to file %2"),
                        0,
                        e.FullPath,
                        origFilePath
                        );
                    b = FALSE;
                    break;
                }
                //
                // success! now move the file to the real destination
                //
                BuildPath (buffer, upginfsSourceDir, origFileName);
                if (pDoesFileExist (buffer)) {
                    DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpginfs: duplicate file found %1"), 0, origFileName);
                    b = FALSE;
                    break;
                }
                //
                // all patches MUST be .rep files; change extension from .inf to .rep
                //
                p = GetFileExtension (buffer);
                if (!p || lstrcmpi (p, TEXT(".inf"))) {
                    DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpginfs: Unexpected file extension in %1"), 0, buffer);
                    b = FALSE;
                    break;
                }
                lstrcpy (p, TEXT(".rep"));

                SetFileAttributes (buffer, FILE_ATTRIBUTE_NORMAL);
                DeleteFile (buffer);
                if (!MoveFile (destFilePath, buffer)) {
                    DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpginfs: Unable to move file %1 to final dest %2"), 0, destFilePath, buffer);
                    b = FALSE;
                    break;
                }
                DynUpdtDebugLog (
                    DynUpdtLogLevel,
                    TEXT("pProcessUpginfs: successfully applied patch %1 to file %2; the new file was renamed %3"),
                    0,
                    e.FullPath,
                    origFilePath,
                    buffer
                    );
                //
                // now remove the patch file
                //
                SetFileAttributes (e.FullPath, FILE_ATTRIBUTE_NORMAL);
                DeleteFile (e.FullPath);
                if (bDeleteTempFile) {
                    SetFileAttributes (origFilePath, FILE_ATTRIBUTE_NORMAL);
                    DeleteFile (origFilePath);
                }
            } while (EnumNextFilePatternRecursive (&e));
            AbortEnumFilePatternRecursive (&e);

            if (!b) {
                goto exit;
            }
        }

        SetFileAttributes (UpginfsCab, FILE_ATTRIBUTE_NORMAL);
        DeleteFile (UpginfsCab);
#endif

    }

    if (!b) {
        goto exit;
    }
    if (!g_DynUpdtStatus->PrepareWinnt32) {
        //
        // only do file installation on Win9x platforms
        //
        OSVERSIONINFO vi;
        vi.dwOSVersionInfoSize = sizeof (vi);
        GetVersionEx (&vi);
        if (vi.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
            DynUpdtDebugLog (
                DynUpdtLogLevel,
                TEXT("Package %1 ignored on NT platforms"),
                0,
                UpginfsCab
                );
            return TRUE;
        }

        //
        // prepare the target directory (%windir%\upginfs)
        //
        if (!MyGetWindowsDirectory (upginfsDir, MAX_PATH)) {
            return FALSE;
        }
        ConcatenatePaths (upginfsDir, S_SUBDIRNAME_UPGINFS, MAX_PATH);
        if (!CreateDir (upginfsDir)) {
            return FALSE;
        }

        //
        // copy relevant files to %windir%\Upginfs
        //
        if (EnumFirstFilePatternRecursive (&e, upginfsSourceDir, TEXT("*.add"), 0)) {
            do {
                if (g_DynUpdtStatus->Cancelled) {
                    SetLastError (ERROR_CANCELLED);
                    b = FALSE;
                    break;
                }

                if (e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    continue;
                }
                if (!e.FindData->nFileSizeLow) {
                    DynUpdtDebugLog (Winnt32LogWarning, TEXT("File %1 has size 0 and will be ignored"), 0, e.FullPath);
                    continue;
                }

                BuildPath (upginfsFile, upginfsDir, e.FileName);
                SetFileAttributes (upginfsFile, FILE_ATTRIBUTE_NORMAL);
                if (!CopyFile (e.FullPath, upginfsFile, FALSE)) {
                    DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpginfs: Error copying %1 to %2"), 0, e.FullPath, upginfsFile);
                    b = FALSE;
                    break;
                }
                //
                // let w95upg.dll know about the new files
                //
                UpginfsUpdated = TRUE;
                DynUpdtDebugLog (DynUpdtLogLevel, TEXT("pProcessUpginfs: INF %1 successfully copied to %2"), 0, e.FullPath, upginfsFile);
            } while (EnumNextFilePatternRecursive (&e));
            AbortEnumFilePatternRecursive (&e);
        }

        if (b) {
            if (EnumFirstFilePatternRecursive (&e, upginfsSourceDir, TEXT("*.rep"), 0)) {
                do {
                    if (g_DynUpdtStatus->Cancelled) {
                        SetLastError (ERROR_CANCELLED);
                        b = FALSE;
                        break;
                    }

                    if (e.FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                        continue;
                    }
                    if (!e.FindData->nFileSizeLow) {
                        DynUpdtDebugLog (Winnt32LogWarning, TEXT("File %1 has size 0 and will be ignored"), 0, e.FullPath);
                        continue;
                    }

                    BuildPath (upginfsFile, upginfsDir, e.FileName);
                    SetFileAttributes (upginfsFile, FILE_ATTRIBUTE_NORMAL);
                    if (!CopyFile (e.FullPath, upginfsFile, FALSE)) {
                        DynUpdtDebugLog (Winnt32LogError, TEXT("pProcessUpginfs: Error copying %1 to %2"), 0, e.FullPath, upginfsFile);
                        b = FALSE;
                        break;
                    }
                    //
                    // let w95upg.dll know about the new files
                    //
                    UpginfsUpdated = TRUE;
                    DynUpdtDebugLog (DynUpdtLogLevel, TEXT("pProcessUpginfs: INF %1 successfully copied to %2"), 0, e.FullPath, upginfsFile);
                } while (EnumNextFilePatternRecursive (&e));
                AbortEnumFilePatternRecursive (&e);
            }
        }
    }

exit:

    return b;
}


#ifdef _X86_

BOOL
pProcessMigdlls (
    IN      PCTSTR MigdllsCab,
    IN      BOOL ClientInstall
    )
{
    FILEPATTERN_ENUM e;
    TCHAR migdllsLocalDir[MAX_PATH];
    TCHAR dirName[MAX_PATH];
    DWORD rc;
    HKEY key;
    PCTSTR strMigdllsRegKey;
    BOOL migdlls = FALSE;
    BOOL b = TRUE;

    if (!pNonemptyFilePresent (MigdllsCab)) {
        return TRUE;
    }

    if (g_DynUpdtStatus->PrepareWinnt32) {
        DynUpdtDebugLog (DynUpdtLogLevel, TEXT("pProcessMigdlls: Skipping it due to /%1 switch"), 0, WINNT_U_DYNAMICUPDATESPREPARE);
        return TRUE;
    }

    DynUpdtDebugLog (
        DynUpdtLogLevel,
        TEXT("Analyzing package %1..."),
        0,
        MigdllsCab
        );

    BuildPath (migdllsLocalDir, g_DynUpdtStatus->WorkingDir, S_SUBDIRNAME_MIGDLLS);

    //
    // expand CAB in this dir
    //
    MyDelnode (migdllsLocalDir);
    if (CreateMultiLevelDirectory (migdllsLocalDir) != ERROR_SUCCESS) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to create dir %1"), 0, migdllsLocalDir);
        return FALSE;
    }
    if (!(*SetupapiCabinetRoutine) (MigdllsCab, 0, pExpandCabInDir, (PVOID)migdllsLocalDir)) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to expand cabinet %1"), 0, MigdllsCab);
        return FALSE;
    }

    //
    // look for CAB files and expand each one in its own subdir
    //
    if (EnumFirstFilePattern (&e, migdllsLocalDir, TEXT("*.cab"))) {
        do {
            if (g_DynUpdtStatus->Cancelled) {
                SetLastError (ERROR_CANCELLED);
                b = FALSE;
                break;
            }

            if (e.FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                continue;
            }
            if (!e.FindData.nFileSizeLow) {
                DynUpdtDebugLog (Winnt32LogWarning, TEXT("File %1 has size 0 and will be ignored"), 0, e.FullPath);
                continue;
            }

            pGetAutoSubdirName (e.FullPath, dirName);
            if (CreateMultiLevelDirectory (dirName) != ERROR_SUCCESS) {
                DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to create dir %1; skipping it"), 0, dirName);
                continue;
            }
            //
            // expand CAB in this dir
            //
            if (!(*SetupapiCabinetRoutine) (e.FullPath, 0, pExpandCabInDir, (PVOID)dirName)) {
                DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to expand cabinet %1; skipping it"), 0, e.FullPath);
                continue;
            }

            migdlls = TRUE;

        } while (EnumNextFilePattern (&e));
    }

    if (b && migdlls) {
        //
        // register them
        //
        strMigdllsRegKey = ISNT () ? S_REGKEY_MIGRATION_DLLS_WINNT : S_REGKEY_MIGRATION_DLLS_WIN9X;
        rc = RegCreateKey (HKEY_LOCAL_MACHINE, strMigdllsRegKey, &key);
        if (rc == ERROR_SUCCESS) {
            rc = RegSetValueEx (key, S_REGVALUE_DYNUPDT, 0, REG_SZ, (CONST BYTE*)migdllsLocalDir, (lstrlen (migdllsLocalDir) + 1) * sizeof (TCHAR));
        }
        if (rc != ERROR_SUCCESS) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Unable to register downloaded migdlls (rc=%1!u!)"), 0, rc);
            b = FALSE;
        }
    }

    return b;
}

#endif


BOOL
ProcessDownloadedFiles (
    OUT     PBOOL StopSetup
    )
{
    TCHAR cabName[MAX_PATH];
    BOOL bClientInstall = FALSE;

    if (g_DynUpdtStatus->UserSpecifiedUpdates && !g_DynUpdtStatus->PrepareWinnt32) {
        bClientInstall = TRUE;
    }

    DynUpdtDebugLog (
        DynUpdtLogLevel,
        TEXT("Source=%1"),
        0,
        g_DynUpdtStatus->UserSpecifiedUpdates ? g_DynUpdtStatus->DynamicUpdatesSource : TEXT("Windows Update")
        );

    if (!g_DynUpdtStatus->HwdbInitialize) {
        if (CreateMultiLevelDirectory (g_DynUpdtStatus->TempDir) != ERROR_SUCCESS ||
            !pInitializeSupport (S_HWDB_DLL, pLoadHwdbLib, FALSE) ||
            !g_DynUpdtStatus->HwdbInitialize (g_DynUpdtStatus->TempDir)
            ) {
            return FALSE;
        }
    }

    if (!Winnt32Restarted ()) {
        BuildPath (cabName, g_DynUpdtStatus->DynamicUpdatesSource, S_CABNAME_WINNT32);
        if (!pProcessWinnt32Files (cabName, bClientInstall, StopSetup)) {
            return FALSE;
        }
        if (g_DynUpdtStatus->RestartWinnt32) {
            MYASSERT (!g_DynUpdtStatus->PrepareWinnt32);
            return TRUE;
        }
    }

    BuildPath (cabName, g_DynUpdtStatus->DynamicUpdatesSource, S_CABNAME_UPDATES);
    if (!pProcessUpdates (cabName, bClientInstall, StopSetup)) {
        if (g_DynUpdtStatus->PrepareWinnt32) {
            MessageBoxFromMessage (
                g_DynUpdtStatus->ProgressWindow,
                MSG_ERROR_PROCESSING_UPDATES,
                FALSE,
                AppTitleStringId,
                MB_OK | MB_ICONERROR | MB_TASKMODAL,
                GetLastError (),
                cabName
                );
        }

        return FALSE;
    }

    //
    // process New Assemblies on WU
    //
    BuildPath (cabName, g_DynUpdtStatus->DynamicUpdatesSource, S_CABNAME_DUASMS);
    if (!pProcessDuasms (cabName, bClientInstall)) {
        //
        // don't fail DU if we didn't install them
        //
    }

    BuildPath (cabName, g_DynUpdtStatus->DynamicUpdatesSource, S_CABNAME_UPGINFS);
    if (!pProcessUpginfs (cabName, bClientInstall)) {
        return FALSE;
    }

#ifdef _X86_

    BuildPath (cabName, g_DynUpdtStatus->DynamicUpdatesSource, S_CABNAME_MIGDLLS);
    if (!pProcessMigdlls (cabName, bClientInstall)) {
        return FALSE;
    }

#endif

    if (!pProcessNewdrvs (g_DynUpdtStatus->DynamicUpdatesSource, bClientInstall)) {
        return FALSE;
    }

    return TRUE;
}


BOOL
Winnt32Restarted (
    VOID
    )
{
    return g_DynUpdtStatus->Winnt32Restarted;
}

BOOL
Winnt32RestartedWithAF (
    VOID
    )
{
    return g_DynUpdtStatus->RestartAnswerFile[0];
}

VOID
pLogWininetError (
    IN      DWORD Error
    )
{
    HMODULE hWinInet = LoadLibrary (TEXT("wininet.dll"));
    if (hWinInet) {
        HLOCAL msg = NULL;
        FormatMessage (
                FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                hWinInet,
                Error,
                0,
                (LPTSTR)&msg,
                0,
                NULL
                );
        if (msg) {
            DynUpdtDebugLog (Winnt32LogError, TEXT("Failure with wininet error code %1!u!: \"%2\""), 0, Error, msg);
            LocalFree (msg);
        }
        FreeLibrary (hWinInet);
    }
}

VOID
pLogStandardError (
    IN      DWORD Error
    )
{
    HLOCAL msg = NULL;
    FormatMessage (
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
            NULL,
            Error,
            0,
            (LPTSTR)&msg,
            0,
            NULL
            );
    if (msg) {
        DynUpdtDebugLog (Winnt32LogError, TEXT("Failure with standard error code %1!u!:\r\n%2"), 0, Error, msg);
        LocalFree (msg);
    }
}

VOID
pUpdateDUStatus (
    IN      DWORD Error
    )
{
    MYASSERT (Error != ERROR_SUCCESS);
    if (Error == ERROR_SUCCESS) {
        g_DynUpdtStatus->DUStatus = DUS_ERROR;
        return;
    }
    switch (Error) {
    case ERROR_CONNECTION_UNAVAIL:
        //
        // ask for manual connection
        //
        MYASSERT (g_DynUpdtStatus->DUStatus == DUS_PREPARING);
        g_DynUpdtStatus->DUStatus = DUS_PREPARING_CONNECTIONUNAVAILABLE;
        break;
    case ERROR_INTERNET_INVALID_URL:
    case ERROR_INTERNET_NAME_NOT_RESOLVED:
        //
        // site not available; ask user if they want to retry
        //
        MYASSERT (g_DynUpdtStatus->DUStatus == DUS_PREPARING);
        g_DynUpdtStatus->DUStatus = DUS_PREPARING_INVALIDURL;
        break;
    case ERROR_INVALID_PARAMETER:
    case ERROR_NOT_ENOUGH_MEMORY:
    case ERROR_OLD_WIN_VERSION:
    case ERROR_OUTOFMEMORY:
    case ERROR_NO_MORE_ITEMS:
    case ERROR_FILE_NOT_FOUND:
    case ERROR_INVALID_DATA:
    case ERROR_UNSUPPORTED_TYPE:
    case ERROR_INVALID_HANDLE:
        pLogStandardError (Error);
        g_DynUpdtStatus->DUStatus = DUS_ERROR;
        break;
    case DU_ERROR_MISSING_DLL:
    case DU_NOT_INITIALIZED:
        DynUpdtDebugLog (Winnt32LogError, TEXT("Failure with custom error code %1!u!"), 0, Error);
        g_DynUpdtStatus->DUStatus = DUS_ERROR;
        break;
    case ERROR_INTERNET_NO_CONTEXT:
        pLogWininetError (Error);
        g_DynUpdtStatus->DUStatus = DUS_ERROR;
        break;
    default:
        if (Error > INTERNET_ERROR_BASE) {
            pLogWininetError (Error);
        } else {
            pLogStandardError (Error);
        }
    }
}

DWORD
WINAPI
DoDynamicUpdate (
    LPVOID Parameter
    )
{

#define MIN_INTERVAL_BETWEEN_TASKS 3000

    HWND hUIWindow = (HWND)Parameter;
    DWORD rc = ERROR_SUCCESS;
    LONG ticks;
    LONG sleep;
    DWORD estTime, estSize;
    TCHAR drive[4];
    DWORD sectorsPerCluster;
    DWORD bytesPerSector;
    ULARGE_INTEGER freeClusters = {0, 0};
    ULARGE_INTEGER totalClusters = {0, 0};
    DWORD clusterSize;
    ULONGLONG availableBytes;
    HANDLE hEvent;
    BOOL bStopSetup;
    BOOL bContinue = TRUE;

    hEvent = OpenEvent (EVENT_ALL_ACCESS, FALSE, S_DU_SYNC_EVENT_NAME);
    if (!hEvent) {
        DynUpdtDebugLog (
            Winnt32LogError,
            TEXT("OpenEvent(%1) failed"),
            0,
            S_DU_SYNC_EVENT_NAME
            );
        g_DynUpdtStatus->DUStatus = DUS_ERROR;
        goto exit;
    }

    while (bContinue) {

        if (g_DynUpdtStatus->Cancelled) {
            g_DynUpdtStatus->DUStatus = DUS_CANCELLED;
            rc = ERROR_CANCELLED;
            DynamicUpdateUninitialize ();
            break;
        }

        switch (g_DynUpdtStatus->DUStatus) {

        case DUS_INITIAL:
            if (Winnt32Restarted () || g_DynUpdtStatus->UserSpecifiedUpdates) {
                g_DynUpdtStatus->DUStatus = DUS_PROCESSING;
                break;
            }
            g_DynUpdtStatus->DUStatus = DUS_PREPARING;
            SendMessage (hUIWindow, WMX_SETUPUPDATE_PREPARING, 0, 0);
            break;

        case DUS_PREPARING:
            ticks = GetTickCount ();
            if (!DynamicUpdateInitDownload (hUIWindow)) {
                DynUpdtDebugLog (
                    Winnt32LogError,
                    TEXT("DynamicUpdateInitDownload failed"),
                    0
                    );
                pUpdateDUStatus (GetLastError ());
                if (g_DynUpdtStatus->DUStatus != DUS_SKIP &&
                    g_DynUpdtStatus->DUStatus != DUS_ERROR) {
                    //
                    // the UI thread will decide what the next state will be
                    // based on user's selection
                    //
                    PostMessage (hUIWindow, WMX_SETUPUPDATE_INIT_RETRY, 0, 0);
                    rc = WaitForSingleObject (hEvent, INFINITE);
                    if (rc != WAIT_OBJECT_0) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("WaitForSingleObject failed (%1!u!)"),
                            0,
                            rc
                            );
                        g_DynUpdtStatus->DUStatus = DUS_ERROR;
                        break;
                    }
                }
                break;
            }
            sleep = ticks + MIN_INTERVAL_BETWEEN_TASKS - (LONG)GetTickCount ();
            if (sleep > 0 && sleep <= MIN_INTERVAL_BETWEEN_TASKS) {
                Sleep (sleep);
            }
            g_DynUpdtStatus->DUStatus = DUS_DOWNLOADING;
            break;

        case DUS_DOWNLOADING:
            ticks = GetTickCount ();
            estSize = estTime = 0;
            if (!DynamicUpdateStart (&estTime, &estSize)) {
                g_DynUpdtStatus->DUStatus = DUS_ERROR;
                break;
            }
            //
            // check if there is enough disk space available for this operation
            //
            lstrcpyn (drive, g_DynUpdtStatus->WorkingDir, 3);
            if (GetDiskFreeSpaceNew (
                    drive,
                    &sectorsPerCluster,
                    &bytesPerSector,
                    &freeClusters,
                    &totalClusters
                    )) {
                clusterSize = bytesPerSector * sectorsPerCluster;
                availableBytes = (ULONGLONG)clusterSize * freeClusters.QuadPart;
                //
                // assume the average-worst case where each file occupies 1/2 cluster
                // then the space required is the double of estimated space
                //
                if (availableBytes < (ULONGLONG)estSize * 2) {
                    DynUpdtDebugLog (
                        Winnt32LogError,
                        TEXT("DoDynamicUpdate: not enough free space on drive %1 to perform download (available=%2!u! MB, needed=%3!u! MB)"),
                        0,
                        drive,
                        (DWORD)(availableBytes >> 20),
                        (DWORD)(estSize >> 20)
                        );
                    g_DynUpdtStatus->DUStatus = DUS_ERROR;
                    DynamicUpdateCancel ();
                    //
                    // wait for the UI thread to signal the event, no more than about a minute
                    //
                    rc = WaitForSingleObject (hEvent, 66000);
                    if (rc == WAIT_TIMEOUT) {
                        //
                        // why?
                        //
                        MYASSERT (FALSE);
                    } else if (rc != WAIT_OBJECT_0) {
                        DynUpdtDebugLog (
                            Winnt32LogError,
                            TEXT("WaitForSingleObject failed (%1!u!)"),
                            0,
                            rc
                            );
                    }
                    break;
                }
            }

            SendMessage (hUIWindow, WMX_SETUPUPDATE_DOWNLOADING, estTime, estSize);

            rc = WaitForSingleObject (hEvent, INFINITE);
            if (rc != WAIT_OBJECT_0) {
                DynUpdtDebugLog (
                    Winnt32LogError,
                    TEXT("WaitForSingleObject failed (%1!u!)"),
                    0,
                    rc
                    );
                g_DynUpdtStatus->DUStatus = DUS_ERROR;
                break;
            }
            sleep = ticks + MIN_INTERVAL_BETWEEN_TASKS - (LONG)GetTickCount ();
            if (sleep > 0 && sleep <= MIN_INTERVAL_BETWEEN_TASKS) {
                Sleep (sleep);
            }
            //
            // the UI thread has already set the next state,
            // based on the result of download
            //
            break;

        case DUS_PROCESSING:
            ticks = GetTickCount ();
            SendMessage (hUIWindow, WMX_SETUPUPDATE_PROCESSING, 0, 0);
            if (!g_DynUpdtStatus->UserSpecifiedUpdates) {
                lstrcpy (g_DynUpdtStatus->DynamicUpdatesSource, g_DynUpdtStatus->WorkingDir);
            }
            bStopSetup = FALSE;
            if (!DynamicUpdateProcessFiles (&bStopSetup)) {
                g_DynUpdtStatus->DUStatus = bStopSetup ? DUS_FATALERROR : DUS_ERROR;
                break;
            }
            sleep = ticks + MIN_INTERVAL_BETWEEN_TASKS - (LONG)GetTickCount ();
            if (sleep > 0 && sleep <= MIN_INTERVAL_BETWEEN_TASKS) {
                Sleep (sleep);
            }
            g_DynUpdtStatus->DUStatus = DUS_SUCCESSFUL;
            break;

        case DUS_SUCCESSFUL:
            if (CheckUpgradeOnly && !g_DynUpdtStatus->RestartWinnt32 && !g_DynUpdtStatus->UserSpecifiedUpdates) {
                if (pSaveLastDownloadInfo ()) {
                    g_DynUpdtStatus->PreserveWorkingDir = TRUE;
                }
            }
            //
            // fall through
            //
        case DUS_ERROR:
        case DUS_FATALERROR:
        case DUS_SKIP:
            //
            // always make sure to uninitialize DU
            // if the user had a modem connection active, this should close
            // the connection
            // DynamicUpdateUninitialize () will not reset any DU data
            // in case the processing was successful
            //
            DynamicUpdateUninitialize ();
            bContinue = FALSE;
            break;

        default:
            MYASSERT (FALSE);
        }
    }

    CloseHandle (hEvent);

exit:
    //
    // always notify the UI thread before exiting
    //
    PostMessage (hUIWindow, WMX_SETUPUPDATE_THREAD_DONE, 0, 0);
    return rc;
}