/*++

Copyright (c) 1995-2000 Microsoft Corporation

Module Name:

    infsdisk.c

Abstract:

    Externally exposed INF routines for source disk descriptor manipulation.

Author:

    Ted Miller (tedm) 9-Feb-1995

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop

//
// Locations of various fields in the [SourceDisksNames] section
// of an inf
//
#define DISKNAMESECT_DESCRIPTION    1
#define DISKNAMESECT_TAGFILE        2       // cabinet name in win95
#define DISKNAMESECT_OEM            3       // unused, indicates oem disk in win95
#define DISKNAMESECT_PATH           4
#define DISKNAMESECT_FLAGS          5       // indicates extra tags
#define DISKNAMESECT_TAGFILE2       6       // real tagfile if DISKNAMESECT_TAGFILE is really a cabfile

#ifdef UNICODE
//
// ANSI version
//
BOOL
SetupGetSourceInfoA(
    IN  HINF   InfHandle,
    IN  UINT   SourceId,
    IN  UINT   InfoDesired,
    OUT PSTR   ReturnBuffer,     OPTIONAL
    IN  DWORD  ReturnBufferSize,
    OUT PDWORD RequiredSize      OPTIONAL
    )
{
    DWORD rc;
    BOOL b;
    PWCHAR buffer;
    DWORD requiredsize;
    PCSTR ansi;

    buffer = MyMalloc(MAX_INF_STRING_LENGTH);
    if (buffer) {

        b = pSetupGetSourceInfo(
                InfHandle,
                NULL,
                SourceId,
                NULL,
                InfoDesired,
                buffer,
                MAX_INF_STRING_LENGTH,
                &requiredsize
                );

        rc = GetLastError();

        if(b) {

            rc = NO_ERROR;

            if(ansi = pSetupUnicodeToAnsi(buffer)) {

                requiredsize = lstrlenA(ansi)+1;

                if(RequiredSize) {
                    try {
                        *RequiredSize = requiredsize;
                    } except(EXCEPTION_EXECUTE_HANDLER) {
                        rc = ERROR_INVALID_PARAMETER;
                        b = FALSE;
                    }
                }

                if((rc == NO_ERROR) && ReturnBuffer) {

                    if(!lstrcpynA(ReturnBuffer,ansi,ReturnBufferSize)) {
                        //
                        // ReturnBuffer invalid
                        //
                        rc = ERROR_INVALID_PARAMETER;
                        b = FALSE;
                    }
                }

                MyFree(ansi);
            } else {
                rc = ERROR_NOT_ENOUGH_MEMORY;
                b = FALSE;
            }
        }

        MyFree(buffer);
    } else {
        rc = ERROR_NOT_ENOUGH_MEMORY;
        b = FALSE;
    }

    SetLastError(rc);
    return(b);
}
#else
//
// Unicode stub
//
BOOL
SetupGetSourceInfoW(
    IN  HINF   InfHandle,
    IN  UINT   SourceId,
    IN  UINT   InfoDesired,
    OUT PWSTR  ReturnBuffer,     OPTIONAL
    IN  DWORD  ReturnBufferSize,
    OUT PDWORD RequiredSize      OPTIONAL
    )
{
    UNREFERENCED_PARAMETER(InfHandle);
    UNREFERENCED_PARAMETER(SourceId);
    UNREFERENCED_PARAMETER(InfoDesired);
    UNREFERENCED_PARAMETER(ReturnBuffer);
    UNREFERENCED_PARAMETER(ReturnBufferSize);
    UNREFERENCED_PARAMETER(RequiredSize);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return(FALSE);
}
#endif

BOOL
SetupGetSourceInfo(
    IN  HINF   InfHandle,
    IN  UINT   SourceId,
    IN  UINT   InfoDesired,
    OUT PTSTR  ReturnBuffer,     OPTIONAL
    IN  DWORD  ReturnBufferSize,
    OUT PDWORD RequiredSize      OPTIONAL
    )
//
// Native version
//
{
    return pSetupGetSourceInfo(InfHandle,
                               NULL,
                               SourceId,
                               NULL,
                               InfoDesired,
                               ReturnBuffer,
                               ReturnBufferSize,
                               RequiredSize
                              );
}

BOOL
pSetupGetSourceInfo(
    IN  HINF                    InfHandle,         OPTIONAL
    IN  PINFCONTEXT             LayoutLineContext, OPTIONAL
    IN  UINT                    SourceId,
    IN  PSP_ALTPLATFORM_INFO_V2 AltPlatformInfo,   OPTIONAL
    IN  UINT                    InfoDesired,
    OUT PTSTR                   ReturnBuffer,      OPTIONAL
    IN  DWORD                   ReturnBufferSize,
    OUT PDWORD                  RequiredSize       OPTIONAL
    )
/*++

Routine Description:

    Get information from SourceDisksNames

    If InfHandle specified instead of LayoutLineContext
    and the ID is specified in more than one INF
    then the wrong information *MAY* be returned.
    This effects callers of SetupGetSourceInfo
    we need a SetupGetSourceInfoEx post 5.0

Arguments:

    InfHandle - required if LayoutLineContext is not provided, else specifies a layout inf

    SourceId  - numerical source ID, used as search key in SourceDisksNames section

    AltPlatformInfo - optionally, supplies alternate platform information used
        in decorating the [SourceDisksNames] section.

    InfoDesired -
        SRCINFO_PATH
        SRCINFO_TAGFILE
        SRCINFO_DESCRIPTION
        SRCINFO_FLAGS

    ReturnBuffer - buffer for returned string
    ReturnBufferSize - size of buffer
    RequiredSize - size buffer needs to be if ReturnBufferSize too small
    LayoutLineContext - if specified, used to determine correct INF to use if SourceID's conflict

Return Value:

    Boolean value indicating outcome. If FALSE, GetLastError() returns
    extended error information.
    ReturnBuffer filled out with string
    RequiredSize filled out with required size of buffer to hold string

--*/
{
    PCTSTR PlatformName;
    UINT ValueIndex;
    BOOL Mandatory;
    BOOL IsPath;
    INFCONTEXT InfContext;
    INFCONTEXT SelectedInfContext;
    int SelectedRank;
    TCHAR SourceIdString[24];
    PCTSTR Value;
    BOOL b;
    UINT Length;
    TCHAR MediaListSectionName[64];
    HINF hInfPreferred = (HINF)(-1);

    try {
        if ((LayoutLineContext != NULL) && (LayoutLineContext != (PINFCONTEXT)(-1))) {
            hInfPreferred = (HINF)LayoutLineContext->CurrentInf;
        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
        hInfPreferred = (HINF)(-1);
    }

    //
    // Determine the index of the value that gives the caller the info he wants.
    //
    switch(InfoDesired) {

    case SRCINFO_PATH:
        ValueIndex = DISKNAMESECT_PATH;
        Mandatory = FALSE;
        IsPath = TRUE;
        break;

    case SRCINFO_TAGFILE:
        ValueIndex = DISKNAMESECT_TAGFILE;
        Mandatory = FALSE;
        IsPath = TRUE;
        break;

    case SRCINFO_DESCRIPTION:
        ValueIndex = DISKNAMESECT_DESCRIPTION;
        Mandatory = TRUE;
        IsPath = FALSE;
        break;

    case SRCINFO_FLAGS:
        ValueIndex = DISKNAMESECT_FLAGS;
        Mandatory = FALSE;
        IsPath = FALSE;
        break;

    case SRCINFO_TAGFILE2:
        ValueIndex = DISKNAMESECT_TAGFILE2;
        Mandatory = FALSE;
        IsPath = TRUE;
        break;

    default:
        SetLastError(ERROR_INVALID_PARAMETER);
        return(FALSE);
    }

    wsprintf(SourceIdString,TEXT("%d"),SourceId);

    if(AltPlatformInfo) {

        switch(AltPlatformInfo->ProcessorArchitecture) {

            case PROCESSOR_ARCHITECTURE_INTEL :
                PlatformName = pszX86SrcDiskSuffix;
                break;

            case PROCESSOR_ARCHITECTURE_ALPHA :
                PlatformName = pszAlphaSrcDiskSuffix;
                break;

            case PROCESSOR_ARCHITECTURE_IA64 :
                PlatformName = pszIa64SrcDiskSuffix;
                break;

            case PROCESSOR_ARCHITECTURE_ALPHA64 :
                PlatformName = pszAxp64SrcDiskSuffix;
                break;

            case PROCESSOR_ARCHITECTURE_AMD64 :
                PlatformName = pszAmd64SrcDiskSuffix;
                break;

            default :
                //
                // unknown/unsupported processor architecture.
                //
                MYASSERT((AltPlatformInfo->ProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) ||
                         (AltPlatformInfo->ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ALPHA) ||
                         (AltPlatformInfo->ProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)  ||
                         (AltPlatformInfo->ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ALPHA64) ||
                         (AltPlatformInfo->ProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
                        );

                SetLastError(ERROR_INVALID_PARAMETER);
                return(FALSE);
        }

    } else {
        PlatformName = pszPlatformSrcDiskSuffix;
    }

    _sntprintf(
        MediaListSectionName,
        sizeof(MediaListSectionName)/sizeof(MediaListSectionName[0]),
        TEXT("%s.%s"),
        pszSourceDisksNames,
        PlatformName
        );

    //
    // we will prefer
    // (1) an entry in hInfPreferred           (Rank 11/12 decorated over undecorated)
    // (2) an entry linked to hInfPreferred    (Rank 21/22 decorated over undecorated)
    // (3) an entry in hInfHandle              (Rank 31/32 decorated over undecorated)
    // (4) an entry linked to InfHandle        (Rank 41/42 decorated over undecorated)
    //

    SelectedRank = 100;       // 11-42 as above

    if ((hInfPreferred != NULL) && (hInfPreferred != (HINF)(-1))) {
        //
        // see if we can find the SourceIdString in the INF that we found the section in
        //
        // rank 11 or 21 (decorated) - always try
        //
        if(SetupFindFirstLine(hInfPreferred,MediaListSectionName,SourceIdString,&InfContext)) {
            if (InfContext.Inf == InfContext.CurrentInf) {
                SelectedRank = 11;
                SelectedInfContext = InfContext;
            } else {
                SelectedRank = 21;
                SelectedInfContext = InfContext;
            }
        }
        if (SelectedRank > 12) {
            //
            // rank 12 or 22 (undecorated) only try if we haven't got anything better than 12
            //
            if(SetupFindFirstLine(hInfPreferred,pszSourceDisksNames,SourceIdString,&InfContext)) {
                if (InfContext.Inf == InfContext.CurrentInf) {
                    SelectedRank = 12;
                    SelectedInfContext = InfContext;
                } else if (SelectedRank > 22) {
                    SelectedRank = 22;
                    SelectedInfContext = InfContext;
                }
            }
        }
    }
    if ((InfHandle != NULL) && (InfHandle != (HINF)(-1)) && (SelectedRank > 31)) {
        //
        // see if we can find the SourceIdString in the supplied INF
        //
        // rank 31 or 41 (decorated) - only try if we haven't got anything better than 31
        //
        if(SetupFindFirstLine(InfHandle,MediaListSectionName,SourceIdString,&InfContext)) {
            if (InfContext.Inf == InfContext.CurrentInf) {
                SelectedRank = 31;
                SelectedInfContext = InfContext;
            } else if (SelectedRank > 41) {
                SelectedRank = 41;
                SelectedInfContext = InfContext;
            }
        }
        if (SelectedRank > 32) {
            //
            // rank 32 or 42 (undecorated) - only try if we haven't got anything better than 32
            //
            if(SetupFindFirstLine(InfHandle,pszSourceDisksNames,SourceIdString,&InfContext)) {
                if (InfContext.Inf == InfContext.CurrentInf) {
                    SelectedRank = 32;
                    SelectedInfContext = InfContext;
                } else if (SelectedRank > 42) {
                    SelectedRank = 42;
                    SelectedInfContext = InfContext;
                }
            }
        }
    }
    if(SelectedRank == 100 || (Value = pSetupGetField(&InfContext,ValueIndex))==NULL) {
        if(Mandatory) {
            SetLastError(ERROR_LINE_NOT_FOUND);
            return(FALSE);
        } else {
            Value = TEXT("");
        }
    }

    //
    // Figure out how many characters are in the output.
    // If the value is a path type value we want to remove
    // the trailing backslash if there is one.
    //
    Length = lstrlen(Value);
    if(IsPath && Length && (*CharPrev(Value,Value+Length) == TEXT('\\'))) {
        Length--;
    }

    //
    // Need to leave space for the trailing nul.
    //
    Length++;
    if(RequiredSize) {
        b = TRUE;
        try {
            *RequiredSize = Length;
        } except(EXCEPTION_EXECUTE_HANDLER) {
            b = FALSE;
        }
        if(!b) {
            SetLastError(ERROR_INVALID_PARAMETER);
            return(FALSE);
        }
    }

    b = TRUE;
    if(ReturnBuffer) {
        if(Length <= ReturnBufferSize) {
            //
            // lstrcpyn is a strange API but the below is correct --
            // the size parameter is actually the capacity of the
            // target buffer. So to get it to put the nul in the
            // right place we pass one larger than the number of chars
            // we want copied.
            //
            if(!lstrcpyn(ReturnBuffer,Value,Length)) {
                //
                // ReturnBuffer invalid
                //
                b = FALSE;
                SetLastError(ERROR_INVALID_PARAMETER);
            }
        } else {
            b = FALSE;
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
        }
    }

    return(b);
}
