//+============================================================================
//
//  File:   RtlStub.cxx
//
//  Purpose:
//          This file provides some RTL routines which are also implemented
//          in NTDLL.  They are duplicated here so that we can build
//          PropTest without linking to NTDLL, which doesn't exist
//          on Win95.
//
//+============================================================================



#include "pch.cxx"          // Brings in most other includes/defines/etc.

#define BSTRLEN(bstrVal)      *((ULONG *) bstrVal - 1)

// we use static array instead of string literals because some systems
// have 4 bytes string literals, and would not produce the correct result
// for REF's 2 byte Unicode convention
// 
OLECHAR aocMap[CCH_MAP + 1] = {'a','b','c','d','e','f','g',
                               'h','i','j','k','l','m','n',
                               'o','p','q','r','s','t','u',
                               'v','w','x','y','z',
                               '0','1','2','3','4','5','\0'};

GUID guidSummary =
    { 0xf29f85e0,
      0x4ff9, 0x1068,
      { 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 } };

OLECHAR oszSummary[] = {'S','u','m','m','a','r','y',
                        'I','n','f','o','r','m','a','t','i','o','n','\0'};

GUID guidDocumentSummary =
    { 0xd5cdd502,
      0x2e9c, 0x101b,
      { 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae } };

OLECHAR oszDocumentSummary[] = {'D','o','c','u','m','e','n','t',
                                'S','u','m','m','a','r','y',
                                'I','n','f','o','r','m','a','t','i','o','n',
                                '\0'};

// Note that user defined properties are placed in section 2 with the below
// GUID as the FMTID -- alas, we did not expect Office95 to actually use it.

GUID guidDocumentSummarySection2 =
    { 0xd5cdd505,
      0x2e9c, 0x101b,
      { 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae } };

// *Global Info*

GUID guidGlobalInfo =
    { 0x56616F00,
      0xC154, 0x11ce,
      { 0x85, 0x53, 0x00, 0xAA, 0x00, 0xA1, 0xF9, 0x5B } };

// *Image Contents*

GUID guidImageContents =
    { 0x56616500,
      0xC154, 0x11ce,
      { 0x85, 0x53, 0x00, 0xAA, 0x00, 0xA1, 0xF9, 0x5B } };

// *Image Info*

GUID guidImageInfo =
    { 0x56616500,
      0xC154, 0x11ce,
      { 0x85, 0x53, 0x00, 0xAA, 0x00, 0xA1, 0xF9, 0x5B } };


//+--------------------------------------------------------------------------
// Function:    RtlGuidToPropertySetName
//
// Synopsis:    Map property set GUID to null-terminated UNICODE name string.
//
//              The awcname parameter is assumed to be a buffer with room for
//              CWC_PROPSETSZ (28) UNICODE characters.  The first character
//              is always WC_PROPSET0 (0x05), as specified by the OLE Appendix
//              B documentation.  The colon character normally used as an NT
//              stream name separator is not written to the caller's buffer.
//
//              No error is possible.
//
// Arguments:   IN GUID *pguid        -- pointer to GUID to convert
//              OUT OLECHAR aocname[] -- output string buffer
//
// Returns:     count of non-NULL characters in the output string buffer
//---------------------------------------------------------------------------

