/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    version.c

Abstract:

    Implements calls to version APIs.

Author:

    Calin Negreanu (calinn)  20-Jan-1999

Revision History:

    <alias> <date> <comments>

--*/

#include "utils.h"
#include "version.h"

static PCTSTR g_DefaultTranslations[] = {
    TEXT("04090000"),
    TEXT("040904E4"),
    TEXT("040904B0"),
    NULL
};

BOOL
ShCreateVersionStruct (
    OUT     PVERSION_STRUCT VersionStruct,
    IN      PCTSTR FileSpec
    )

/*++

Routine Description:

  ShCreateVersionStruct is called to load a version structure from a file
  and to obtain the fixed version stamp info that is language-independent.

  The caller must call ShDestroyVersionStruct after the VersionStruct is no
  longer needed.

Arguments:

  VersionStruct - Receives the version stamp info to be used by other
                  functions in this module

  FileSpec - Specifies the file to obtain version info from

Return Value:

  TRUE if the routine was able to get version info, or FALSE if an
  error occurred.

--*/

{
    ZeroMemory (VersionStruct, sizeof (VERSION_STRUCT));
    VersionStruct->FileSpec = FileSpec;

    //
    // Allocate enough memory for the version stamp
    //

    VersionStruct->Size = GetFileVersionInfoSize (
                                (PTSTR) FileSpec,
                                &VersionStruct->Handle
                                );
    if (!VersionStruct->Size) {
        return FALSE;
    }

    VersionStruct->VersionBuffer = HeapAlloc (GetProcessHeap (), 0, VersionStruct->Size);

    if (!VersionStruct->VersionBuffer) {
        return FALSE;
    }

    VersionStruct->StringBuffer = HeapAlloc (GetProcessHeap (), 0, VersionStruct->Size);

    if (!VersionStruct->StringBuffer) {
        return FALSE;
    }


    //
    // Now get the version info from the file
    //

    if (!GetFileVersionInfo (
             (PTSTR) FileSpec,
             VersionStruct->Handle,
             VersionStruct->Size,
             VersionStruct->VersionBuffer
             )) {
        ShDestroyVersionStruct (VersionStruct);
        return FALSE;
    }

    //
    // Extract the fixed info
    //

    VerQueryValue (
        VersionStruct->VersionBuffer,
        TEXT("\\"),
        &VersionStruct->FixedInfo,
        &VersionStruct->FixedInfoSize
        );

    return TRUE;
}


VOID
ShDestroyVersionStruct (
    IN      PVERSION_STRUCT VersionStruct
    )

/*++

Routine Description:

  ShDestroyVersionStruct cleans up all memory allocated by the routines
  in this module.

Arguments:

  VersionStruct - Specifies the structure to clean up

Return Value:

  none

--*/

{
    if (VersionStruct->VersionBuffer) {
        HeapFree (GetProcessHeap (), 0, VersionStruct->VersionBuffer);
    }
    if (VersionStruct->StringBuffer) {
        HeapFree (GetProcessHeap (), 0, VersionStruct->StringBuffer);
    }

    ZeroMemory (VersionStruct, sizeof (VERSION_STRUCT));
}


ULONGLONG
ShVerGetFileVer (
    IN      PVERSION_STRUCT VersionStruct
    )
{
    ULONGLONG result = 0;
    if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
        *((PDWORD) (&result)) = VersionStruct->FixedInfo->dwFileVersionLS;
        *(((PDWORD) (&result)) + 1) = VersionStruct->FixedInfo->dwFileVersionMS;
    }
    return result;
}

ULONGLONG
ShVerGetProductVer (
    IN      PVERSION_STRUCT VersionStruct
    )
{
    ULONGLONG result = 0;
    if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
        *((PDWORD) (&result)) = VersionStruct->FixedInfo->dwProductVersionLS;
        *(((PDWORD) (&result)) + 1) = VersionStruct->FixedInfo->dwProductVersionMS;
    }
    return result;
}

DWORD
ShVerGetFileDateLo (
    IN      PVERSION_STRUCT VersionStruct
    )
{
    if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
        return VersionStruct->FixedInfo->dwFileDateLS;
    }
    return 0;
}

DWORD
ShVerGetFileDateHi (
    IN      PVERSION_STRUCT VersionStruct
    )
{
    if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
        return VersionStruct->FixedInfo->dwFileDateMS;
    }
    return 0;
}

DWORD
ShVerGetFileVerOs (
    IN      PVERSION_STRUCT VersionStruct
    )
{
    if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
        return VersionStruct->FixedInfo->dwFileOS;
    }
    return 0;
}

DWORD
ShVerGetFileVerType (
    IN      PVERSION_STRUCT VersionStruct
    )
{
    if (VersionStruct->FixedInfoSize >= sizeof (VS_FIXEDFILEINFO)) {
        return VersionStruct->FixedInfo->dwFileType;
    }
    return 0;
}

PCTSTR
pShEnumVersionValueCommon (
    IN OUT  PVERSION_STRUCT VersionStruct
    );

