/*++

Copyright (c) 1995-2000 Microsoft Corporation

Module Name:

    fileq2.c

Abstract:

    Setup file queue routines for enqueing copy operations.

Author:

    Ted Miller (tedm) 15-Feb-1995

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop


#define STR_DRIVERCACHEINF  TEXT("drvindex.inf")


//
// Structure used with _SetupQueueCopy
//
typedef struct _SP_FILE_COPY_PARAMS_AEX {
    DWORD    cbSize;
    HSPFILEQ QueueHandle;
    PCSTR    SourceRootPath;     OPTIONAL
    PCSTR    SourcePath;         OPTIONAL
    PCSTR    SourceFilename;
    PCSTR    SourceDescription;  OPTIONAL
    PCSTR    SourceTagfile;      OPTIONAL
    PCSTR    TargetDirectory;
    PCSTR    TargetFilename;     OPTIONAL
    DWORD    CopyStyle;
    HINF     LayoutInf;          OPTIONAL
    PCSTR    SecurityDescriptor; OPTIONAL
    DWORD    SourceFlags;        OPTIONAL
    BOOL     SourceFlagsSet;     OPTIONAL // we need this flag since SourceFlags may be zero
    PCSTR    CacheName;
} SP_FILE_COPY_PARAMS_AEX, *PSP_FILE_COPY_PARAMS_AEX;

typedef struct _SP_FILE_COPY_PARAMS_WEX {
    DWORD    cbSize;
    HSPFILEQ QueueHandle;
    PCWSTR   SourceRootPath;     OPTIONAL
    PCWSTR   SourcePath;         OPTIONAL
    PCWSTR   SourceFilename;
    PCWSTR   SourceDescription;  OPTIONAL
    PCWSTR   SourceTagfile;      OPTIONAL
    PCWSTR   TargetDirectory;
    PCWSTR   TargetFilename;     OPTIONAL
    DWORD    CopyStyle;
    HINF     LayoutInf;          OPTIONAL
    PCWSTR   SecurityDescriptor; OPTIONAL
    DWORD    SourceFlags;        OPTIONAL
    BOOL     SourceFlagsSet;     OPTIONAL // we need this flag since SourceFlags may be zero
    PCWSTR   CacheName;
} SP_FILE_COPY_PARAMS_WEX, *PSP_FILE_COPY_PARAMS_WEX;

#ifdef UNICODE
typedef SP_FILE_COPY_PARAMS_WEX SP_FILE_COPY_PARAMSEX;
typedef PSP_FILE_COPY_PARAMS_WEX PSP_FILE_COPY_PARAMSEX;
#else
typedef SP_FILE_COPY_PARAMS_AEX SP_FILE_COPY_PARAMSEX;
typedef PSP_FILE_COPY_PARAMS_AEX PSP_FILE_COPY_PARAMSEX;
#endif




BOOL
_SetupQueueCopy(
    IN PSP_FILE_COPY_PARAMSEX CopyParams,
    IN PINFCONTEXT          LayoutLineContext, OPTIONAL
    IN HINF                 AdditionalInfs     OPTIONAL
    );

PSOURCE_MEDIA_INFO
pSetupQueueSourceMedia(
    IN OUT PSP_FILE_QUEUE      Queue,
    IN OUT PSP_FILE_QUEUE_NODE QueueNode,
    IN     LONG                SourceRootStringId,
    IN     PCTSTR              SourceDescription,   OPTIONAL
    IN     PCTSTR              SourceTagfile,       OPTIONAL
    IN     PCTSTR              SourceCabfile,       OPTIONAL
    IN     DWORD               MediaFlags
    );

BOOL
pSetupQueueSingleCopy(
    IN HSPFILEQ QueueHandle,
    IN HINF     InfHandle,
    IN HINF     ListInfHandle,  OPTIONAL
    IN PCTSTR   SectionName,    OPTIONAL
    IN PCTSTR   SourceRootPath,
    IN PCTSTR   SourceFilename,
    IN PCTSTR   TargetFilename,
    IN DWORD    CopyStyle,
    IN PCTSTR   SecurityDescriptor,
    IN PCTSTR   CacheName
    );

BOOL
pSetupGetSourceAllInfo(
    IN  HINF                     InfHandle,
    IN  PINFCONTEXT              LayoutLineContext, OPTIONAL
    IN  UINT                     SourceId,
    IN  PSP_ALTPLATFORM_INFO_V2  AltPlatformInfo,   OPTIONAL
    OUT PCTSTR                  *Description,
    OUT PCTSTR                  *Tagfile,
    OUT PCTSTR                  *RelativePath,
    OUT PUINT                    SourceFlags
    );

BOOL
pIsDriverCachePresent(
    IN PCTSTR DriverName,
    IN PCTSTR SubDirectory,
    OUT PTSTR DriverBuffer
    );

BOOL
pIsFileInDriverCache(
    IN  HINF   CabInf,
    IN  PCTSTR TargetFilename,
    IN  PCTSTR SubDirectory,
    OUT PCTSTR *CacheName
    );



//
// HACK ALERT!!! HACK HACK HACK!!!!
//
// There might be an override platform specified. If this is so,
// we will look for \i386, \mips, etc as the final component of the
// specified path when queuing files, and replace it with the
// override path. This is a TOTAL HACK.
//
PCTSTR PlatformPathOverride;

VOID
pSetupInitPlatformPathOverrideSupport(
    IN BOOL Init
    )
{
    if(Init) {
        PlatformPathOverride = NULL;
    } else {
        if( PlatformPathOverride ) {
            MyFree(PlatformPathOverride);
            PlatformPathOverride = NULL;
        }
    }
}

#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupSetPlatformPathOverrideA(
    IN PCSTR Override   OPTIONAL
    )
{
    BOOL b;
    DWORD rc;
    PCWSTR p;

    if(Override) {
        rc = pSetupCaptureAndConvertAnsiArg(Override,&p);
    } else {
        p = NULL;
        rc = NO_ERROR;
    }

    if(rc == NO_ERROR) {
        b = SetupSetPlatformPathOverrideW(p);
        rc = GetLastError();
    } else {
        b = FALSE;
    }

    if(p) {
        MyFree(p);
    }

    SetLastError(rc);
    return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupSetPlatformPathOverrideW(
    IN PCWSTR Override  OPTIONAL
    )
{
    UNREFERENCED_PARAMETER(Override);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return(FALSE);
}
#endif

BOOL
SetupSetPlatformPathOverride(
    IN PCTSTR Override  OPTIONAL
    )
{
    BOOL b = FALSE;
    DWORD rc = ERROR_NOT_ENOUGH_MEMORY;
    BOOL locked = FALSE;

    try {
        EnterCriticalSection(&PlatformPathOverrideCritSect);
        locked = TRUE;
        rc = ERROR_INVALID_DATA;

        if(Override) {
            if(PlatformPathOverride) {
                MyFree(PlatformPathOverride);
                PlatformPathOverride = NULL;
            }

            try {
                b = ((PlatformPathOverride = DuplicateString(Override)) != NULL);
                if(!b) {
                    rc = ERROR_NOT_ENOUGH_MEMORY;
                }
            } except(EXCEPTION_EXECUTE_HANDLER) {
                b = FALSE;
                rc = ERROR_INVALID_PARAMETER;
            }
        } else {
            if(PlatformPathOverride) {
                MyFree(PlatformPathOverride);
                PlatformPathOverride = NULL;
            }
            b = TRUE;
        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
    }

    if(locked) {
        LeaveCriticalSection(&PlatformPathOverrideCritSect);
    } else {
        b = FALSE;
        rc = ERROR_NOT_ENOUGH_MEMORY;
    }

    if(!b) {
        SetLastError(rc);
    }
    return(b);
}


#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupQueueCopyA(
    IN HSPFILEQ QueueHandle,
    IN PCSTR    SourceRootPath,     OPTIONAL
    IN PCSTR    SourcePath,         OPTIONAL
    IN PCSTR    SourceFilename,
    IN PCSTR    SourceDescription,  OPTIONAL
    IN PCSTR    SourceTagfile,      OPTIONAL
    IN PCSTR    TargetDirectory,
    IN PCSTR    TargetFilename,     OPTIONAL
    IN DWORD    CopyStyle
    )
{
    PCWSTR sourceRootPath;
    PCWSTR sourcePath;
    PCWSTR sourceFilename;
    PCWSTR sourceDescription;
    PCWSTR sourceTagfile;
    PCWSTR targetDirectory;
    PCWSTR targetFilename;
    BOOL b;
    DWORD rc;
    SP_FILE_COPY_PARAMS_WEX CopyParams = {0};

    sourceRootPath = NULL;
    sourcePath = NULL;
    sourceFilename = NULL;
    sourceDescription = NULL;
    sourceTagfile = NULL;
    targetDirectory = NULL;
    targetFilename = NULL;
    rc = NO_ERROR;
    b = FALSE;

    if(SourceRootPath) {
        rc = pSetupCaptureAndConvertAnsiArg(SourceRootPath,&sourceRootPath);
    }
    if((rc == NO_ERROR) && SourcePath) {
        rc = pSetupCaptureAndConvertAnsiArg(SourcePath,&sourcePath);
    }
    if((rc == NO_ERROR) && SourceFilename) {
        rc = pSetupCaptureAndConvertAnsiArg(SourceFilename,&sourceFilename);
    }
    if((rc == NO_ERROR) && SourceDescription) {
        rc = pSetupCaptureAndConvertAnsiArg(SourceDescription,&sourceDescription);
    }
    if((rc == NO_ERROR) && SourceTagfile) {
        rc = pSetupCaptureAndConvertAnsiArg(SourceTagfile,&sourceTagfile);
    }
    if((rc == NO_ERROR) && TargetDirectory) {
        rc = pSetupCaptureAndConvertAnsiArg(TargetDirectory,&targetDirectory);
    }
    if((rc == NO_ERROR) && TargetFilename) {
        rc = pSetupCaptureAndConvertAnsiArg(TargetFilename,&targetFilename);
    }

    if(rc == NO_ERROR) {

        CopyParams.cbSize = sizeof(SP_FILE_COPY_PARAMS_WEX);
        CopyParams.QueueHandle = QueueHandle;
        CopyParams.SourceRootPath = sourceRootPath;
        CopyParams.SourcePath = sourcePath;
        CopyParams.SourceFilename = sourceFilename;
        CopyParams.SourceDescription = sourceDescription;
        CopyParams.SourceTagfile = sourceTagfile;
        CopyParams.TargetDirectory = targetDirectory;
        CopyParams.TargetFilename = targetFilename;
        CopyParams.CopyStyle = CopyStyle;
        CopyParams.LayoutInf = NULL;
        CopyParams.SecurityDescriptor= NULL;

        b = _SetupQueueCopy(&CopyParams, NULL, NULL);
        rc = GetLastError();
    }

    if(sourceRootPath) {
        MyFree(sourceRootPath);
    }
    if(sourcePath) {
        MyFree(sourcePath);
    }
    if(sourceFilename) {
        MyFree(sourceFilename);
    }
    if(sourceDescription) {
        MyFree(sourceDescription);
    }
    if(sourceTagfile) {
        MyFree(sourceTagfile);
    }
    if(targetDirectory) {
        MyFree(targetDirectory);
    }
    if(targetFilename) {
        MyFree(targetFilename);
    }

    SetLastError(rc);
    return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupQueueCopyW(
    IN HSPFILEQ QueueHandle,
    IN PCWSTR   SourceRootPath,     OPTIONAL
    IN PCWSTR   SourcePath,         OPTIONAL
    IN PCWSTR   SourceFilename,
    IN PCWSTR   SourceDescription,  OPTIONAL
    IN PCWSTR   SourceTagfile,      OPTIONAL
    IN PCWSTR   TargetDirectory,
    IN PCWSTR   TargetFilename,     OPTIONAL
    IN DWORD    CopyStyle
    )
{
    UNREFERENCED_PARAMETER(QueueHandle);
    UNREFERENCED_PARAMETER(SourceRootPath);
    UNREFERENCED_PARAMETER(SourcePath);
    UNREFERENCED_PARAMETER(SourceFilename);
    UNREFERENCED_PARAMETER(SourceDescription);
    UNREFERENCED_PARAMETER(SourceTagfile);
    UNREFERENCED_PARAMETER(TargetDirectory);
    UNREFERENCED_PARAMETER(TargetFilename);
    UNREFERENCED_PARAMETER(CopyStyle);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return(FALSE);
}
#endif

BOOL
SetupQueueCopy(
    IN HSPFILEQ QueueHandle,
    IN PCTSTR   SourceRootPath,     OPTIONAL
    IN PCTSTR   SourcePath,         OPTIONAL
    IN PCTSTR   SourceFilename,
    IN PCTSTR   SourceDescription,  OPTIONAL
    IN PCTSTR   SourceTagfile,      OPTIONAL
    IN PCTSTR   TargetDirectory,
    IN PCTSTR   TargetFilename,     OPTIONAL
    IN DWORD    CopyStyle
    )

/*++

Routine Description:

    Place a copy operation on a setup file queue.

Arguments:

    QueueHandle - supplies a handle to a setup file queue, as returned
        by SetupOpenFileQueue.

    SourceRootPath - Supplies the root of the source for this copy,
        such as A:\ or \\FOO\BAR\BAZ.  If this parameter isn't supplied, then
        this queue node will be added to a media descriptor's queue that
        matches on SourceDescription and SourceTagfile.  (This merge will take
        place regardless of whether or not the media descriptor entry was
        already in the queue prior to calling SetupQueueCopy.)

        If there is no matching media descriptor that contains SourceRootPath
        information, the path will be set to the directory where the system was
        installed from.

    SourcePath - if specified, supplies the path relative to SourceRootPath
        where the file can be found.

    SourceFilename - supplies the filename part of the file to be copied.

    SourceDescription - if specified, supplies a description of the source
        media, to be used during disk prompts.

    SourceTagfile - if specified, supplies a tag file whose presence at
        SourceRootPath indicates the presence of the source media.
        If not specified, the file itself will be used as the tag file
        if required (tagfiles are used only for removable media).

    TargetDirectory - supplies the directory where the file is to be copied.

    TargetFilename - if specified, supplies the name of the target file.
        If not specified, the target file will have the same name as the source.

    CopyStyle - supplies flags that control the behavior of the copy operation
        for this file.

Return Value:

    Boolean value indicating outcome. If FALSE, GetLastError() returns
    extended error information.

--*/