ULONG PROPSYSAPI PROPAPI
RtlGuidToPropertySetName(
    IN GUID const *pguid,
    OUT OLECHAR aocname[])
{
    ULONG cbitRemain = CBIT_BYTE;
    OLECHAR *poc = aocname;

    BYTE *pb;
    BYTE *pbEnd;    

    *poc++ = OC_PROPSET0;

    //  -----------------------
    //  Check for special-cases
    //  -----------------------

    // Note: CCH_PROPSET includes the OC_PROPSET0, and sizeof(osz...)
    // includes the trailing '\0', so sizeof(osz...) is ok because the
    // OC_PROPSET0 character compensates for the trailing NULL character.

    // Is this the SummaryInformation propset?
    PROPASSERT(CCH_PROPSET >= sizeof(oszSummary)/sizeof(OLECHAR));

    if (*pguid == guidSummary)
    {
        RtlCopyMemory(poc, oszSummary, sizeof(oszSummary));
        return(sizeof(oszSummary)/sizeof(OLECHAR));
    }

    // Is this The DocumentSummaryInformation or User-Defined propset?
    PROPASSERT(CCH_PROPSET >= sizeof(oszDocumentSummary)/sizeof(OLECHAR));

    if (*pguid == guidDocumentSummary || *pguid == guidDocumentSummarySection2)
    {
        RtlCopyMemory(poc, oszDocumentSummary, sizeof(oszDocumentSummary));
        return(sizeof(oszDocumentSummary)/sizeof(OLECHAR));
    }

    // Is this the Global Info propset?
    PROPASSERT(CCH_PROPSET >= sizeof(oszGlobalInfo)/sizeof(OLECHAR));
    if (*pguid == guidGlobalInfo)
    {
        RtlCopyMemory(poc, oszGlobalInfo, cboszGlobalInfo);
        return(cboszGlobalInfo/sizeof(OLECHAR));
    }

    // Is this the Image Contents propset?
    PROPASSERT(CCH_PROPSET >= sizeof(oszImageContents)/sizeof(OLECHAR));
    if (*pguid == guidImageContents)
    {
        RtlCopyMemory(poc, oszImageContents, cboszImageContents);
        return(cboszImageContents/sizeof(OLECHAR));
    }

    // Is this the Image Info propset?
    PROPASSERT(CCH_PROPSET >= sizeof(oszImageInfo)/sizeof(OLECHAR));
    if (*pguid == guidImageInfo)
    {
        RtlCopyMemory(poc, oszImageInfo, cboszImageInfo);
        return(cboszImageInfo/sizeof(OLECHAR));
    }


    //  ------------------------------
    //  Calculate the string-ized GUID
    //  ------------------------------

    // If this is a big-endian system, we need to convert
    // the GUID to little-endian for the conversion.

#if BIGENDIAN
    GUID guidByteSwapped = *pguid;
    PropByteSwap( &guidByteSwapped );
    pguid = &guidByteSwapped;
#endif

    // Point to the beginning and ending of the GUID
    pb = (BYTE*) pguid;
    pbEnd = pb + sizeof(*pguid);

    // Walk 'pb' through each byte of the GUID.

    while (pb < pbEnd)
    {
        ULONG i = *pb >> (CBIT_BYTE - cbitRemain);

        if (cbitRemain >= CBIT_CHARMASK)
        {
            *poc = MapChar(i);
            if (cbitRemain == CBIT_BYTE && *poc >= (OLECHAR)'a' 
                && *poc <= ((OLECHAR)'z'))
            {
                *poc += (OLECHAR) ( ((OLECHAR)'A') - ((OLECHAR)'a') );
            }
            poc++;
            cbitRemain -= CBIT_CHARMASK;
            if (cbitRemain == 0)
            {
                pb++;
                cbitRemain = CBIT_BYTE;
            }
        }
        else
        {
            if (++pb < pbEnd)
            {
                i |= *pb << cbitRemain;
            }
            *poc++ = MapChar(i);
            cbitRemain += CBIT_BYTE - CBIT_CHARMASK;
        }
    }   // while (pb < pbEnd)

    *poc = OLESTR( '\0' );
    return(CCH_PROPSET);

}


//+--------------------------------------------------------------------------
// Function:    RtlPropertySetNameToGuid
//
// Synopsis:    Map non null-terminated UNICODE string to a property set GUID.
//
//              If the name is not properly formed as per
//              RtlGuidToPropertySetName(), STATUS_INVALID_PARAMETER is
//              returned.  The pguid parameter is assumed to point to a buffer
//              with room for a GUID structure.
//
// Arguments:   IN ULONG cocname     -- count of OLECHARs in string to convert
//              IN OLECHAR aocname[] -- input string to convert
//              OUT GUID *pguid      -- pointer to buffer for converted GUID
//
// Returns:     NTSTATUS
//---------------------------------------------------------------------------