PCTSTR
pShEnumNextVersionTranslation (
    IN OUT  PVERSION_STRUCT VersionStruct
    );

PCTSTR
pShEnumFirstVersionTranslation (
    IN OUT  PVERSION_STRUCT VersionStruct
    )
{
    UINT ArraySize;

    if (!VerQueryValue (
            VersionStruct->VersionBuffer,
            TEXT("\\VarFileInfo\\Translation"),
            &VersionStruct->Translations,
            &ArraySize
            )) {
        //
        // No translations are available
        //

        ArraySize = 0;
    }

    //
    // Return a pointer to the first translation
    //

    VersionStruct->CurrentDefaultTranslation = 0;
    VersionStruct->MaxTranslations = ArraySize / sizeof (TRANSLATION);
    VersionStruct->CurrentTranslation = 0;

    return pShEnumNextVersionTranslation (VersionStruct);
}

BOOL
pShIsDefaultTranslation (
    IN      PCTSTR TranslationStr
    )
{
    INT i;

    for (i = 0 ; g_DefaultTranslations[i] ; i++) {
        if (lstrcmpi (TranslationStr, g_DefaultTranslations[i]) == 0) {
            return TRUE;
        }
    }
    return FALSE;
}

PCTSTR
pShEnumNextVersionTranslation (
    IN OUT  PVERSION_STRUCT VersionStruct
    )
{
    PTRANSLATION Translation;

    if (g_DefaultTranslations[VersionStruct->CurrentDefaultTranslation]) {

        lstrcpy (VersionStruct->TranslationStr, g_DefaultTranslations[VersionStruct->CurrentDefaultTranslation]);
        VersionStruct->CurrentDefaultTranslation++;

    } else {

        do {
            if (VersionStruct->CurrentTranslation == VersionStruct->MaxTranslations) {
                return NULL;
            }

            Translation = &VersionStruct->Translations[VersionStruct->CurrentTranslation];

            wsprintf (
                VersionStruct->TranslationStr,
                TEXT("%04x%04x"),
                Translation->CodePage,
                Translation->Language
                );

            VersionStruct->CurrentTranslation++;

        } while (pShIsDefaultTranslation (VersionStruct->TranslationStr));
    }

    return VersionStruct->TranslationStr;
}


PCTSTR
pShEnumNextVersionValue (
    IN OUT  PVERSION_STRUCT VersionStruct
    )
{
    PCTSTR rc = NULL;

    do {
        if (!pShEnumNextVersionTranslation (VersionStruct)) {
            break;
        }

        rc = pShEnumVersionValueCommon (VersionStruct);

    } while (!rc);

    return rc;
}

PCTSTR
pShEnumFirstVersionValue (
    IN OUT  PVERSION_STRUCT VersionStruct,
    IN      PCTSTR VersionField
    )
{
    PCTSTR rc;

    if (!pShEnumFirstVersionTranslation (VersionStruct)) {
        return NULL;
    }

    VersionStruct->VersionField = VersionField;

    rc = pShEnumVersionValueCommon (VersionStruct);

    if (!rc) {
        rc = pShEnumNextVersionValue (VersionStruct);
    }

    return rc;
}

PCTSTR
pShEnumVersionValueCommon (
    IN OUT  PVERSION_STRUCT VersionStruct
    )
{
    PTSTR Text;
    UINT StringLen;
    PBYTE String;
    PCTSTR Result = NULL;

    //
    // Prepare sub block for VerQueryValue API
    //
    Text = HeapAlloc (GetProcessHeap (), 0, (18 + lstrlen (VersionStruct->TranslationStr) + lstrlen (VersionStruct->VersionField)) * sizeof (TCHAR));

    if (!Text) {
        return NULL;
    }

    wsprintf (
        Text,
        TEXT("\\StringFileInfo\\%s\\%s"),
        VersionStruct->TranslationStr,
        VersionStruct->VersionField
        );

    __try {
        //
        // Get the value from the version stamp
        //

        if (!VerQueryValue (
                VersionStruct->VersionBuffer,
                Text,
                &String,
                &StringLen
                )) {
            //
            // No value is available
            //

            return NULL;
        }
        CopyMemory (VersionStruct->StringBuffer, String, StringLen * sizeof (TCHAR));
        VersionStruct->StringBuffer [StringLen * sizeof (TCHAR)] = 0;
        Result = (PTSTR) VersionStruct->StringBuffer;

    }
    __finally {
        HeapFree (GetProcessHeap (), 0, Text);
    }

    return Result;
}

BOOL
ShGlobalVersionCheck (
    IN      PVERSION_STRUCT VersionData,
    IN      PCTSTR NameToCheck,
    IN      PCTSTR ValueToCheck
    )
{
    PCTSTR CurrentStr;
    BOOL result = FALSE;

    CurrentStr = pShEnumFirstVersionValue (VersionData, NameToCheck);
    while (CurrentStr) {
        if (ShIsPatternMatch (ValueToCheck, CurrentStr)) {
            result = TRUE;
            break;
        }
        CurrentStr = pShEnumNextVersionValue (VersionData);
    }
    return result;
}