//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1993
//
// File: ole2dup.c
//
//  This file contains all the duplicated code from OLE 2.0 DLLs to avoid
// any link to their DLLs from the shell. If we decided to have links to
// them, we need to delete these files.
//
// History:
//  04-16-97 AndyP      moved parts to shlwapi (from shell32)
//  12-29-92 SatoNa     Created.
//
//---------------------------------------------------------------------------

#include "priv.h"

//
// SHStringFromGUIDA
//
// converts GUID into (...) form without leading identifier; returns
// amount of data copied to lpsz if successful; 0 if buffer too small.
//

// An endian-dependant map of what bytes go where in the GUID
// text representation.
//
// Do NOT use the TEXT() macro in GuidMap... they're intended to be bytes
//

static const BYTE c_rgbGuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
                                     8, 9, '-', 10, 11, 12, 13, 14, 15 };

static const CHAR c_szDigitsA[] = "0123456789ABCDEF";
static const WCHAR c_szDigitsW[] = TEXTW("0123456789ABCDEF");

STDAPI_(int) 
SHStringFromGUIDA(
    UNALIGNED REFGUID rguid, 
    LPSTR   psz, 
    int     cchMax)
{
    int i;
    const BYTE * pBytes = (const BYTE *) rguid;

    if (cchMax < GUIDSTR_MAX)
        return 0;

#ifdef BIG_ENDIAN
    // This is the slow, but portable version
    wnsprintf(psz, cchMax,"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
            rguid->Data1, rguid->Data2, rguid->Data3,
            rguid->Data4[0], rguid->Data4[1],
            rguid->Data4[2], rguid->Data4[3],
            rguid->Data4[4], rguid->Data4[5],
            rguid->Data4[6], rguid->Data4[7]);
#else
    // The following algorithm is faster than the wsprintf.
    *psz++ = '{';

    for (i = 0; i < SIZEOF(c_rgbGuidMap); i++)
    {
        if (c_rgbGuidMap[i] == '-')      // don't TEXT() this line
        {
            *psz++ = '-';
        }
        else
        {
            // Convert a byte-value into a character representation
            *psz++ = c_szDigitsA[ (pBytes[c_rgbGuidMap[i]] & 0xF0) >> 4 ];
            *psz++ = c_szDigitsA[ (pBytes[c_rgbGuidMap[i]] & 0x0F) ];
        }
    }
    *psz++ = '}';
    *psz   = '\0';
#endif /* !BIG_ENDIAN */

    return GUIDSTR_MAX;
}


STDAPI_(int) 
SHStringFromGUIDW(
    UNALIGNED REFGUID rguid, 
    LPWSTR  psz, 
    int     cchMax)
{
    int i;
    const BYTE * pBytes = (const BYTE *) rguid;

    if (cchMax < GUIDSTR_MAX)
        return 0;

#ifdef BIG_ENDIAN
    // This is the slow, but portable version
    wnsprintfW(psz, cchMax, L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
            rguid->Data1, rguid->Data2, rguid->Data3,
            rguid->Data4[0], rguid->Data4[1],
            rguid->Data4[2], rguid->Data4[3],
            rguid->Data4[4], rguid->Data4[5],
            rguid->Data4[6], rguid->Data4[7]);
#else
    // The following algorithm is faster than the wsprintf.
    *psz++ = TEXTW('{');

    for (i = 0; i < SIZEOF(c_rgbGuidMap); i++)
    {
        if (c_rgbGuidMap[i] == '-')      // don't TEXT() this line
        {
            *psz++ = TEXTW('-');
        }
        else
        {
            // Convert a byte-value into a character representation
            *psz++ = c_szDigitsW[ (pBytes[c_rgbGuidMap[i]] & 0xF0) >> 4 ];
            *psz++ = c_szDigitsW[ (pBytes[c_rgbGuidMap[i]] & 0x0F) ];
        }
    }
    *psz++ = TEXTW('}');
    *psz   = TEXTW('\0');
#endif /* !BIG_ENDIAN */

    return GUIDSTR_MAX;
}