NTSTATUS PROPSYSAPI PROPAPI
RtlPropertySetNameToGuid(
    IN ULONG cocname,
    IN OLECHAR const aocname[],
    OUT GUID *pguid)
{
    NTSTATUS Status = STATUS_INVALID_PARAMETER;

    OLECHAR const *poc = aocname;

    if (poc[0] == OC_PROPSET0)
    {
        //  -----------------------
        //  Check for Special-Cases 
        //  -----------------------

        // Note: cocname includes the OC_PROPSET0, and sizeof(osz...)
        // includes the trailing OLESTR('\0'), but the comparison excludes both
        // the leading OC_PROPSET0 and the trailing '\0'.

        // Is this SummaryInformation?
        if (cocname == sizeof(oszSummary)/sizeof(OLECHAR) &&
            ocsnicmp(&poc[1], oszSummary, cocname - 1) == 0)
        {
            *pguid = guidSummary;
            return(STATUS_SUCCESS);
        }

        // Is this DocumentSummaryInformation?
        if (cocname == sizeof(oszDocumentSummary)/sizeof(OLECHAR) &&
            ocsnicmp(&poc[1], oszDocumentSummary, cocname - 1) == 0)
        {
            *pguid = guidDocumentSummary;
            return(STATUS_SUCCESS);
        }

        // Is this Global Info?
        if (cocname == cboszGlobalInfo/sizeof(OLECHAR) &&
            ocsnicmp(&poc[1], oszGlobalInfo, cocname - 1) == 0)
        {
            *pguid = guidGlobalInfo;
            return(STATUS_SUCCESS);
        }

        // Is this Image Info?
        if (cocname == cboszImageInfo/sizeof(OLECHAR) &&
            ocsnicmp(&poc[1], oszImageInfo, cocname - 1) == 0)
        {
            *pguid = guidImageInfo;
            return(STATUS_SUCCESS);
        }

        // Is this Image Contents?
        if (cocname == cboszImageContents/sizeof(OLECHAR) &&
            ocsnicmp(&poc[1], oszImageContents, cocname - 1) == 0)
        {
            *pguid = guidImageContents;
            return(STATUS_SUCCESS);
        }

        //  ------------------
        //  Calculate the GUID
        //  ------------------

        // None of the special-cases hit, so we must calculate
        // the GUID from the name.

        if (cocname == CCH_PROPSET)
        {
            ULONG cbit;
            BYTE *pb = (BYTE *) pguid - 1;

            RtlZeroMemory(pguid, sizeof(*pguid));
            for (cbit = 0; cbit < CBIT_GUID; cbit += CBIT_CHARMASK)
            {
                ULONG cbitUsed = cbit % CBIT_BYTE;
                ULONG cbitStored;
                OLECHAR oc;

                if (cbitUsed == 0)
                {
                    pb++;
                }

                oc = *++poc - (OLECHAR)'A'; // assume upper case 
                // for wchar (unsigned) -ve values becomes a large number
                // but for char, which is signed, -ve is -ve
                if (oc > CALPHACHARS || oc < 0)
                {
                    // oops, try lower case
                    oc += (OLECHAR) ( ((OLECHAR)'A') - ((OLECHAR)'a'));
                    if (oc > CALPHACHARS || oc < 0)
                    {
                        // must be a digit
                        oc += ((OLECHAR)'a') - ((OLECHAR)'0') + CALPHACHARS;
                        if (oc > CHARMASK)
                        {
                            goto Exit;                  // invalid character
                        }
                    }
                }
                *pb |= (BYTE) (oc << cbitUsed);

                cbitStored = min(CBIT_BYTE - cbitUsed, CBIT_CHARMASK);

                // If the translated bits wouldn't all fit in the current byte

                if (cbitStored < CBIT_CHARMASK)
                {
                    oc >>= CBIT_BYTE - cbitUsed;

                    if (cbit + cbitStored == CBIT_GUID)
                    {
                        if (oc != 0)
                        {
                            goto Exit;                  // extra bits
                        }
                        break;
                    }
                    pb++;

                    *pb |= (BYTE) oc;
                }
            }   // for (cbit = 0; cbit < CBIT_GUID; cbit += CBIT_CHARMASK)

            Status = STATUS_SUCCESS;

            // If byte-swapping is necessary, do so now on the calculated
            // GUID.

            PropByteSwap( pguid );

        }   // if (cocname == CCH_PROPSET)
    }   // if (poc[0] == OC_PROPSET0)


    //  ----
    //  Exit
    //  ----

Exit:

    return(Status);
}