{
    SP_FILE_COPY_PARAMSEX CopyParams = {0};

    //
    // Work is done by common worker routine
    //
    CopyParams.cbSize = sizeof(SP_FILE_COPY_PARAMSEX);
    CopyParams.QueueHandle = QueueHandle;
    CopyParams.SourceRootPath = SourceRootPath;
    CopyParams.SourcePath = SourcePath;
    CopyParams.SourceFilename = SourceFilename;
    CopyParams.SourceDescription = SourceDescription;
    CopyParams.SourceTagfile = SourceTagfile;
    CopyParams.TargetDirectory = TargetDirectory;
    CopyParams.TargetFilename = TargetFilename;
    CopyParams.CopyStyle = CopyStyle;
    CopyParams.LayoutInf = NULL;
    CopyParams.SecurityDescriptor= NULL;
    CopyParams.CacheName = NULL;

    return(_SetupQueueCopy(&CopyParams, NULL, NULL));
}


BOOL
_SetupQueueCopy(
    IN PSP_FILE_COPY_PARAMSEX CopyParams,
    IN PINFCONTEXT            LayoutLineContext, OPTIONAL
    IN HINF                   AdditionalInfs     OPTIONAL
    )

/*++

Routine Description:

    Worker routine for SetupQueueCopy and friends.

Arguments:

    CopyParams - supplies a structure with information about the file
        to be queued. Fields are used as follows.

        cbSize - must be sizeof(SP_FILE_COPY_PARAMS). The caller should
            have verified this before calling this routine.

        QueueHandle - supplies a handle to a setup file queue, as returned
            by SetupOpenFileQueue.

        SourceRootPath - Supplies the root of the source for this copy,
            such as A:\ or \\FOO\BAR\BAZ.   If this field is NULL, then this
            queue node will be added to a media descriptor's queue that matches
            on SourceDescription and SourceTagfile.  (This merge will take
            place regardless of whether or not the media descriptor entry was
            already in the queue prior to calling SetupQueueCopy.)

            If there is no matching media descriptor that contains
            SourceRootPath information, the path will be set to the directory
            where the system was installed from.

        SourcePath - if specified, supplies the path relative to SourceRootPath
            where the file can be found.

        SourceFilename - supplies the filename part of the file to be copied.

        SourceDescription - if specified, supplies a description of the source
            media, to be used during disk prompts.

        SourceTagfile - if specified, supplies a tag file whose presence at
            SourceRootPath indicates the presence of the source media.
            If not specified, the file itself will be used as the tag file
            if required (tagfiles are used only for removable media).

        TargetDirectory - supplies the directory where the file is to be copied.

        TargetFilename - if specified, supplies the name of the target file.  If
            not specified, the target file will have the same name as the source.

        CopyStyle - supplies flags that control the behavior of the copy
            operation for this file.

        LayoutInf - supplies the handle to the inf which contains the source
            layout info for this file, if any.

    LayoutLineContext - if specified, this argument provides the INF context
        for the [SourceDisksFiles] entry pertaining to the file to be copied.
        If not specified, the relevant [SourceDisksFiles] entry will be searched
        for in the LayoutInf handle specified in the CopyParams structure.  This
        context must be contained within either the CopyParams->LayoutInf or
        AdditionalInfs loaded INF handles (because those are the two INFs we're
        gonna lock).  The argument is used to prevent us from having to search
        for the file to be copied in a [SourceDisksFiles] section.  The caller
        has already done that, and is either handing us the context to that INF
        entry, or has passed -1 to indicate that there is no [SourceDisksFiles]
        entry.

    AdditionalInfs - if specified, supplies an additional HINF (potentially
        containing multiple append-loaded INFs) that need to be added to our
        SPQ_CATALOG_INFO list for later validation.  Do not supply this parameter
        if it is identical to the value of the LayoutInf field in the CopyParams
        structure.

Return Value:

    Boolean value indicating outcome. If FALSE, GetLastError() returns
    extended error information.

--*/