// this makes sure the DLL for the given clsid stays in memory
// this is needed because we violate COM rules and hold apparment objects
// across the lifetime of appartment threads. these objects really need
// to be free threaded (we have always treated them as such)
//
//  Look in the registry and pull out the name of the DLL who owns
//  the CLSID.  We must pull the DLL name as unicode in case the
//  DLL name contains unicode characters.
//
STDAPI_(HINSTANCE) SHPinDllOfCLSID(const CLSID *pclsid)
{
    HKEY hk;
    DWORD dwSize;
    HINSTANCE hinst = NULL;
    TCHAR szClass[GUIDSTR_MAX + 64];    // CLSID\{...}\InProcServer32
    WCHAR szDllPath[MAX_PATH];

    lstrcpy(szClass, TEXT("CLSID\\"));
    SHStringFromGUID(pclsid, szClass+6, GUIDSTR_MAX); // 6 = strlen("CLSID\\")
    lstrcat(szClass, TEXT("\\InProcServer32"));

    if (RegOpenKeyEx(HKEY_CLASSES_ROOT, szClass, 0, KEY_QUERY_VALUE, &hk)
                == ERROR_SUCCESS) {

        // Explicitly read as unicode.  SHQueryValueEx handles REG_EXPAND_SZ
        dwSize = SIZEOF(szDllPath);
        if (SHQueryValueExW(hk, 0, 0, 0, szDllPath, &dwSize) == ERROR_SUCCESS) {
            hinst = LoadLibraryExWrapW(szDllPath, NULL, 0);
        }

        RegCloseKey(hk);
    }

    return hinst;
}

// scan psz for a number of hex digits (at most 8); update psz, return
// value in Value; check for chDelim; return TRUE for success.
BOOL HexStringToDword(LPCTSTR * ppsz, DWORD * lpValue, int cDigits, TCHAR chDelim)
{
    int ich;
    LPCTSTR psz = *ppsz;
    DWORD Value = 0;
    BOOL fRet = TRUE;

    for (ich = 0; ich < cDigits; ich++)
    {
        TCHAR ch = psz[ich];
        if (InRange(ch, TEXT('0'), TEXT('9')))
        {
            Value = (Value << 4) + ch - TEXT('0');
        }
        else if ( InRange( (ch |= (TEXT('a')-TEXT('A'))), TEXT('a'), TEXT('f')) )
        {
            Value = (Value << 4) + ch - TEXT('a') + 10;
        }
        else
            return(FALSE);
    }

    if (chDelim)
    {
        fRet = (psz[ich++] == chDelim);
    }

    *lpValue = Value;
    *ppsz = psz+ich;

    return fRet;
}

// parse above format; return TRUE if succesful; always writes over *pguid.
STDAPI_(BOOL) GUIDFromString(LPCTSTR psz, GUID *pguid)
{
    DWORD dw;
    if (*psz++ != TEXT('{') /*}*/ )
        return FALSE;

    if (!HexStringToDword(&psz, &pguid->Data1, SIZEOF(DWORD)*2, TEXT('-')))
        return FALSE;

    if (!HexStringToDword(&psz, &dw, SIZEOF(WORD)*2, TEXT('-')))
        return FALSE;

    pguid->Data2 = (WORD)dw;

    if (!HexStringToDword(&psz, &dw, SIZEOF(WORD)*2, TEXT('-')))
        return FALSE;

    pguid->Data3 = (WORD)dw;

    if (!HexStringToDword(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[0] = (BYTE)dw;

    if (!HexStringToDword(&psz, &dw, SIZEOF(BYTE)*2, TEXT('-')))
        return FALSE;

    pguid->Data4[1] = (BYTE)dw;

    if (!HexStringToDword(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[2] = (BYTE)dw;

    if (!HexStringToDword(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[3] = (BYTE)dw;

    if (!HexStringToDword(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[4] = (BYTE)dw;

    if (!HexStringToDword(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[5] = (BYTE)dw;

    if (!HexStringToDword(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[6] = (BYTE)dw;
    if (!HexStringToDword(&psz, &dw, SIZEOF(BYTE)*2, /*(*/ TEXT('}')))
        return FALSE;

    pguid->Data4[7] = (BYTE)dw;

    return TRUE;
}

#ifdef UNICODE

LWSTDAPI_(BOOL) GUIDFromStringA(LPCSTR psz, GUID *pguid)
{
    TCHAR sz[GUIDSTR_MAX];

    SHAnsiToTChar(psz, sz, SIZECHARS(sz));
    return GUIDFromString(sz, pguid);
}

#else

LWSTDAPI_(BOOL) GUIDFromStringW(LPCWSTR psz, GUID *pguid)
{
    TCHAR sz[GUIDSTR_MAX];

    SHUnicodeToAnsi(psz, sz, SIZECHARS(sz));
    return GUIDFromString(sz, pguid);
}

#endif // UNICODE