inline BOOLEAN
_Compare_VT_BOOL(VARIANT_BOOL bool1, VARIANT_BOOL bool2)
{
    // Allow any non-zero value to match any non-zero value

    return((bool1 == FALSE) == (bool2 == FALSE));
}


BOOLEAN
_Compare_VT_CF(CLIPDATA *pclipdata1, CLIPDATA *pclipdata2)
{
    BOOLEAN fSame;

    if (pclipdata1 != NULL && pclipdata2 != NULL)
    {
        fSame = ( pclipdata1->cbSize == pclipdata2->cbSize
                  &&
                  pclipdata1->ulClipFmt == pclipdata2->ulClipFmt );

        if (fSame)
        {
            if (pclipdata1->pClipData != NULL && pclipdata2->pClipData != NULL)
            {
                fSame = memcmp(
                            pclipdata1->pClipData,
                            pclipdata2->pClipData,
                            CBPCLIPDATA(*pclipdata1)
                              ) == 0;
            }
            else
            {
                // They're the same if both are NULL, or if
                // they have a zero length (if they have a zero
                // length, either one may or may not be NULL, but they're
                // still considered the same).

                fSame = pclipdata1->pClipData == pclipdata2->pClipData
                        ||
                        CBPCLIPDATA(*pclipdata1) == 0;
            }
        }
    }
    else
    {
        fSame = pclipdata1 == pclipdata2;
    }
    return(fSame);
}


//+---------------------------------------------------------------------------
// Function:    RtlCompareVariants, public
//
// Synopsis:    Compare two passed PROPVARIANTs -- case sensitive for strings
//
// Arguments:   [CodePage]      -- CodePage
//              [pvar1]         -- pointer to PROPVARIANT
//              [pvar2]         -- pointer to PROPVARIANT
//
// Returns:     TRUE if identical, else FALSE
//---------------------------------------------------------------------------

#ifdef _MAC
EXTERN_C    // The Mac linker doesn't seem to be able to export with C++ decorations
#endif