{
    PSP_FILE_QUEUE Queue;
    PSP_FILE_QUEUE_NODE QueueNode, TempNode, PrevQueueNode;
    PSOURCE_MEDIA_INFO Source;
    TCHAR TempBuffer[MAX_PATH];
    TCHAR TempSubDir[MAX_PATH];
    TCHAR SourceCabfileBuffer[MAX_PATH];
    TCHAR SourceTagfile2Buffer[MAX_PATH];
    TCHAR DriverCache[MAX_PATH] = {0};
    PCTSTR LastPathPart;
    PCTSTR p;
    int Size;
    DWORD d;
    HINF LayoutInfHandle;
    INFCONTEXT LineContext;
    BOOL b;
    PSPQ_CATALOG_INFO CatalogNode, PrevCatalogNode, LastOldCatalogNode;
    LONG l1,l2, l3, l4;
    PLOADED_INF pLoadedInfs[2];
    DWORD LoadedInfCount, i;
    PLOADED_INF pCurLoadedInf;
    DWORD MediaFlags;
    PCTSTR SourcePath, SourceRootPath;
    PCTSTR SourceTagfile = NULL;
    PCTSTR SourceCabfile = NULL;
#if defined(_X86_)
    BOOL ForcePlatform = FALSE;
#endif
    UINT SourceFlags = 0;
    PINFCONTEXT pContext = LayoutLineContext;
    INFCONTEXT tmpContext,tmpContext2;
    UINT SourceId = 0;
    BOOL locked = FALSE;

    d = NO_ERROR;
    LoadedInfCount = 0;
    MediaFlags = 0;

    try {
        MYASSERT(CopyParams->cbSize == sizeof(SP_FILE_COPY_PARAMSEX));
        Queue = (PSP_FILE_QUEUE)CopyParams->QueueHandle;
        if (Queue->Signature != SP_FILE_QUEUE_SIG) {
            d = ERROR_INVALID_PARAMETER;
        }

        LayoutInfHandle = CopyParams->LayoutInf;

        //
        // Maintain local pointers to the SourceRootPath and SourcePath strings,
        // since we may be modifying them, and we don't want to muck with the
        // caller-supplied buffer.
        //
        SourcePath = CopyParams->SourcePath;
        if(CopyParams->SourceRootPath) {
            SourceRootPath = CopyParams->SourceRootPath;
        } else {
            SourceRootPath = SystemSourcePath;
            MediaFlags |= SMI_FLAG_NO_SOURCE_ROOT_PATH;
        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
        d = ERROR_INVALID_PARAMETER;
    }
    if(d != NO_ERROR) {
        goto clean0;
    }

    //
    // Make sure that we weren't passed the same HINF in both CopyParams->LayoutInf
    // and AdditionalInfs (just adds redundant work to process the same
    // LOADED_INF list twice).
    //
    MYASSERT(!LayoutInfHandle || (LayoutInfHandle != AdditionalInfs));

    //
    // Lock inf(s). We do a whole bunch of operations on the inf later,
    // and we don't want anything changing out from under us.
    //
    if(LayoutInfHandle) {
        if(LockInf((PLOADED_INF)LayoutInfHandle)) {
            pLoadedInfs[LoadedInfCount++] = (PLOADED_INF)LayoutInfHandle;
        } else {
            d = ERROR_INVALID_HANDLE;
            goto clean0;
        }
    }

    if(AdditionalInfs) {
        if(LockInf((PLOADED_INF)AdditionalInfs)) {
            pLoadedInfs[LoadedInfCount++] = (PLOADED_INF)AdditionalInfs;
        } else {
            d = ERROR_INVALID_HANDLE;
            goto clean0;
        }
    }

    if(!(Queue->Flags & FQF_DEVICE_INSTALL)) {
        //
        // Look through all the INFs to see if any of them are device INFs.
        //
        for(i = 0; i < LoadedInfCount; i++) {

            if(IsInfForDeviceInstall(Queue->LogContext,
                                     NULL,
                                     pLoadedInfs[i],
                                     NULL,
                                     NULL,
                                     NULL,
                                     NULL)) {
                //
                // There be device INFs here!  Mark the queue accordingly.
                //
                d = MarkQueueForDeviceInstall(CopyParams->QueueHandle,
                                              (HINF)(pLoadedInfs[i]),
                                              NULL
                                             );
                if(d == NO_ERROR) {
                    break;
                } else {
                    goto clean0;
                }
            }
        }
    }

    //
    // check if we already have a line context for the file we're adding
    // and if we don't, then try to fetch it
    //
    if (!LayoutLineContext || LayoutLineContext == (PINFCONTEXT) -1) {
        if ((LayoutInfHandle == (PINFCONTEXT) -1) || (LayoutInfHandle == NULL)) {
            pContext = NULL;
        } else {
            //
            // find the sourcerootpaths section
            //
            b = _SetupGetSourceFileLocation(
                    LayoutInfHandle,
                    NULL,
                    CopyParams->SourceFilename,
                    (Queue->Flags & FQF_USE_ALT_PLATFORM)
                       ? &(Queue->AltPlatformInfo)
                       : NULL,
                    NULL,
                    NULL,
                    0,
                    NULL,
                    &tmpContext // location in SourceDisksFiles
                    );

            pContext = b ? &tmpContext : NULL;
        }
    }
    if(pContext) {
        //
        // now obtain the source id (file,*disk*,...)
        //
        SetupGetIntField(pContext,1,&SourceId);
    }

    //
    // if we have a NULL source path, then we should check 2 things:
    // 1) source flag information, which indicates if we have a service
    //    pack or CDM source location.
    // 2) if the relative source path is null, we'll go looking for
    //    a relative path in the inf file, just in case the caller didn't
    //    supply it
    //
    // note that we use the SourceFlagsSet item in the COPY_FILE structure
    // to optimize this path -- the first item contains the source flags,
    // and the second item indicates that we shouldn't bother looking for
    // any information, we've already done a search
    //
    if (CopyParams->SourceFlagsSet) {
        SourceFlags = CopyParams->SourceFlags;
    } else if (pContext && LayoutInfHandle) {
        TCHAR data[32];

        if(pSetupGetSourceInfo(LayoutInfHandle,
                               pContext,
                               SourceId,
                               (Queue->Flags & FQF_USE_ALT_PLATFORM)
                                  ? &(Queue->AltPlatformInfo)
                                  : NULL,
                               SRCINFO_FLAGS,
                               data,
                               SIZECHARS(data),
                               NULL)) {

            pAToI(data,&SourceFlags);
        }
    }

    if (MediaFlags & SMI_FLAG_NO_SOURCE_ROOT_PATH) {
        if(pContext
           && LayoutInfHandle
           && !CopyParams->SourceFlagsSet
           && !SourcePath
           && pSetupGetSourceInfo(LayoutInfHandle,
                                  pContext,
                                  SourceId,
                                  (Queue->Flags & FQF_USE_ALT_PLATFORM)
                                     ? &(Queue->AltPlatformInfo)
                                     : NULL,
                                  SRCINFO_PATH,
                                  TempSubDir,
                                  SIZECHARS(TempSubDir),
                                  NULL)) {

           SourcePath = TempSubDir;
        }
        //
        // override the system source path with the servicepack source path
        // if the flags are set
        //
        if (SourceFlags & SRC_FLAGS_SVCPACK_SOURCE) {
            MediaFlags |= SMI_FLAG_USE_SVCPACK_SOURCE_ROOT_PATH;
            SourceRootPath = ServicePackSourcePath;
        }

    }
    //
    // now determine tag file vs cab file
    //
    SourceTagfile = CopyParams->SourceTagfile;
    if (LayoutInfHandle && pContext && (SourceFlags & SRC_FLAGS_CABFILE)) {
        //
        // the given tagfile is really a cabfile, we may have optionally a real tagfile
        //
        SourceCabfile = CopyParams->SourceTagfile;
        if(SourceCabfile == NULL || SourceCabfile[0]==TEXT('\0')) {
            //
            // cab name hasn't been determined yet
            //
            if(pSetupGetSourceInfo(LayoutInfHandle,
                                   pContext,
                                   SourceId,
                                   (Queue->Flags & FQF_USE_ALT_PLATFORM)
                                      ? &(Queue->AltPlatformInfo)
                                      : NULL,
                                   SRCINFO_TAGFILE,
                                   SourceCabfileBuffer,
                                   SIZECHARS(SourceCabfileBuffer),
                                   NULL
                                   )) {
                SourceCabfile = SourceCabfileBuffer;
            }
        }

        if(SourceCabfile == NULL || SourceCabfile[0]==TEXT('\0')) {
            //
            // cabfilename is erroneous
            //
            SourceCabfile = SourceTagfile = NULL;

        } else if(pSetupGetSourceInfo(LayoutInfHandle,
                               pContext,
                               SourceId,
                               (Queue->Flags & FQF_USE_ALT_PLATFORM)
                                  ? &(Queue->AltPlatformInfo)
                                  : NULL,
                               SRCINFO_TAGFILE2,
                               SourceTagfile2Buffer,
                               SIZECHARS(SourceTagfile2Buffer),
                               NULL
                               )) {
            SourceTagfile = SourceTagfile2Buffer;
        }
    }

    //
    // Allocate a queue structure.
    //
    QueueNode = MyMalloc(sizeof(SP_FILE_QUEUE_NODE));
    if(!QueueNode) {
        d = ERROR_NOT_ENOUGH_MEMORY;
        goto clean0;
    }

    //
    // Operation is copy.
    //
    QueueNode->Operation = FILEOP_COPY;
    QueueNode->InternalFlags = 0;

    //
    // HACK ALERT!!! HACK HACK HACK!!!!
    //
    // There might be an override platform specified. If this is so,
    // we will look for \i386, \mips, etc as the final component of the
    // specified path, and replace it with the override path.
    // This is a TOTAL HACK.
    //
    try {
        EnterCriticalSection(&PlatformPathOverrideCritSect);
        locked = TRUE;
        if(PlatformPathOverride) {
            p = SourcePath ? SourcePath : SourceRootPath;
            if(LastPathPart = _tcsrchr(p,L'\\')) {
                LastPathPart++;
            } else {
                LastPathPart = p;
            }
#if defined(_AXP64_)
            if(!lstrcmpi(LastPathPart,TEXT("axp64"))) {
#elif defined(_ALPHA_)
            if(!lstrcmpi(LastPathPart,TEXT("alpha"))) {
#elif defined(_AMD64_)
            if(!lstrcmpi(LastPathPart,TEXT("amd64"))) {
#elif defined(_X86_)
            //
            // NEC98
            //
            // During GUI setup, source path on local disk must be "nec98",
            // so we don't override "i386".
            //
            if (IsNEC98()) {
                HKEY    hKey;
                DWORD   DataType, DataSize;
                PTSTR   ForceOverride;
                if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\Setup"), 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
                    if (QueryRegistryValue(hKey, TEXT("ForcePlatform"), &ForceOverride, &DataType, &DataSize)
                        == NO_ERROR) {
                        ForcePlatform = TRUE;
                        MyFree(ForceOverride);
                    }
                    RegCloseKey(hKey);
                }
                // If use driver cache, There is not i386 driver cache on NEC98.
                if ((CopyParams->CopyStyle & PSP_COPY_USE_DRIVERCACHE) && !lstrcmpi(PlatformPathOverride,TEXT("i386"))) {
                    ForcePlatform = TRUE;
                }
            }
            if((!IsNEC98() && (!lstrcmpi(LastPathPart,TEXT("x86")) || !lstrcmpi(LastPathPart,TEXT("i386"))))
            || (IsNEC98()  && (!lstrcmpi(LastPathPart,TEXT("nec98")) && !ForcePlatform))) {
#elif defined(_IA64_)
            if(!lstrcmpi(LastPathPart,TEXT("ia64"))) {
#endif
                Size = (int)(LastPathPart - p);
                Size = min(Size,MAX_PATH);
                Size *= sizeof(TCHAR);

                CopyMemory(TempBuffer,p,Size);
                TempBuffer[Size/sizeof(TCHAR)] = 0;

                //
                // If the path was something like "mips" then TempBuffer
                // will be empty and we don't want to introduce any extra
                // backslashes.
                //
                if(*TempBuffer) {
                    pSetupConcatenatePaths(TempBuffer,PlatformPathOverride,MAX_PATH,NULL);
                } else {
                    lstrcpyn(TempBuffer,PlatformPathOverride,MAX_PATH);
                }

                if(SourcePath) {
                    SourcePath = TempBuffer;
                } else {
                    SourceRootPath = TempBuffer;
                }
            }
        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
        d = ERROR_INVALID_PARAMETER;
    }
    if(locked) {
        LeaveCriticalSection(&PlatformPathOverrideCritSect);
    } else {
        //
        // if lock not grabbed, this is the cause
        //
        d = ERROR_NOT_ENOUGH_MEMORY;
    }
    if(d != NO_ERROR) {
        goto clean1;
    }

    //
    // check here if the cab-file is present on the disk.
    // if it isn't, then we fall back on the current
    // sourcerootpath
    //
    if (CopyParams->CopyStyle & PSP_COPY_USE_DRIVERCACHE) {
        if (pIsDriverCachePresent(CopyParams->CacheName,
                                  SourcePath,
                                  DriverCache)) {
            SourceRootPath = DriverCache;
            MediaFlags |= SMI_FLAG_USE_LOCAL_SOURCE_CAB;
        }

        SourceTagfile = CopyParams->CacheName;
        SourceCabfile = NULL;
    }



    //
    // NOTE: When adding the following strings to the string table, we cast away
    // their CONST-ness to avoid a compiler warning.  Since we are adding them
    // case-sensitively, we are guaranteed they will not be modified.
    //
    try {
        //
        // Set up the source root path.
        //
        QueueNode->SourceRootPath = pSetupStringTableAddString(
                                        Queue->StringTable,
                                        (PTSTR)SourceRootPath,
                                        STRTAB_CASE_SENSITIVE
                                        );

        if(QueueNode->SourceRootPath == -1) {
            d = ERROR_NOT_ENOUGH_MEMORY;
        }

        //
        // Set up the source path.
        //
        if(d == NO_ERROR) {
            if(SourcePath) {
                QueueNode->SourcePath = pSetupStringTableAddString(
                                            Queue->StringTable,
                                            (PTSTR)SourcePath,
                                            STRTAB_CASE_SENSITIVE
                                            );

                if(QueueNode->SourcePath == -1) {
                    d = ERROR_NOT_ENOUGH_MEMORY;
                }
            } else {
                QueueNode->SourcePath = -1;
            }
        }

        //
        // Set up the source filename.
        //
        if(d == NO_ERROR) {
            QueueNode->SourceFilename = pSetupStringTableAddString(
                                            Queue->StringTable,
                                            (PTSTR)CopyParams->SourceFilename,
                                            STRTAB_CASE_SENSITIVE
                                            );

            if(QueueNode->SourceFilename == -1) {
                d = ERROR_NOT_ENOUGH_MEMORY;
            }
        }

        //
        // Set up the target directory.
        //
        if(d == NO_ERROR) {
            QueueNode->TargetDirectory = pSetupStringTableAddString(
                                            Queue->StringTable,
                                            (PTSTR)CopyParams->TargetDirectory,
                                            STRTAB_CASE_SENSITIVE
                                            );

            if(QueueNode->TargetDirectory == -1) {
                d = ERROR_NOT_ENOUGH_MEMORY;
            }
        }

        //
        // Set up the target filename.
        //
        if(d == NO_ERROR) {
            QueueNode->TargetFilename = pSetupStringTableAddString(
                                            Queue->StringTable,
                                            (PTSTR)(CopyParams->TargetFilename ? CopyParams->TargetFilename
                                                                               : CopyParams->SourceFilename),
                                            STRTAB_CASE_SENSITIVE
                                            );

            if(QueueNode->TargetFilename == -1) {
                d = ERROR_NOT_ENOUGH_MEMORY;
            }
        }

        //
        // Set up the Security Descriptor
        //
        if(d == NO_ERROR) {
            if( CopyParams->SecurityDescriptor){

                QueueNode->SecurityDesc = pSetupStringTableAddString(
                                              Queue->StringTable,
                                              (PTSTR)(CopyParams->SecurityDescriptor),
                                                STRTAB_CASE_SENSITIVE
                                                );

                if(QueueNode->SecurityDesc == -1) {
                    d = ERROR_NOT_ENOUGH_MEMORY;
                }
            } else {
                QueueNode->SecurityDesc = -1;
            }
        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
        d = ERROR_INVALID_PARAMETER;
    }

    if(d != NO_ERROR) {
        goto clean1;
    }

    //
    // Initialize a pointer to the end of the queue's current catalog info list.
    // We do this so that later, we can easily back-out our changes by truncating
    // the list after this node, and freeing all subsequent elements.
    //
    LastOldCatalogNode = Queue->CatalogList;
    if(LastOldCatalogNode) {
        while(LastOldCatalogNode->Next) {
            LastOldCatalogNode = LastOldCatalogNode->Next;
        }
    }

    //
    // Now process all members of our pLoadedInfs lists, adding each one to the
    // SPQ_CATALOG_INFO list (avoiding duplicates entries, of course).
    //
    for(i = 0; i < LoadedInfCount; i++) {

        for(pCurLoadedInf = pLoadedInfs[i]; pCurLoadedInf; pCurLoadedInf = pCurLoadedInf->Next) {
            //
            // First, get the (native) CatalogFile= entry from the version block
            // of this INF member.
            //
            if(pSetupGetCatalogFileValue(&(pCurLoadedInf->VersionBlock),
                                         TempBuffer,
                                         SIZECHARS(TempBuffer),
                                         NULL)) {

                l1 = pSetupStringTableAddString(Queue->StringTable,
                                          TempBuffer,
                                          STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
                                         );
                if(l1 == -1) {
                    d = ERROR_NOT_ENOUGH_MEMORY;
                    goto clean2;
                }
            } else {
                //
                // This INF doesn't have a CatalogFile= entry.
                //
                l1 = -1;
            }

            //
            // If this file queue is currently setup for a platform override,
            // then retrieve that CatalogFile= entry as well.
            //
            if(Queue->Flags & FQF_USE_ALT_PLATFORM) {

                if(pSetupGetCatalogFileValue(&(pCurLoadedInf->VersionBlock),
                                             TempBuffer,
                                             SIZECHARS(TempBuffer),
                                             &(Queue->AltPlatformInfo))) {

                    l3 = pSetupStringTableAddString(Queue->StringTable,
                                              TempBuffer,
                                              STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
                                             );
                    if(l3 == -1) {
                        d = ERROR_NOT_ENOUGH_MEMORY;
                        goto clean2;
                    }
                } else {
                    //
                    // This INF doesn't have a CatalogFile= entry.
                    //
                    l3 = -1;
                }
            } else {
                //
                // We're not in a platform override scenario.
                //
                l3 = -1;
            }

            //
            // Now, get the INF's full path.
            //
            lstrcpyn(TempBuffer, pCurLoadedInf->VersionBlock.Filename, SIZECHARS(TempBuffer));
            l2 = pSetupStringTableAddString(Queue->StringTable,
                                      TempBuffer,
                                      STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
                                     );
            if(l2 == -1) {
                d = ERROR_NOT_ENOUGH_MEMORY;
                goto clean2;
            }

            //
            // Finally, retrieve the INF's original name, if different than the
            // current name.
            //
            if(pCurLoadedInf->OriginalInfName) {
                lstrcpyn(TempBuffer, pCurLoadedInf->OriginalInfName, SIZECHARS(TempBuffer));
                l4 = pSetupStringTableAddString(Queue->StringTable,
                                          TempBuffer,
                                          STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
                                         );
                if(l4 == -1) {
                    d = ERROR_NOT_ENOUGH_MEMORY;
                    goto clean2;
                }

            } else {
                //
                // The INF's original name is the same as its current name.
                //
                l4 = -1;
            }

            b = TRUE;
            for(PrevCatalogNode=NULL, CatalogNode=Queue->CatalogList;
                CatalogNode;
                CatalogNode=CatalogNode->Next) {

                if(CatalogNode->InfFullPath == l2) {
                    //
                    // Already in there. No need to create a new node.
                    // Break out here, with CatalogNode pointing at the
                    // proper node for this catalog file.
                    //
                    // In this case, PrevCatalogNode should not be used later,
                    // but it shouldn't need to be used, since we won't be
                    // adding anything new onto the list of catalog nodes.
                    //
                    MYASSERT(CatalogNode->CatalogFileFromInf == l1);
                    MYASSERT(CatalogNode->InfOriginalName == l4);
                    b = FALSE;
                    break;
                }

                //
                // PrevCatalogNode will end up pointing to the final node
                // currently in the linked list, in the case where we need
                // to allocate a new node. This is useful so we don't have to
                // traverse the list again later when we add the new catalog
                // node to the list for this queue.
                //
                PrevCatalogNode = CatalogNode;
            }

            if(b) {
                //
                // Need to create a new catalog node.
                //
                CatalogNode = MyMalloc(sizeof(SPQ_CATALOG_INFO));
                if(!CatalogNode) {
                    d = ERROR_NOT_ENOUGH_MEMORY;
                    goto clean2;
                }
                ZeroMemory(CatalogNode, sizeof(SPQ_CATALOG_INFO));
                CatalogNode->CatalogFileFromInf = l1;
                CatalogNode->InfFullPath = l2;
                CatalogNode->AltCatalogFileFromInf = l3;
                CatalogNode->InfOriginalName = l4;
                CatalogNode->AltCatalogFileFromInfPending = -1;
                CatalogNode->InfFinalPath = -1;
                //
                // Go ahead and link the new node into the list.  If we
                // encounter a failure later, we can easily back out of this by
                // truncating the list, since we know we always append, and we
                // remembered the original list tail.
                //
                if(Queue->CatalogList) {
                    PrevCatalogNode->Next = CatalogNode;
                } else {
                    Queue->CatalogList = CatalogNode;
                }

                //
                // We've successfully added a new, as yet unvalidated, catalog
                // node.  We must therefore reset the "catalog verifications
                // done" flags so that we'll redo them later.
                //
                Queue->Flags &= ~(FQF_DID_CATALOGS_OK | FQF_DID_CATALOGS_FAILED);
            }
        }
    }

    //
    // At this point, all the INFs involved in this installation (i.e., all INFs
    // in the HINFs we were passed in) have been added to our catalog info list,
    // if they weren't already present.  Now we need to figure out which one of
    // them should be associated with the file to be copied.
    //
    // Note that we want to get the CatalogFile= line from the actual inf
    // that contains source layout information for the file being queued.
    // If the layout inf handle references multiple append-loaded infs,
    // the simple mechanism of just looking up CatalogFile= in the [Version]
    // section using the given handle might give us the value from the
    // wrong inf.
    //
    // To deal with this, we attempt to locate the file in a [SourceDisksFiles]
    // section using the given inf handle, which gives us back a line context.
    // From the line context we can easily get at the [Version] section of the
    // actual inf where the file's layout info is contained.
    //
    // This handles all cases properly. For example, a file that is shipped by
    // a vendor that replaces one of our files. If the OEM's inf has a
    // SourceDisksFiles section with the file in it, it will be found first
    // when we look the file up using the given inf handle because of the way
    // inf append-loading works.
    //
    // If we cannot find the file in a [SourceDisksFiles] section (such as
    // if there is no such section), then we can't associate the file to be
    // copied with any INF/CAT.  If we do find a [SourceDisksFiles] entry, but
    // the containing INF doesn't specify a CatalogFile= entry, then we'll go
    // ahead and associate that with a SPQ_CATALOG_INFO node for that INF, but
    // that catalog info node will have a CatalogFileFromInf field of -1.
    // That's OK for system-provided INFs, but it will fail validation if it's
    // an OEM INF (this check is done later in _SetupVerifyQueuedCatalogs).
    //
    if(LayoutInfHandle || LayoutLineContext) {
        //
        // If we already have a valid layout line context, we don't need to go
        // looking for the file in [SourceDisksFiles] again (the caller is
        // assumed to have done that already). The caller might also have told
        // us that he *knows* that there is no [SourceDisksFiles] by passing us
        // a LayoutLineContext of -1.
        //
        if(LayoutLineContext == (PINFCONTEXT)(-1)) {
            //
            // For driver signing purposes, this may be an invalid file copy,
            // because it's being copied by an INF that contains no source
            // media information, nor does it use a layout file to supply such
            // information.
            //
            // Since we don't have a LayoutLineContext, we don't know exactly
            // which INF contained the CopyFile directive that initiated this
            // copy.  However, since the context is -1, that means that it was
            // INF based (i.e., as opposed to being manually queued up via
            // SetupQueueCopy).  Therefore, we scan all the INFs passed into
            // this routine (i.e., all the INFs in the pLoadedInfs lists), and
            // check to see if they're all located in %windir%\Inf.  If any of
            // them aren't, then we mark this copynode such that later it will
            // result in a signature verification failure of
            // ERROR_NO_CATALOG_FOR_OEM_INF.
            //
            for(i = 0; i < LoadedInfCount; i++) {

                for(pCurLoadedInf = pLoadedInfs[i]; pCurLoadedInf; pCurLoadedInf = pCurLoadedInf->Next) {

                    if(pSetupInfIsFromOemLocation(pCurLoadedInf->VersionBlock.Filename,
                                            TRUE)) {
                        //
                        // INF doesn't exist in %windir%\Inf--mark the copynode
                        // for codesigning verification failure.
                        //
                        QueueNode->InternalFlags |= IQF_FROM_BAD_OEM_INF;
                        break;
                    }

                    //
                    // Even if the INF does exist in %windir%\Inf, it might have
                    // originally been an OEM INF that was installed here--check
                    // its original filename to be sure...
                    //
                    if(pCurLoadedInf->OriginalInfName &&
                       pSetupInfIsFromOemLocation(pCurLoadedInf->OriginalInfName, TRUE)) {
                        //
                        // INF was an OEM INF--in this case, too, we need to
                        // mark the copynode for codesigning verification failure.
                        //
                        QueueNode->InternalFlags |= IQF_FROM_BAD_OEM_INF;
                        break;
                    }
                }

                if(QueueNode->InternalFlags & IQF_FROM_BAD_OEM_INF) {
                    //
                    // We found an OEM INF--no need to look any further.
                    //
                    break;
                }
            }

            LayoutLineContext = NULL;

        } else {
            if(!LayoutLineContext) {
                b = _SetupGetSourceFileLocation(
                        LayoutInfHandle,
                        NULL,
                        CopyParams->SourceFilename,
                        (Queue->Flags & FQF_USE_ALT_PLATFORM)
                           ? &(Queue->AltPlatformInfo)
                           : NULL,
                        NULL,
                        NULL,
                        0,
                        NULL,
                        &LineContext
                        );

                LayoutLineContext = b ? &LineContext : NULL;
            }
        }
    }

    //
    // At this point, a non-NULL LayoutLineContext indicates that we found an
    // INF to associate with the file to be copied (via a [SourceDisksFiles]
    // entry).
    //
    if(LayoutLineContext) {

        pSetupGetPhysicalInfFilepath(LayoutLineContext,
                                     TempBuffer,
                                     SIZECHARS(TempBuffer)
                                    );

        l2 = pSetupStringTableAddString(Queue->StringTable,
                                  TempBuffer,
                                  STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
                                 );
        //
        // This INF path should already be in the string table, and since we're
        // supplying a writeable buffer, there's no need for memory allocation.
        // Thus the addition of this string can't fail.
        //
        MYASSERT(l2 != -1);

        for(CatalogNode=Queue->CatalogList; CatalogNode; CatalogNode=CatalogNode->Next) {
            if(CatalogNode->InfFullPath == l2) {
                break;
            }
        }

        //
        // This node had better already be in the list!
        //
        MYASSERT(CatalogNode);

        QueueNode->CatalogInfo = CatalogNode;

    } else {
        //
        // There really is no catalog info.
        //
        QueueNode->CatalogInfo = NULL;
    }

    //
    // Unlock the INF(s) here, since the code below potentially returns without
    // hitting the final clean-up code at the bottom of the routine.
    //
    for(i = 0; i < LoadedInfCount; i++) {
        UnlockInf(pLoadedInfs[i]);
    }
    LoadedInfCount = 0;

    //
    // Set up the copy style flags
    //
    QueueNode->StyleFlags = CopyParams->CopyStyle;
    QueueNode->Next = NULL;

    //
    // Set up the source media.
    //
    try {
        Source = pSetupQueueSourceMedia(
                    Queue,
                    QueueNode,
                    QueueNode->SourceRootPath,
                    CopyParams->SourceDescription,
                    SourceTagfile,
                    SourceCabfile,
                    MediaFlags
                    );
        if(!Source) {
            d = ERROR_NOT_ENOUGH_MEMORY;
        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
        d = ERROR_INVALID_PARAMETER;
    }

    if(d != NO_ERROR) {
        goto clean2;
    }

    //
    // Link the node onto the end of the copy queue for this source media.
    //
    if(Source->CopyQueue) {
        //
        // Check to see if this same copy operation has already been enqueued
        // for this source media, and if so, get rid of the new one, to avoid
        // duplicates.  NOTE: We don't check the "InternalFlags" field, since
        // if the node already exists in the queue (based on all the other
        // fields comparing successfully), then any internal flags that were set
        // on the previously-existing node should be preserved.  (I.e., our new
        // node always is created with InternalFlags set to zero, except for
        // possibly IQF_FROM_BAD_OEM_INF, which we'll OR into the original
        // queue node's InternalFlags, if necessary.)
        //
        for(TempNode=Source->CopyQueue, PrevQueueNode = NULL;
            TempNode;
            PrevQueueNode = TempNode, TempNode=TempNode->Next) {

            if((TempNode->SourceRootPath == QueueNode->SourceRootPath) &&
               (TempNode->SourcePath == QueueNode->SourcePath) &&
               (TempNode->SourceFilename == QueueNode->SourceFilename) &&
               (TempNode->TargetDirectory == QueueNode->TargetDirectory) &&
               (TempNode->TargetFilename == QueueNode->TargetFilename) &&
               (TempNode->StyleFlags == QueueNode->StyleFlags) &&
               (TempNode->CatalogInfo == QueueNode->CatalogInfo)) {
                //
                // We have a duplicate.  OR in the IQF_FROM_BAD_OEM_INF flag
                // from our present queue node, if necessary, into the existing
                // queue node's InternalFlags.
                //
                if(QueueNode->InternalFlags & IQF_FROM_BAD_OEM_INF) {
                    TempNode->InternalFlags |= IQF_FROM_BAD_OEM_INF;
                }

                //
                // Now kill the newly-created queue node and return success.
                //
                MyFree(QueueNode);
                return TRUE;
            }
        }
        MYASSERT(PrevQueueNode);
        PrevQueueNode->Next = QueueNode;
    } else {
        Source->CopyQueue = QueueNode;
    }

    Queue->CopyNodeCount++;
    Source->CopyNodeCount++;

    return TRUE;

clean2:
    //
    // Truncate the catalog info node list at its original tail, and free all
    // subsequent (newly-added) nodes.
    //
    if(LastOldCatalogNode) {
        while(LastOldCatalogNode->Next) {
            CatalogNode = LastOldCatalogNode->Next;
            LastOldCatalogNode->Next = CatalogNode->Next;
            MyFree(CatalogNode);
        }
    }

clean1:
    MyFree(QueueNode);

clean0:
    for(i = 0; i < LoadedInfCount; i++) {
        UnlockInf(pLoadedInfs[i]);
    }

    SetLastError(d);
    return FALSE;
}


#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupQueueCopyIndirectA(
    IN PSP_FILE_COPY_PARAMS_A CopyParams
    )
{
    SP_FILE_COPY_PARAMS_WEX copyParams;
    DWORD rc;
    BOOL b;

    ZeroMemory(&copyParams,sizeof(SP_FILE_COPY_PARAMS_W));
    rc = NO_ERROR;
    b = FALSE;

    try {
        if(CopyParams->cbSize == sizeof(SP_FILE_COPY_PARAMS_W)) {
            copyParams.QueueHandle = CopyParams->QueueHandle;
            copyParams.CopyStyle = CopyParams->CopyStyle;
            copyParams.LayoutInf = CopyParams->LayoutInf;
            copyParams.SecurityDescriptor = NULL;
        } else {
            rc = ERROR_INVALID_PARAMETER;
        }
        if((rc == NO_ERROR) && CopyParams->SourceRootPath) {
            rc = pSetupCaptureAndConvertAnsiArg(CopyParams->SourceRootPath,&copyParams.SourceRootPath);
        }
        if((rc == NO_ERROR) && CopyParams->SourcePath) {
            rc = pSetupCaptureAndConvertAnsiArg(CopyParams->SourcePath,&copyParams.SourcePath);
        }
        if((rc == NO_ERROR) && CopyParams->SourceFilename) {
            rc = pSetupCaptureAndConvertAnsiArg(CopyParams->SourceFilename,&copyParams.SourceFilename);
        }
        if((rc == NO_ERROR) && CopyParams->SourceDescription) {
            rc = pSetupCaptureAndConvertAnsiArg(CopyParams->SourceDescription,&copyParams.SourceDescription);
        }
        if((rc == NO_ERROR) && CopyParams->SourceTagfile) {
            rc = pSetupCaptureAndConvertAnsiArg(CopyParams->SourceTagfile,&copyParams.SourceTagfile);
        }
        if((rc == NO_ERROR) && CopyParams->TargetDirectory) {
            rc = pSetupCaptureAndConvertAnsiArg(CopyParams->TargetDirectory,&copyParams.TargetDirectory);
        }
        if((rc == NO_ERROR) && CopyParams->TargetFilename) {
            rc = pSetupCaptureAndConvertAnsiArg(CopyParams->TargetFilename,&copyParams.TargetFilename);
        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
        //
        // This is to catch the case where the CopyParams pointer goes bad.
        //
        rc = ERROR_INVALID_PARAMETER;
    }

    if(rc == NO_ERROR) {

        copyParams.cbSize = sizeof(SP_FILE_COPY_PARAMS_WEX);

        b = _SetupQueueCopy(&copyParams, NULL, NULL);
        rc = GetLastError();
    }

    if(copyParams.SourceRootPath) {
        MyFree(copyParams.SourceRootPath);
    }
    if(copyParams.SourcePath) {
        MyFree(copyParams.SourcePath);
    }
    if(copyParams.SourceFilename) {
        MyFree(copyParams.SourceFilename);
    }
    if(copyParams.SourceDescription) {
        MyFree(copyParams.SourceDescription);
    }
    if(copyParams.SourceTagfile) {
        MyFree(copyParams.SourceTagfile);
    }
    if(copyParams.TargetDirectory) {
        MyFree(copyParams.TargetDirectory);
    }
    if(copyParams.TargetFilename) {
        MyFree(copyParams.TargetFilename);
    }

    SetLastError(rc);
    return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupQueueCopyIndirectW(
    IN PSP_FILE_COPY_PARAMS_W CopyParams
    )
{
    UNREFERENCED_PARAMETER(CopyParams);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return(FALSE);
}
#endif

BOOL
SetupQueueCopyIndirect(
    IN PSP_FILE_COPY_PARAMS CopyParams
    )
{
    BOOL b;
    SP_FILE_COPY_PARAMSEX copyParamsEx = {0};

    //
    // All work is done by an internal subroutine.
    // The only thing we need to do here is validate the size
    // of the structure we've been given by the caller.
    //
    try {
        b = (CopyParams->cbSize == sizeof(SP_FILE_COPY_PARAMS));
        if (b) {
            CopyMemory(&copyParamsEx,CopyParams,sizeof(SP_FILE_COPY_PARAMS));
            copyParamsEx.cbSize = sizeof(SP_FILE_COPY_PARAMSEX);
        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
        b = FALSE;
    }

    if(b) {
        b = _SetupQueueCopy(&copyParamsEx, NULL, NULL);
    } else {
        SetLastError(ERROR_INVALID_PARAMETER);
    }

    return(b);
}


#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupQueueCopySectionA(
    IN HSPFILEQ QueueHandle,
    IN PCSTR    SourceRootPath,
    IN HINF     InfHandle,
    IN HINF     ListInfHandle,      OPTIONAL
    IN PCSTR    Section,
    IN DWORD    CopyStyle
    )
{
    PWSTR sourcerootpath;
    PWSTR section;
    DWORD rc;
    BOOL b;

    rc = pSetupCaptureAndConvertAnsiArg(SourceRootPath,&sourcerootpath);
    if(rc != NO_ERROR) {
        SetLastError(rc);
        return(FALSE);
    }
    rc = pSetupCaptureAndConvertAnsiArg(Section,&section);
    if(rc != NO_ERROR) {
        MyFree(sourcerootpath);
        SetLastError(rc);
        return(FALSE);
    }

    b = SetupQueueCopySectionW(
            QueueHandle,
            sourcerootpath,
            InfHandle,
            ListInfHandle,
            section,
            CopyStyle
            );

    rc = GetLastError();

    MyFree(sourcerootpath);
    MyFree(section);

    SetLastError(rc);
    return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupQueueCopySectionW(
    IN HSPFILEQ QueueHandle,
    IN PCWSTR   SourceRootPath,
    IN HINF     InfHandle,
    IN HINF     ListInfHandle,      OPTIONAL
    IN PCWSTR   Section,
    IN DWORD    CopyStyle
    )
{
    UNREFERENCED_PARAMETER(QueueHandle);
    UNREFERENCED_PARAMETER(SourceRootPath);
    UNREFERENCED_PARAMETER(InfHandle);
    UNREFERENCED_PARAMETER(ListInfHandle);
    UNREFERENCED_PARAMETER(Section);
    UNREFERENCED_PARAMETER(CopyStyle);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return(FALSE);
}
#endif

BOOL
SetupQueueCopySection(
    IN HSPFILEQ QueueHandle,
    IN PCTSTR   SourceRootPath,
    IN HINF     InfHandle,
    IN HINF     ListInfHandle,   OPTIONAL
    IN PCTSTR   Section,
    IN DWORD    CopyStyle
    )

/*++

Routine Description:

    Queue an entire section in an inf file for copy. The section must be
    in copy-section format and the inf file must contain [SourceDisksFiles]
    and [SourceDisksNames] sections.

Arguments:

    QueueHandle - supplies a handle to a setup file queue, as returned
        by SetupOpenFileQueue.

    SourceRootPath - supplies the root directory for the intended source.
        This should be a sharepoint or a device root such as a:\ or g:\.

    InfHandle - supplies a handle to an open inf file, that contains the
        [SourceDisksFiles] and [SourceDisksNames] sections, and, if
        ListInfHandle is not specified, contains the section names by Section.
        This handle must be for a win95-style inf.

    ListInfHandle - if specified, supplies a handle to an open inf file
        containing the section to be queued for copy. Otherwise InfHandle
        is assumed to contain the section.

    Section - supplies the name of the section to be queued for copy.

    CopyStyle - supplies flags that control the behavior of the copy operation
        for this file.

Return Value:

    Boolean value indicating outcome. If FALSE, GetLastError() returns
    extended error information. Some of the files may have been queued.

--*/

{
    BOOL b;
    INFCONTEXT LineContext;
    PCTSTR SourceFilename;
    PCTSTR TargetFilename;
    PCTSTR SecurityDescriptor = NULL;
    PCTSTR CacheName = NULL;
    UINT Flags;
    DWORD CopyStyleLocal;
    LONG LineCount;
    HINF CabInf = INVALID_HANDLE_VALUE;
    DWORD rc;

    //
    // Note that there are no potential faults here so no try/excepts
    // are necessary. pSetupQueueSingleCopy does all validation.
    //

    if(!ListInfHandle || (ListInfHandle == INVALID_HANDLE_VALUE)) {
        ListInfHandle = InfHandle;
    }

    //
    // Check for missing section
    //
    LineCount = SetupGetLineCount (ListInfHandle, Section);
    if(LineCount == -1) {
        rc = GetLastError();
        pSetupLogSectionError(ListInfHandle,NULL,NULL,QueueHandle,Section,MSG_LOG_NOSECTION_COPY,rc,NULL);
        SetLastError(ERROR_SECTION_NOT_FOUND); // maintain existing error code, log contains correct error code
        return(FALSE);
    }

    //
    // if section is empty, do nothing.
    //
    if(LineCount == 0) {
        return(TRUE);
    }

    //
    // The section has to exist and there has to be at least one line in it.
    //
    b = SetupFindFirstLine(ListInfHandle,Section,NULL,&LineContext);
    if(!b) {
        rc = GetLastError();
        pSetupLogSectionError(ListInfHandle,NULL,NULL,QueueHandle,Section,MSG_LOG_NOSECTION_COPY,rc,NULL);
        SetLastError(ERROR_SECTION_NOT_FOUND); // maintain existing error code, log contains correct error code
        return(FALSE);
    }

    //
    //Get the Security descriptor
    //

    if( !pSetupGetSecurityInfo( ListInfHandle, Section, &SecurityDescriptor ) )
        SecurityDescriptor = NULL;


    //
    // load driver cache inf
    //
    CabInf = SetupOpenInfFile( STR_DRIVERCACHEINF , NULL, INF_STYLE_WIN4, NULL );
    if (CabInf != INVALID_HANDLE_VALUE) {
        CopyStyle |= PSP_COPY_USE_DRIVERCACHE;
    }

    //
    // Iterate every line in the section.
    //
    do {
        CopyStyleLocal = CopyStyle;
        //
        // Get the target filename out of the line.
        // Field 1 is the target so there must be one for the line to be valid.
        //
        TargetFilename = pSetupFilenameFromLine(&LineContext,FALSE);
        if(!TargetFilename) {
            if (CabInf != INVALID_HANDLE_VALUE) {
                SetupCloseInfFile(CabInf);
            }
            try {
                if (QueueHandle != NULL
                    && QueueHandle != (HSPFILEQ)INVALID_HANDLE_VALUE
                    && ((PSP_FILE_QUEUE)QueueHandle)->Signature == SP_FILE_QUEUE_SIG) {

                    WriteLogEntry(
                        ((PSP_FILE_QUEUE)QueueHandle)->LogContext,
                        SETUP_LOG_ERROR,
                        MSG_LOG_COPY_TARGET,
                        NULL,
                        Section);
                }
            } except(EXCEPTION_EXECUTE_HANDLER) {
            }
            SetLastError(ERROR_INVALID_DATA);
            return(FALSE);
        }

        //
        // Get source filename out of the line. If there is none, use
        // the target name as the source name.
        //
        SourceFilename = pSetupFilenameFromLine(&LineContext,TRUE);
        if(!SourceFilename || (*SourceFilename == 0)) {
            SourceFilename = TargetFilename;
        }

        //
        // if we were asked to use the driver cache, then check if the file
        // is in the associated INF for the cab.
        //
        if (CabInf != INVALID_HANDLE_VALUE) {
            if (!pIsFileInDriverCache(CabInf, SourceFilename, NULL, &CacheName)) {
                CopyStyleLocal &= ~PSP_COPY_USE_DRIVERCACHE;
            }
        }

        //
        // If present, flags are field 3.
        //
        if(SetupGetIntField(&LineContext,4,(PINT)&Flags)) {

            if(Flags & COPYFLG_WARN_IF_SKIP) {
                CopyStyleLocal |= SP_COPY_WARNIFSKIP;
            }

            if(Flags & COPYFLG_NOSKIP) {
                CopyStyleLocal |= SP_COPY_NOSKIP;
            }

            if(Flags & COPYFLG_NOVERSIONCHECK) {
                CopyStyleLocal &= ~SP_COPY_NEWER;
            }

            if(Flags & COPYFLG_FORCE_FILE_IN_USE) {
                CopyStyleLocal |= SP_COPY_FORCE_IN_USE;
                CopyStyleLocal |= SP_COPY_IN_USE_NEEDS_REBOOT;
            }

            if(Flags & COPYFLG_NO_OVERWRITE) {
                CopyStyleLocal |= SP_COPY_FORCE_NOOVERWRITE;
            }

            if(Flags & COPYFLG_NO_VERSION_DIALOG) {
                CopyStyleLocal |= SP_COPY_FORCE_NEWER;
            }

            if(Flags & COPYFLG_OVERWRITE_OLDER_ONLY) {
                CopyStyleLocal |= SP_COPY_NEWER_ONLY;
            }

            if(Flags & COPYFLG_REPLACEONLY) {
                CopyStyleLocal |= SP_COPY_REPLACEONLY;
            }

            if(Flags & COPYFLG_NODECOMP) {
                CopyStyleLocal |= SP_COPY_NODECOMP;
            }

            if(Flags & COPYFLG_REPLACE_BOOT_FILE) {
                CopyStyleLocal |= SP_COPY_REPLACE_BOOT_FILE;
            }

            if(Flags & COPYFLG_NOPRUNE) {
                CopyStyleLocal |= SP_COPY_NOPRUNE;
            }

        }

        b = pSetupQueueSingleCopy(
                QueueHandle,
                InfHandle,
                ListInfHandle,
                Section,
                SourceRootPath,
                SourceFilename,
                TargetFilename,
                CopyStyleLocal,
                SecurityDescriptor,
                CacheName
                );

        if (CacheName) {
            MyFree( CacheName );
            CacheName = NULL;
        }

        if(!b) {
            DWORD LastError = GetLastError();

            if (CabInf != INVALID_HANDLE_VALUE) {
                SetupCloseInfFile(CabInf);
            }

            SetLastError( LastError );

            return(FALSE);
        }
    } while(SetupFindNextLine(&LineContext,&LineContext));

    if (CabInf != INVALID_HANDLE_VALUE) {
        SetupCloseInfFile(CabInf);
    }
    return(TRUE);
}


BOOL
pSetupQueueSingleCopy(
    IN HSPFILEQ    QueueHandle,
    IN HINF        InfHandle,
    IN HINF        ListInfHandle,  OPTIONAL
    IN PCTSTR      SectionName,    OPTIONAL
    IN PCTSTR      SourceRootPath,
    IN PCTSTR      SourceFilename,
    IN PCTSTR      TargetFilename,
    IN DWORD       CopyStyle,
    IN PCTSTR      SecurityDescriptor,
    IN PCTSTR      CacheName
    )

/*++

Routine Description:

    Add a single file to the copy queue, using the default source media
    and destination as specified in an inf file.

Arguments:

    QueueHandle - supplies a handle to a setup file queue, as returned
        by SetupOpenFileQueue.

    InfHandle - supplies a handle to an open inf file, that contains the
        [SourceDisksFiles] and [SourceDisksNames] sections.
        This handle must be for a win95-style inf.

    ListInfHandle - if specified, supplies handle to the inf in which
        the file being copied appears (such as in a file copy list section).
        If not specified, this is assumed to be the same inf as InfHandle.

    SourceRootPath - supplies the root directory for the intended source.
        This should be a sharepoint or a device root such as a:\ or g:\.

    SourceFilename - supplies the filename of the source file. Filename part
        only.

    TargetFilename - supplies the filename of the target file. Filename part
        only.

    CopyStyle - supplies flags that control the behavior of the copy operation
        for this file.

    SecurityDescriptor - describes the permissions for the target file

    CacheName - if supplied this is the name of the driver cache we should
                use to copy the file out of instead of the specified source path

Return Value:

    Boolean value indicating outcome. If FALSE, GetLastError() returns
    extended error information.

--*/

{
    BOOL b;
    UINT SourceId;
    DWORD SizeRequired;
    PTSTR TargetDirectory;
    PCTSTR SourceDescription,SourceTagfile,SourceRelativePath;
    PCTSTR TmpCacheName = CacheName;
    UINT SourceFlags;
    DWORD rc;
    TCHAR FileSubdir[MAX_PATH];
    TCHAR RelativePath[MAX_PATH];
    INFCONTEXT LineContext;
    PINFCONTEXT pLineContext;
    SP_FILE_COPY_PARAMSEX CopyParams;
    HINF CabInf = INVALID_HANDLE_VALUE;
    PSETUP_LOG_CONTEXT lc = NULL;
    BOOL AlreadyLoggedError = FALSE;

    if(!ListInfHandle || (ListInfHandle == INVALID_HANDLE_VALUE)) {
        ListInfHandle = InfHandle;
    }

    //
    // Determine the source disk id and subdir where the file is located.
    //
    try {

        if((QueueHandle != NULL) &&
           (QueueHandle != INVALID_HANDLE_VALUE) &&
           (((PSP_FILE_QUEUE)QueueHandle)->Signature == SP_FILE_QUEUE_SIG)) {

            lc = ((PSP_FILE_QUEUE)QueueHandle)->LogContext;

            b = _SetupGetSourceFileLocation(
                    InfHandle,
                    NULL,
                    SourceFilename,
                    (((PSP_FILE_QUEUE)QueueHandle)->Flags & FQF_USE_ALT_PLATFORM)
                       ? &(((PSP_FILE_QUEUE)QueueHandle)->AltPlatformInfo)
                       : NULL,
                    &SourceId,
                    FileSubdir,
                    MAX_PATH,
                    &rc,
                    &LineContext
                    );

            if(!b) {
                rc = GetLastError();
            }

        } else {
            b = FALSE;
            rc = ERROR_INVALID_HANDLE;
        }

    } except(EXCEPTION_EXECUTE_HANDLER) {
        b = FALSE;
        rc = ERROR_INVALID_HANDLE;
        lc = NULL;
    }

    if(!b) {

        if((rc == ERROR_INVALID_PARAMETER) || (rc == ERROR_INVALID_HANDLE)) {
            //
            // if we failed due to a bad parameter, bail now
            //
            goto clean1;
        }

        //
        // Try to fetch just the id and assume there is no subdir.
        //
        try {

            b = _SetupGetSourceFileLocation(
                    InfHandle,
                    NULL,
                    SourceFilename,
                    (((PSP_FILE_QUEUE)QueueHandle)->Flags & FQF_USE_ALT_PLATFORM)
                       ? &(((PSP_FILE_QUEUE)QueueHandle)->AltPlatformInfo)
                       : NULL,
                    &SourceId,
                    NULL,
                    0,
                    NULL,
                    &LineContext
                    );

        } except(EXCEPTION_EXECUTE_HANDLER) {
            b = FALSE;
        }

        if(b) {
            FileSubdir[0] = 0;
        }
    }

    if(b) {
        //
        // Get information about the source. Need the tag file,
        // description, and relative source path.
        //
        try {

            b = pSetupGetSourceAllInfo(
                    InfHandle,
                    &LineContext,
                    SourceId,
                    (((PSP_FILE_QUEUE)QueueHandle)->Flags & FQF_USE_ALT_PLATFORM)
                       ? &(((PSP_FILE_QUEUE)QueueHandle)->AltPlatformInfo)
                       : NULL,
                    &SourceDescription,
                    &SourceTagfile,
                    &SourceRelativePath,
                    &SourceFlags
                    );

            if(!b) {
                rc = GetLastError();
                if((rc == ERROR_LINE_NOT_FOUND) || (rc == ERROR_SECTION_NOT_FOUND)) {
                    WriteLogEntry(
                        lc,
                        SETUP_LOG_ERROR,
                        MSG_LOG_NO_SOURCE,
                        NULL,
                        SourceId
                        );
                    AlreadyLoggedError = TRUE;
                }
            }

        } except(EXCEPTION_EXECUTE_HANDLER) {
            b = FALSE;
            rc = ERROR_INVALID_PARAMETER;
        }

        if(!b) {
            goto clean1;
        }

        //
        // Set a value that causes _SetupQueueCopy to skip looking for the
        // [SourceDisksFiles] section -- we just found it, so we just pass
        // the info along!
        //
        pLineContext = &LineContext;

    } else {
        //
        // Assume there is no SourceDisksFiles section and fake it as best we can.
        // Assume the media has a description of "Unknown," set the source path to
        // the source root if there is one, and assume no tag file.
        //
        // We also set a special value that tells _SetupQueueCopy not to bother trying
        // to look for the [SourceDisksFiles] section itself, since there isn't one.
        //
        FileSubdir[0] = 0;
        SourceDescription = NULL;
        SourceTagfile = NULL;
        SourceRelativePath = NULL;
        pLineContext = (PINFCONTEXT)(-1);
    }

    if ( CopyStyle & PSP_COPY_CHK_DRIVERCACHE) {
        CabInf = SetupOpenInfFile( STR_DRIVERCACHEINF, NULL, INF_STYLE_WIN4, NULL );
        if (CabInf != INVALID_HANDLE_VALUE) {
            if (pIsFileInDriverCache(CabInf, SourceFilename, SourceRelativePath, &TmpCacheName)) {
                CopyStyle |= PSP_COPY_USE_DRIVERCACHE;
                CopyStyle &= ~PSP_COPY_CHK_DRIVERCACHE;
            }

            SetupCloseInfFile(CabInf);

        }
    }

    if (CopyStyle & PSP_COPY_USE_DRIVERCACHE) {
        //
        // check if the inf we want to copy from is an OEM inf
        //
        if (!pLineContext || pLineContext==(PINFCONTEXT)-1) {
            CopyStyle &= ~PSP_COPY_USE_DRIVERCACHE;
        } else if (pSetupInfIsFromOemLocation( ((PLOADED_INF)pLineContext->CurrentInf)->VersionBlock.Filename,TRUE )) {
            CopyStyle &= ~PSP_COPY_USE_DRIVERCACHE;
        } else if ( ((PLOADED_INF)pLineContext->CurrentInf)->OriginalInfName
                    && pSetupInfIsFromOemLocation( ((PLOADED_INF)pLineContext->CurrentInf)->OriginalInfName, TRUE) ) {
            CopyStyle &= ~PSP_COPY_USE_DRIVERCACHE;
        }
    }

    //
    // Determine the target path for the file.
    //
    if(b = SetupGetTargetPath(ListInfHandle,NULL,SectionName,NULL,0,&SizeRequired)) {

        if(TargetDirectory = MyMalloc(SizeRequired*sizeof(TCHAR))) {

            if(b = SetupGetTargetPath(ListInfHandle,NULL,SectionName,TargetDirectory,SizeRequired,NULL)) {

                try {
                    WriteLogEntry(
                        lc,
                        SETUP_LOG_VVERBOSE,
                        SectionName ? MSG_LOG_COPY_QUEUE : MSG_LOG_DEFCOPY_QUEUE,
                        NULL,
                        SectionName ? SectionName : TEXT(""),
                        ((PLOADED_INF)ListInfHandle)->VersionBlock.Filename,
                        TargetFilename ? TargetFilename : TEXT(""),
                        SourceFilename ? SourceFilename : TEXT(""),
                        CopyStyle,
                        TargetDirectory ? TargetDirectory : TEXT(""));
                    if (pLineContext && (pLineContext != (PINFCONTEXT)(-1))) {
                        LPCTSTR SrcSecName = NULL;
                        LPCTSTR SrcInfName = NULL;
                        PLOADED_INF pInf = (PLOADED_INF)(pLineContext->CurrentInf);

                        MYASSERT(pInf);
                        SrcSecName = pStringTableStringFromId(
                                                pInf->StringTable,
                                                pInf->SectionBlock[pLineContext->Section].SectionName);
                        SrcInfName = pInf->VersionBlock.Filename;
                        WriteLogEntry(
                            lc,
                            SETUP_LOG_VVERBOSE,
                            (CopyStyle & PSP_COPY_USE_DRIVERCACHE) ? MSG_LOG_COPY_QUEUE_DRIVERCACHE : MSG_LOG_COPY_QUEUE_SOURCE,
                            NULL,
                            SrcSecName ? SrcSecName : TEXT(""),
                            SrcInfName ? SrcInfName : TEXT(""),
                            SourceId ? SourceId : TEXT('\0'),
                            SourceDescription ? SourceDescription : TEXT(""),
                            SourceTagfile ? SourceTagfile : TEXT(""),
                            SourceRelativePath ? SourceRelativePath : TEXT(""));

                    } else {
                        WriteLogEntry(
                            lc,
                            SETUP_LOG_VVERBOSE,
                            MSG_LOG_COPY_QUEUE_DEFAULT,
                            NULL);
                    }
                } except(EXCEPTION_EXECUTE_HANDLER) {
                }
                //
                // Append the source relative path and the file subdir.
                //
                if(SourceRelativePath) {
                    lstrcpyn(RelativePath,SourceRelativePath,MAX_PATH);
                    if(FileSubdir[0]) {
                        pSetupConcatenatePaths(RelativePath,FileSubdir,MAX_PATH,NULL);
                    }
                } else {
                    RelativePath[0] = 0;
                }

                //
                // Add to queue.
                //
                CopyParams.cbSize            = sizeof(SP_FILE_COPY_PARAMSEX);
                CopyParams.QueueHandle       = QueueHandle;
                CopyParams.SourceRootPath    = SourceRootPath;
                CopyParams.SourcePath        = RelativePath[0] ? RelativePath : NULL ;
                CopyParams.SourceFilename    = SourceFilename;
                CopyParams.SourceDescription = SourceDescription;
                CopyParams.SourceTagfile     = SourceTagfile;
                CopyParams.TargetDirectory   = TargetDirectory;
                CopyParams.TargetFilename    = TargetFilename;
                CopyParams.CopyStyle         = CopyStyle;
                CopyParams.LayoutInf         = InfHandle;
                CopyParams.SecurityDescriptor= SecurityDescriptor;
                CopyParams.CacheName         = TmpCacheName;
                //
                // first item indicates source flag information
                // second item indicates that we've already retrieved
                // this information, so even if the SourceFlags are zero,
                // we won't go looking for it again
                //
                CopyParams.SourceFlags       = SourceFlags;
                CopyParams.SourceFlagsSet    = TRUE;

                b = _SetupQueueCopy(&CopyParams,
                                    pLineContext,
                                    ((InfHandle == ListInfHandle) ? NULL : ListInfHandle)
                                   );

                rc = GetLastError();
            }

            MyFree(TargetDirectory);

        } else {
            rc = ERROR_NOT_ENOUGH_MEMORY;
        }
    } else {
        rc = GetLastError();
    }

    if(SourceDescription) {
        MyFree(SourceDescription);
    }
    if(SourceTagfile) {
        MyFree(SourceTagfile);
    }
    if(SourceRelativePath) {
        MyFree(SourceRelativePath);
    }
    if(TmpCacheName && TmpCacheName != CacheName) {
        MyFree(TmpCacheName);
    }

clean1:
    if(!b) {

        BOOL FreeLC = FALSE;

        if(!lc) {
            if(CreateLogContext(NULL, TRUE, &lc) == NO_ERROR) {
                //
                // success
                //
                FreeLC = TRUE;
            } else {
                lc = NULL;
            }
        }

        //
        // If we couldn't create a log context (i.e., due to out-of-memory),
        // don't bother calling WriteLogEntry, because it's not going to have
        // any better luck...
        //
        if(lc) {

            if(!AlreadyLoggedError) {
                try {
                    WriteLogEntry(
                        lc,
                        SETUP_LOG_ERROR|SETUP_LOG_BUFFER,
                        MSG_LOG_COPY_QUEUE_ERROR,
                        NULL,
                        SectionName ? SectionName : TEXT(""),
                        ((PLOADED_INF)ListInfHandle)->VersionBlock.Filename,
                        TargetFilename ? TargetFilename : TEXT(""),
                        SourceFilename ? SourceFilename : TEXT(""));
                    WriteLogError(
                        lc,
                        SETUP_LOG_ERROR,
                        rc
                        );
                } except(EXCEPTION_EXECUTE_HANDLER) {
                }
            }

            if(FreeLC) {
                DeleteLogContext(lc);
            }
        }

        SetLastError(rc);
    }

    return(b);
}


#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupQueueDefaultCopyA(
    IN HSPFILEQ QueueHandle,
    IN HINF     InfHandle,
    IN PCSTR    SourceRootPath,
    IN PCSTR    SourceFilename,
    IN PCSTR    TargetFilename,
    IN DWORD    CopyStyle
    )
{
    PWSTR sourcerootpath;
    PWSTR sourcefilename;
    PWSTR targetfilename;
    DWORD rc;
    BOOL b;

    b = FALSE;
    rc = pSetupCaptureAndConvertAnsiArg(SourceRootPath,&sourcerootpath);
    if(rc == NO_ERROR) {

        rc = pSetupCaptureAndConvertAnsiArg(SourceFilename,&sourcefilename);
        if(rc == NO_ERROR) {

            rc = pSetupCaptureAndConvertAnsiArg(TargetFilename,&targetfilename);
            if(rc == NO_ERROR) {

                b = SetupQueueDefaultCopyW(
                        QueueHandle,
                        InfHandle,
                        sourcerootpath,
                        sourcefilename,
                        targetfilename,
                        CopyStyle
                        );

                rc = GetLastError();

                MyFree(targetfilename);
            }

            MyFree(sourcefilename);
        }

        MyFree(sourcerootpath);
    }

    SetLastError(rc);
    return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupQueueDefaultCopyW(
    IN HSPFILEQ QueueHandle,
    IN HINF     InfHandle,
    IN PCWSTR   SourceRootPath,
    IN PCWSTR   SourceFilename,
    IN PCWSTR   TargetFilename,
    IN DWORD    CopyStyle
    )
{
    UNREFERENCED_PARAMETER(QueueHandle);
    UNREFERENCED_PARAMETER(InfHandle);
    UNREFERENCED_PARAMETER(SourceRootPath);
    UNREFERENCED_PARAMETER(SourceFilename);
    UNREFERENCED_PARAMETER(TargetFilename);
    UNREFERENCED_PARAMETER(CopyStyle);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return(FALSE);
}
#endif


BOOL
SetupQueueDefaultCopy(
    IN HSPFILEQ QueueHandle,
    IN HINF     InfHandle,
    IN PCTSTR   SourceRootPath,
    IN PCTSTR   SourceFilename,
    IN PCTSTR   TargetFilename,
    IN DWORD    CopyStyle
    )

/*++

Routine Description:

    Add a single file to the copy queue, using the default source media
    and destination as specified in an inf file.

Arguments:

    QueueHandle - supplies a handle to a setup file queue, as returned
        by SetupOpenFileQueue.

    InfHandle - supplies a handle to an open inf file, that contains the
        [SourceDisksFiles] and [SourceDisksNames] sections.
        This handle must be for a win95-style inf.

    SourceRootPath - supplies the root directory for the intended source.
        This should be a sharepoint or a device root such as a:\ or g:\.

    SourceFilename - supplies the filename of the source file. Filename part
        only.

    TargetFilename - supplies the filename of the target file. Filename part
        only.

    CopyStyle - supplies flags that control the behavior of the copy operation
        for this file.

Return Value:

    Boolean value indicating outcome. If FALSE, GetLastError() returns
    extended error information.

--*/

{
    BOOL b;

    b = pSetupQueueSingleCopy(
            QueueHandle,
            InfHandle,
            NULL,
            NULL,
            SourceRootPath,
            SourceFilename,
            TargetFilename,
            CopyStyle | PSP_COPY_CHK_DRIVERCACHE,
            NULL,
            NULL
            );

    return(b);
}


PSOURCE_MEDIA_INFO
pSetupQueueSourceMedia(
    IN OUT PSP_FILE_QUEUE      Queue,
    IN OUT PSP_FILE_QUEUE_NODE QueueNode,
    IN     LONG                SourceRootStringId,
    IN     PCTSTR              SourceDescription,   OPTIONAL
    IN     PCTSTR              SourceTagfile,       OPTIONAL
    IN     PCTSTR              SourceCabfile,       OPTIONAL
    IN     DWORD               MediaFlags
    )

/*++

Routine Description:

    Set up a file queue node's source media descriptor pointer, creating a new
    source media descriptor if necessary.

Arguments:

    Queue - supplies pointer to file queue with which the queue node
        is associated.

    QueueNode - supplies file queue node whose source media descriptor pointer
        is to be set.

    SourceRootStringId - supplies string id of root to source (something like a:\).

    SourceDescription - if specified, supplies a description for the media.

    SourceTagfile - if specified, supplies a tag file for the media.

    SourceCabfile - if specified, supplies a cabfile for the media different to the tagfile.

    MediaFlags - specifies additional information used in searching for an
        existing source media descriptor in the specified queue, and in adding
        new source media descriptors to that queue.  May be a combination of
        the following values:

        SMI_FLAG_NO_SOURCE_ROOT_PATH : The caller didn't supply a SourceRootPath
            for this copy action, so we're using a default path.  This flag
            causes us to not include the SourceRootStringId as part of our
            match criteria when searching to see if the specified source media
            information is already present in an existing media descriptor.  If
            we don't find a match (i.e., we have to create a new descriptor),
            we'll store this flag away in the SOURCE_MEDIA_INFO.Flags field so
            that if we come along later to add source media descriptors where
            the caller did specify SourceRootPath, then we'll re-use this
            descriptor and overwrite the existing (default) source root path
            with the caller-specified one.

        SMI_FLAG_USE_SVCPACK_SOURCE_ROOT_PATH : The caller didn't supply a SourceRootPath
            for this copy action, and it's a tagged source media, so we're using a
            service pack path.  This flag  causes us to not include the SourceRootStringId
            as part of our match criteria when searching to see if the specified source media
            information is already present in an existing media descriptor.  If
            we don't find a match (i.e., we have to create a new descriptor),
            we'll store this flag away in the SOURCE_MEDIA_INFO.Flags field so
            that if we come along later to add source media descriptors where
            the caller did specify SourceRootPath, then we'll re-use this
            descriptor and overwrite the existing (default) source root path
            with the caller-specified one.

        SMI_FLAG_USE_LOCAL_SOURCE_CAB : The caller wants to use the local source cab containing
            driver files, etc.  In this case, we supply the source description and tagfile,
            ignoring what the caller passes in.  At this point we know the media is present, as
            the caller provided this check.  If it wasnt't, we default to the OS Source path location.


Return Value:

    Pointer to source media info structure, or NULL if out of memory.

--*/

{
    LONG DescriptionStringId;
    LONG TagfileStringId;
    LONG CabfileStringId;
    PSOURCE_MEDIA_INFO Source,LastSource, TempSource;
    BOOL b1,b2,b3;
    TCHAR TempTagfileString[MAX_PATH];
    TCHAR TempCabfileString[MAX_PATH];
    TCHAR TempSrcDescString[LINE_LEN];


    if (MediaFlags & SMI_FLAG_USE_LOCAL_SOURCE_CAB) {
        LoadString( MyDllModuleHandle, IDS_DRIVERCACHE_DESC, TempSrcDescString, sizeof(TempSrcDescString)/sizeof(TCHAR) );
        SourceDescription = TempSrcDescString;
    } else {
        //
        // For the optional SourceDescription and SourceTagfile parameters, treat
        // empty strings as if the parameter had not been specified.
        //
        if(SourceDescription && !(*SourceDescription)) {
            SourceDescription = NULL;
        }
        if(SourceTagfile && !(*SourceTagfile)) {
            SourceTagfile = NULL;
        }

        //
        // If no description is specified, force the tagfile to none.
        //
        if(!SourceDescription) {
            SourceTagfile = NULL;
        }
    }

    if(SourceDescription) {
        //
        // Description specified. See if it's in the table. If not,
        // no need to search the list of media descriptors because we know
        // we can't find a match.
        //
        // (We must first copy this string to a writeable buffer, to speed up the
        // case-insensitive lookup.
        //
        lstrcpyn(TempSrcDescString, SourceDescription, SIZECHARS(TempSrcDescString));
        DescriptionStringId = pSetupStringTableLookUpString(Queue->StringTable,
                                                      TempSrcDescString,
                                                      STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
                                                     );
        b1 = (DescriptionStringId != -1);
    } else {
        //
        // No description specified, look for a source media with -1 as the
        // description string id
        //
        DescriptionStringId = -1;
        b1 = TRUE;
    }

    if(SourceTagfile) {
        //
        // Tagfile specified. See if it's in the table. If not,
        // no need to search the list of media descriptors because we know
        // we can't find a match.
        //
        // (Again, we must first copy the string to a writeable buffer.
        //
        lstrcpyn(TempTagfileString, SourceTagfile, SIZECHARS(TempTagfileString));
        TagfileStringId = pSetupStringTableLookUpString(Queue->StringTable,
                                                  TempTagfileString,
                                                  STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
                                                 );
        b2 = (TagfileStringId != -1);
    } else {
        //
        // No tagfile specified, look for a source media with -1 as the
        // tagfile string id
        //
        TagfileStringId = -1;
        b2 = TRUE;
    }

    if(SourceCabfile) {
        //
        // Cabfile specified. See if it's in the table. If not,
        // no need to search the list of media descriptors because we know
        // we can't find a match.
        //
        // (Again, we must first copy the string to a writeable buffer.
        //
        lstrcpyn(TempCabfileString, SourceCabfile, SIZECHARS(TempCabfileString));
        CabfileStringId = pSetupStringTableLookUpString(Queue->StringTable,
                                                  TempCabfileString,
                                                  STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
                                                 );
        b3 = (CabfileStringId != -1);
    } else {
        //
        // No cabfile specified, merge Cabfile&Tagfile together
        // since b2==b3, then we have the identities b2|b3 == b2 and b2&b3 == b2
        // ie, old behavior
        //
        CabfileStringId = TagfileStringId;
        b3 = b2;
    }

    //
    // If we think there's a possibility of finding an existing source that
    // matches the caller's parameters, scan the source media list looking
    // for a match.
    //
    if(b1 && b2 && b3) {

        for(Source=Queue->SourceMediaList; Source; Source=Source->Next) {

            if((Source->Description == DescriptionStringId)
               && (Source->Tagfile == TagfileStringId)
               && (Source->Cabfile == CabfileStringId)) {
                //
                // We only consider the SourceRootPath when both existing
                // media descriptor and new media descriptor have actual
                // caller-supplied paths (as opposed to something we made up).
                //
                if((Source->Flags & SMI_FLAG_NO_SOURCE_ROOT_PATH) ||
                   (MediaFlags & SMI_FLAG_NO_SOURCE_ROOT_PATH) ||
                   (Source->SourceRootPath == SourceRootStringId)) {
                    //
                    // Got a match. Point the queue node at this source and return.
                    //
                    QueueNode->SourceMediaInfo = Source;
                    //
                    // If the existing media descriptor had a made-up source
                    // root path, but the new media information had an actual
                    // caller-supplied one, then replace the made-up one with
                    // the real one and clear the no-source-root-path flag.
                    //
                    if((Source->Flags & SMI_FLAG_NO_SOURCE_ROOT_PATH) &&
                       !(MediaFlags & SMI_FLAG_NO_SOURCE_ROOT_PATH)) {

                        Source->SourceRootPath = SourceRootStringId;
                        Source->Flags &= ~SMI_FLAG_NO_SOURCE_ROOT_PATH;
                    }

                    return(Source);
                }
            }
        }
    }

    //
    // Need to add a new source media descriptor.
    // Allocate the structure and fill it in.
    //
    Source = MyMalloc(sizeof(SOURCE_MEDIA_INFO));
    if(!Source) {
        return(NULL);
    }

    Source->Next = NULL;
    Source->CopyQueue = NULL;
    Source->CopyNodeCount = 0;
    Source->Flags = MediaFlags;

    if(SourceDescription) {
        //
        // Since we already passed this in for a case-insensitive lookup with a writeable
        // buffer, we can add it case-sensitively, because it's already lower-cased.
        //
        Source->Description = pSetupStringTableAddString(Queue->StringTable,
                                                   TempSrcDescString,
                                                   STRTAB_CASE_SENSITIVE | STRTAB_ALREADY_LOWERCASE
                                                  );
        //
        // We also must add the description in its original case, since this is a displayable string.
        // (We're safe in casting away the CONST-ness of this string, since it won't be modified.)
        //
        Source->DescriptionDisplayName = pSetupStringTableAddString(Queue->StringTable,
                                                              (PTSTR)SourceDescription,
                                                              STRTAB_CASE_SENSITIVE
                                                             );

        if((Source->Description == -1) || (Source->DescriptionDisplayName == -1)) {
            MyFree(Source);
            return(NULL);
        }
    } else {
        Source->Description = Source->DescriptionDisplayName = -1;
    }

    if(SourceTagfile) {
        //
        // Again, we already lower-cased this in a writeable buffer above.
        //
        Source->Tagfile = pSetupStringTableAddString(Queue->StringTable,
                                               TempTagfileString,
                                               STRTAB_CASE_SENSITIVE | STRTAB_ALREADY_LOWERCASE
                                              );
        if(Source->Tagfile == -1) {
            MyFree(Source);
            return(NULL);
        }
    } else {
        Source->Tagfile = -1;
    }

    if(SourceCabfile) {
        //
        // Again, we already lower-cased this in a writeable buffer above.
        //
        Source->Cabfile = pSetupStringTableAddString(Queue->StringTable,
                                               TempCabfileString,
                                               STRTAB_CASE_SENSITIVE | STRTAB_ALREADY_LOWERCASE
                                              );
        if(Source->Cabfile == -1) {
            MyFree(Source);
            return(NULL);
        }
    } else {
        Source->Cabfile = Source->Tagfile;
    }

    Source->SourceRootPath = SourceRootStringId;

    //
    // insert our media descriptor into the list of descriptors
    // Note: if the new descriptor has the "service pack" or
    // "local cab driver cache" tag set, then we insert it into
    // the head of the list, otherwise we put it into the end
    // of the list.  This ensures that if the user get's a
    // need media complaint for os binaries, and overrides
    // the source path, then the user will first be prompted for service
    // pack media, then the os media. This saves us from adding lots of
    // code to handle need media overrides in this case, since we would
    // potentially have the os source files first in the media list, which
    // would cause us to install the os media files instead of the service
    // pack media files
    //
    // another potential service pack issue is if we get Tag==Cab entries mixed with Tag!=Cab
    // for exactly the same cab
    // nothing much we can do here, other than ensure that any change where Tag!=Cab
    // is done across the board
    //
    LastSource = NULL;
    for(TempSource=Queue->SourceMediaList; TempSource; LastSource=TempSource,TempSource=LastSource->Next) {
        if ((Source->Flags ^ TempSource->Flags) & (SMI_FLAG_USE_LOCAL_SOURCE_CAB | SMI_FLAG_USE_SVCPACK_SOURCE_ROOT_PATH)) {
            //
            // one is either normal, local source cab, or source root path, and the other is different
            //
            // SMI_FLAG_USE_LOCAL_SOURCE_CAB inserted before anything else
            //
            if(TempSource->Flags & SMI_FLAG_USE_LOCAL_SOURCE_CAB) {
                //
                // ISSUE-2002/04/19-JamieHun Source->Flags can have this bit set
                // ordering needs to be performed with this in mind
                //
                //MYASSERT(!(Source->Flags & SMI_FLAG_USE_LOCAL_SOURCE_CAB));
                continue;
            }
            if(Source->Flags & SMI_FLAG_USE_LOCAL_SOURCE_CAB) {
                break;
            }
            //
            // SMI_FLAG_USE_SVCPACK_SOURCE_ROOT_PATH comes next
            //
            if(TempSource->Flags & SMI_FLAG_USE_SVCPACK_SOURCE_ROOT_PATH) {
                //
                // ISSUE-2002/04/19-JamieHun Source->Flags can have this bit set
                // ordering needs to be performed with this in mind
                //
                //MYASSERT(!(Source->Flags & SMI_FLAG_USE_SVCPACK_SOURCE_ROOT_PATH));
                continue;
            }
            if(Source->Flags & SMI_FLAG_USE_SVCPACK_SOURCE_ROOT_PATH) {
                break;
            }
        }
        //
        // group same tagfiles together (needed because of tag+cab combinations)
        //
        if( LastSource && (Source->Tagfile == LastSource->Tagfile)
                       && (Source->Tagfile != TempSource->Tagfile)) {
            break;
        }
    }
    if (LastSource) {
        //
        // insert after this one
        //
        Source->Next = LastSource->Next;
        LastSource->Next = Source;
    } else {
        //
        // TempSource will either be NULL (no media) or first media (insert before first)
        //
        Source->Next = TempSource;
        Queue->SourceMediaList = Source;
    }

    Queue->SourceMediaCount++;

    QueueNode->SourceMediaInfo = Source;
    return(Source);
}


BOOL
pSetupGetSourceAllInfo(
    IN  HINF                     InfHandle,
    IN  PINFCONTEXT              LayoutLineContext, OPTIONAL
    IN  UINT                     SourceId,
    IN  PSP_ALTPLATFORM_INFO_V2  AltPlatformInfo,   OPTIONAL
    OUT PCTSTR                  *Description,
    OUT PCTSTR                  *Tagfile,
    OUT PCTSTR                  *RelativePath,
    OUT PUINT                    SourceFlags
    )
{
    BOOL b;
    DWORD RequiredSize;
    PTSTR p;
    DWORD ec;
    TCHAR Buffer[MAX_PATH];

    //
    // Get path relative to the source.
    //
    b = pSetupGetSourceInfo(InfHandle,
                            LayoutLineContext,
                            SourceId,
                            AltPlatformInfo,
                            SRCINFO_PATH,
                            NULL,
                            0,
                            &RequiredSize
                           );
    if(!b) {
        ec = GetLastError();
        goto clean0;
    }

    p = MyMalloc(RequiredSize*sizeof(TCHAR));
    if(!p) {
        ec = ERROR_NOT_ENOUGH_MEMORY;
        goto clean0;
    }
    pSetupGetSourceInfo(InfHandle,
                        LayoutLineContext,
                        SourceId,
                        AltPlatformInfo,
                        SRCINFO_PATH,
                        p,
                        RequiredSize,
                        NULL
                       );
    *RelativePath = p;

    //
    // Get description.
    //
    b = pSetupGetSourceInfo(InfHandle,
                            LayoutLineContext,
                            SourceId,
                            AltPlatformInfo,
                            SRCINFO_DESCRIPTION,
                            NULL,
                            0,
                            &RequiredSize
                           );
    if(!b) {
        ec = GetLastError();
        goto clean1;
    }

    p = MyMalloc(RequiredSize*sizeof(TCHAR));
    if(!p) {
        ec =  ERROR_NOT_ENOUGH_MEMORY;
        goto clean1;
    }
    pSetupGetSourceInfo(InfHandle,
                        LayoutLineContext,
                        SourceId,
                        AltPlatformInfo,
                        SRCINFO_DESCRIPTION,
                        p,
                        RequiredSize,
                        NULL
                       );
    *Description = p;

    //
    // Get tagfile, if any.
    //
    b = pSetupGetSourceInfo(InfHandle,
                            LayoutLineContext,
                            SourceId,
                            AltPlatformInfo,
                            SRCINFO_TAGFILE,
                            NULL,
                            0,
                            &RequiredSize
                           );
    if(!b) {
        ec = GetLastError();
        goto clean2;
    }

    p = MyMalloc(RequiredSize*sizeof(TCHAR));
    if(!p) {
        ec =  ERROR_NOT_ENOUGH_MEMORY;
        goto clean2;
    }
    pSetupGetSourceInfo(InfHandle,
                        LayoutLineContext,
                        SourceId,
                        AltPlatformInfo,
                        SRCINFO_TAGFILE,
                        p,
                        RequiredSize,
                        NULL
                       );
    if(*p) {
        *Tagfile = p;
    } else {
        MyFree(p);
        *Tagfile = NULL;
    }

    //
    // Get flags, if any.
    //
    b = pSetupGetSourceInfo(InfHandle,
                            LayoutLineContext,
                            SourceId,
                            AltPlatformInfo,
                            SRCINFO_FLAGS,
                            Buffer,
                            SIZECHARS(Buffer),
                            NULL
                           );
    if(!b) {
        ec = GetLastError();
        goto clean3;
    }

    pAToI( Buffer, SourceFlags );


    return(TRUE);

clean3:
    MyFree(*Tagfile);
clean2:
    MyFree(*Description);
clean1:
    MyFree(*RelativePath);
clean0:
    SetLastError(ec);
    return(FALSE);
}


BOOL
pIsFileInDriverCache(
    IN  HINF   CabInf,
    IN  PCTSTR TargetFilename,
    IN  PCTSTR SubDirectory,
    OUT PCTSTR *CacheName
    )
{
    INFCONTEXT Context,SectionContext;
    PCTSTR      SectionName,CabName;
    TCHAR      TempBuffer[MAX_PATH];

    UINT Field, FieldCount;

    MYASSERT(CabInf != INVALID_HANDLE_VALUE);
    MYASSERT(TargetFilename);
    MYASSERT(CacheName);

    if (!SetupFindFirstLine(CabInf, TEXT("Version"), TEXT("CabFiles"), &SectionContext)) {
        return(FALSE);
    }

    do  {


        FieldCount = SetupGetFieldCount(&SectionContext);
        for(Field=1; Field<=FieldCount; Field++) {

            SectionName = pSetupGetField(&SectionContext,Field);

            if (SetupFindFirstLine(CabInf,SectionName,TargetFilename,&Context)) {
                //
                // we found a match
                //
                if (SetupFindFirstLine(CabInf,TEXT("Cabs"),SectionName,&Context)) {
                    CabName= pSetupGetField(&Context,1);
                    //if (pIsDriverCachePresent(CabName,SubDirectory,TempBuffer)) {
                        *CacheName = DuplicateString( CabName );
                        if (*CacheName) {
                            return(TRUE);
                        }
                    //}
                }
            }
        } // end for

    } while (SetupFindNextMatchLine(&SectionContext,TEXT("CabFiles"),&SectionContext));

    return(FALSE);

}

BOOL
pIsDriverCachePresent(
    IN     PCTSTR DriverName,
    IN     PCTSTR Subdirectory,
    IN OUT PTSTR DriverBuffer
    )
/*++

Routine Description:

    Looks at the proper location for the driver cache cab-file, and if it's
    present, return TRUE.  If present, it returns the partial path to the
    cab file

Arguments:

    DriveName    - the cab file we're looking for

    Subdirectory - if specified, use this as the subdirectory from the root of the driver
                   cache, otherwise use the specified architecture's subdirectory

    DriverBuffer - if the cab file is present, return the source root to the cab file


Return Value:

    TRUE if the cab file is present

--*/

{

    TCHAR TempBuffer[MAX_PATH];

    if (!DriverCacheSourcePath || !DriverName) {
        return FALSE;
    }

    if (!Subdirectory) {
        Subdirectory =
#if   defined(_AXP64_)
         TEXT("axp64");
#elif defined(_ALPHA_)
         TEXT("alpha");
#elif defined(_X86_)
         IsNEC98() ? TEXT("nec98") : TEXT("i386");
#elif defined(_IA64_)
         TEXT("ia64");
#elif defined(_AMD64_)
         TEXT("amd64");
#endif
    }

    lstrcpy(TempBuffer, DriverCacheSourcePath);
    pSetupConcatenatePaths(TempBuffer, Subdirectory , MAX_PATH, NULL);
    pSetupConcatenatePaths(TempBuffer, DriverName, MAX_PATH, NULL);

    if (FileExists(TempBuffer,NULL)) {
        lstrcpy(DriverBuffer,DriverCacheSourcePath);
        return(TRUE);
    }

    return(FALSE);

}