BOOLEAN PROPSYSAPI PROPAPI
PropTestCompareVariants(
    USHORT CodePage,
    PROPVARIANT const *pvar1,
    PROPVARIANT const *pvar2)
{
    if (pvar1->vt != pvar2->vt)
    {
        return(FALSE);
    }

    BOOLEAN fSame;
    ULONG i;

    switch (pvar1->vt)
    {
    case VT_EMPTY:
    case VT_NULL:
        fSame = TRUE;
        break;

    case VT_I1:
    case VT_UI1:
        fSame = pvar1->bVal == pvar2->bVal;
        break;

    case VT_I2:
    case VT_UI2:
        fSame = pvar1->iVal == pvar2->iVal;
        break;

    case VT_BOOL:
        fSame = _Compare_VT_BOOL(pvar1->boolVal, pvar2->boolVal);
        break;

    case VT_I4:
    case VT_UI4:
    case VT_R4:
    case VT_ERROR:
        fSame = pvar1->lVal == pvar2->lVal;
        break;

    case VT_I8:
    case VT_UI8:
    case VT_R8:
    case VT_CY:
    case VT_DATE:
    case VT_FILETIME:
        fSame = pvar1->hVal.HighPart == pvar2->hVal.HighPart
                &&
                pvar1->hVal.LowPart  == pvar2->hVal.LowPart;
        break;

    case VT_CLSID:
        fSame = memcmp(pvar1->puuid, pvar2->puuid, sizeof(CLSID)) == 0;
        break;

    case VT_BLOB:
    case VT_BLOB_OBJECT:
        fSame = ( pvar1->blob.cbSize == pvar2->blob.cbSize );
        if (fSame)
        {
            fSame = memcmp(
                        pvar1->blob.pBlobData,
                        pvar2->blob.pBlobData,
                        pvar1->blob.cbSize) == 0;
        }
        break;

    case VT_CF:
        fSame = _Compare_VT_CF(pvar1->pclipdata, pvar2->pclipdata);
        break;

    case VT_BSTR:
        if (pvar1->bstrVal != NULL && pvar2->bstrVal != NULL)
        {
            fSame = ( BSTRLEN(pvar1->bstrVal) == BSTRLEN(pvar2->bstrVal) );
            if (fSame)
            {
                fSame = memcmp(
                            pvar1->bstrVal,
                            pvar2->bstrVal,
                            BSTRLEN(pvar1->bstrVal)) == 0;
            }
        }
        else
        {
            fSame = pvar1->bstrVal == pvar2->bstrVal;
        }
        break;

    case VT_LPSTR:
        if (pvar1->pszVal != NULL && pvar2->pszVal != NULL)
        {
            fSame = strcmp(pvar1->pszVal, pvar2->pszVal) == 0;
        }
        else
        {
            fSame = pvar1->pszVal == pvar2->pszVal;
        }
        break;

    case VT_STREAM:
    case VT_STREAMED_OBJECT:
    case VT_STORAGE:
    case VT_STORED_OBJECT:
    case VT_LPWSTR:
        if (pvar1->pwszVal != NULL && pvar2->pwszVal != NULL)
        {
            fSame = Prop_wcscmp(pvar1->pwszVal, pvar2->pwszVal) == 0;
        }
        else
        {
            fSame = pvar1->pwszVal == pvar2->pwszVal;
        }
        break;

    case VT_VECTOR | VT_I1:
    case VT_VECTOR | VT_UI1:
        fSame = ( pvar1->caub.cElems == pvar2->caub.cElems );
        if (fSame)
        {
            fSame = memcmp(
                        pvar1->caub.pElems,
                        pvar2->caub.pElems,
                        pvar1->caub.cElems * sizeof(pvar1->caub.pElems[0])) == 0;
        }
        break;

    case VT_VECTOR | VT_I2:
    case VT_VECTOR | VT_UI2:
        fSame = ( pvar1->cai.cElems == pvar2->cai.cElems );
        if (fSame)
        {
            fSame = memcmp(
                        pvar1->cai.pElems,
                        pvar2->cai.pElems,
                        pvar1->cai.cElems * sizeof(pvar1->cai.pElems[0])) == 0;
        }
        break;

    case VT_VECTOR | VT_BOOL:
        fSame = ( pvar1->cabool.cElems == pvar2->cabool.cElems );
        if (fSame)
        {
            for (i = 0; i < pvar1->cabool.cElems; i++)
            {
                fSame = _Compare_VT_BOOL(
                                pvar1->cabool.pElems[i],
                                pvar2->cabool.pElems[i]);
                if (!fSame)
                {
                    break;
                }
            }
        }
        break;

    case VT_VECTOR | VT_I4:
    case VT_VECTOR | VT_UI4:
    case VT_VECTOR | VT_R4:
    case VT_VECTOR | VT_ERROR:
        fSame = ( pvar1->cal.cElems == pvar2->cal.cElems );
        if (fSame)
        {
            fSame = memcmp(
                        pvar1->cal.pElems,
                        pvar2->cal.pElems,
                        pvar1->cal.cElems * sizeof(pvar1->cal.pElems[0])) == 0;
        }
        break;

    case VT_VECTOR | VT_I8:
    case VT_VECTOR | VT_UI8:
    case VT_VECTOR | VT_R8:
    case VT_VECTOR | VT_CY:
    case VT_VECTOR | VT_DATE:
    case VT_VECTOR | VT_FILETIME:
        fSame = ( pvar1->cah.cElems == pvar2->cah.cElems );
        if (fSame)
        {
            fSame = memcmp(
                        pvar1->cah.pElems,
                        pvar2->cah.pElems,
                        pvar1->cah.cElems *
                            sizeof(pvar1->cah.pElems[0])) == 0;
        }
        break;

    case VT_VECTOR | VT_CLSID:
        fSame = ( pvar1->cauuid.cElems == pvar2->cauuid.cElems );
        if (fSame)
        {
            fSame = memcmp(
                        pvar1->cauuid.pElems,
                        pvar2->cauuid.pElems,
                        pvar1->cauuid.cElems *
                            sizeof(pvar1->cauuid.pElems[0])) == 0;
        }
        break;

    case VT_VECTOR | VT_CF:
        fSame = ( pvar1->caclipdata.cElems == pvar2->caclipdata.cElems );
        if (fSame)
        {
            for (i = 0; i < pvar1->caclipdata.cElems; i++)
            {
                fSame = _Compare_VT_CF(
                                &pvar1->caclipdata.pElems[i],
                                &pvar2->caclipdata.pElems[i]);
                if (!fSame)
                {
                    break;
                }
            }
        }
        break;

    case VT_VECTOR | VT_BSTR:
        fSame = ( pvar1->cabstr.cElems == pvar2->cabstr.cElems );
        if (fSame)
        {
            for (i = 0; i < pvar1->cabstr.cElems; i++)
            {
                if (pvar1->cabstr.pElems[i] != NULL &&
                    pvar2->cabstr.pElems[i] != NULL)
                {
                    fSame = ( BSTRLEN(pvar1->cabstr.pElems[i])
                              ==
                              BSTRLEN(pvar2->cabstr.pElems[i]) );
                    if (fSame)
                    {
                        fSame = memcmp(
                                    pvar1->cabstr.pElems[i],
                                    pvar2->cabstr.pElems[i],
                                    BSTRLEN(pvar1->cabstr.pElems[i])) == 0;
                    }
                }
                else
                {
                    fSame = pvar1->cabstr.pElems[i] == pvar2->cabstr.pElems[i];
                }
                if (!fSame)
                {
                    break;
                }
            }
        }
        break;

    case VT_VECTOR | VT_LPSTR:
        fSame = ( pvar1->calpstr.cElems == pvar2->calpstr.cElems );
        if (fSame)
        {
            for (i = 0; i < pvar1->calpstr.cElems; i++)
            {
                if (pvar1->calpstr.pElems[i] != NULL &&
                    pvar2->calpstr.pElems[i] != NULL)
                {
                    fSame = strcmp(
                                pvar1->calpstr.pElems[i],
                                pvar2->calpstr.pElems[i]) == 0;
                }
                else
                {
                    fSame = pvar1->calpstr.pElems[i] == 
                            pvar2->calpstr.pElems[i];
                }
                if (!fSame)
                {
                    break;
                }
            }
        }
        break;

    case VT_VECTOR | VT_LPWSTR:
        fSame = ( pvar1->calpwstr.cElems == pvar2->calpwstr.cElems );
        if (fSame)
        {
            for (i = 0; i < pvar1->calpwstr.cElems; i++)
            {
                if (pvar1->calpwstr.pElems[i] != NULL &&
                    pvar2->calpwstr.pElems[i] != NULL)
                {
                    fSame = Prop_wcscmp(
                                pvar1->calpwstr.pElems[i],
                                pvar2->calpwstr.pElems[i]) == 0;
                }
                else
                {
                    fSame = pvar1->calpwstr.pElems[i] == 
                            pvar2->calpwstr.pElems[i];
                }
                if (!fSame)
                {
                    break;
                }
            }
        }
        break;

    case VT_VECTOR | VT_VARIANT:
        fSame = ( pvar1->capropvar.cElems == pvar2->capropvar.cElems );
        if (fSame)
        {
            for (i = 0; i < pvar1->capropvar.cElems; i++)
            {
                fSame = PropTestCompareVariants(
                                CodePage,
                                &pvar1->capropvar.pElems[i],
                                &pvar2->capropvar.pElems[i]);
                if (!fSame)
                {
                    break;
                }
            }
        }
        break;

    default:
        PROPASSERT(!"Invalid type for PROPVARIANT Comparison");
        fSame = FALSE;
        break;

    }
    return(fSame);
}