/*++



// Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved 

Module Name:

    BMOF.C

Abstract:

    Structures and helper functions for naviagating a BMOF file.

History:

    a-davj  14-April-97   Created.

--*/

#include <windows.h>
#include <oleauto.h>
#include <string.h>
#include "bmof.h"
#include <wbemutil.h>

int wmi_unaligned_wcslen( LPCWSTR wszString )
{
    BYTE*   pbData = (BYTE*) wszString;
	int i;

    // Walk the string looking for two 0 bytes next to each other.
    for( i =0; !(!*(pbData) && !*(pbData+1) ); pbData+=2, i++ );

    return i;
}

WCHAR* wmi_unaligned_wcscpy( WCHAR* wszDest, LPCWSTR wszSource )
{
    int nLen = wmi_unaligned_wcslen( wszSource );

    // Account for the NULL terminator when copying
    CopyMemory( (BYTE*) wszDest, (BYTE*) wszSource, (nLen+1) * 2 );

    return wszDest;
}

//***************************************************************************
//
//  BOOL LookupFlavor
//
//  DESCRIPTION:
//
//  Looks in the flavor table to see if a qualifier has a flavor.
//
//  PARAMETERS:
//
//  pQual      Pointer to the qualifier.
//  pdwFlavor  Pointer to where the return value is put
//  pBuff      Pointer to the main buffer.  I.e. "BMOF...."
//  pToFar	   Pointer to 1 past the last byte in the buffer
//
//
//  RETURN VALUE:
//
//  TRUE if there is a flavor.  Note that failure is normal
//
//***************************************************************************

BOOL LookupFlavor(BYTE * pQual, DWORD * pdwFlavor, BYTE * pBuff, BYTE * pToFar)
{
    UNALIGNED DWORD * pTemp;
    BYTE * pFlavorBlob;
    DWORD dwNumPairs;
    UNALIGNED DWORD * pOffset;
    UNALIGNED DWORD * pFlavor;
    DWORD dwMyOffset;
    DWORD dwCnt;

    *pdwFlavor = 0;

    // Calculate the pointer of the start of the flavor data

    pTemp = (DWORD * )pBuff;
    pTemp++;                            // point to the original blob size
    pFlavorBlob = pBuff + *pTemp;

	// Dont even try past the end of memory

	if(pToFar == NULL)
		return FALSE;

	if(pFlavorBlob + 16 >= pToFar)
		return FALSE;

    // Check if the flavor blob is valid, it should start off with the 
    // characters "BMOFQUALFLAVOR11"

    if(memcmp(pFlavorBlob, "BMOFQUALFLAVOR11", 16))
        return FALSE;                               // Not really a problem since it may be old file
    
    // The flavor part of the file has the format 
    // DWORD dwNumPair, followed by pairs of dwords;
    // offset, flavor

    // Determine the number of pairs

    pFlavorBlob+= 16;
    pTemp = (DWORD *)pFlavorBlob;
    dwNumPairs = *pTemp;              // Number of offset/value pairs
    if(dwNumPairs < 1)
        return FALSE;

    // point to the first offset/flavor pair

    pOffset = pTemp+1;
    pFlavor = pOffset+1;

    // Determine the offset we are looking for.  That is the pointer to the qualifier minus
    // the pointer to the start of the block;

    dwMyOffset = (DWORD)(pQual - pBuff);

    for(dwCnt = 0; dwCnt < dwNumPairs; dwCnt++)
    {
        if(dwMyOffset == *pOffset)
        {
            *pdwFlavor = *pFlavor;
        }
        if(dwMyOffset < *pOffset)
            return FALSE;
        pOffset += 2;
        pFlavor += 2;
    }
    return FALSE;
}

//***************************************************************************
//
//  int ITypeSize
//
//  DESCRIPTION:
//
//  Gets the number of bytes acutally used to store
//  a variant type.  0 if the type is unknown
//
//  PARAMETERS:
//
//  vtTest      Type in question.
//
//
//  RETURN VALUE:
//
//  see description
//
//***************************************************************************

int iTypeSize(
        IN DWORD vtTest)
{
    int iRet;
    vtTest &= ~ VT_ARRAY; // get rid of possible array bit
    vtTest &= ~ VT_BYREF; // get rid of possible byref bit

    switch (vtTest) {
        case VT_UI1:
        case VT_LPSTR:
            iRet = 1;
            break;
        case VT_LPWSTR:
        case VT_BSTR:
        case VT_I2:
            iRet = 2;
            break;
        case VT_I4:
        case VT_R4:
            iRet = 4;
            break;
        case VT_R8:
            iRet = 8;
            break;
        case VT_BOOL:
            iRet = sizeof(VARIANT_BOOL);
            break;
        case VT_ERROR:
            iRet = sizeof(SCODE);
            break;
        case VT_CY:
            iRet = sizeof(CY);
            break;
        case VT_DATE:
            iRet = sizeof(DATE);
            break;

        default:
            iRet = 0;
        }
    return iRet;
}


//***************************************************************************
//
//  CBMOFQualList * CreateQualList
//
//  DESCRIPTION:
//
//  Create a CBMOFQualList object which serves as a wrapper.
//
//  PARAMETERS:
//
//  pwql                 pointer to the WBEM_Qualifier structure in the binary
//                      MOF.
//
//  RETURN VALUE:
//
//  Pointer to CBMOFQualList structure that servers as a wrapper.  NULL
//  if error.  This must be freed via BMOFFree() when no longer needed.
//
//
//***************************************************************************

CBMOFQualList * CreateQualList(UNALIGNED WBEM_QualifierList *pwql)
{

    CBMOFQualList * pRet = NULL;
    if(pwql == NULL)
      return NULL;


    pRet = (CBMOFQualList *)BMOFAlloc(sizeof (CBMOFQualList));
    if(pRet != NULL)
    {
        pRet->m_pql = pwql;
        pRet->m_pInfo = (UNALIGNED WBEM_Qualifier *)
            ((BYTE *)pRet->m_pql + sizeof(WBEM_QualifierList));;
        ResetQualList(pRet);
    }
    return pRet;
}


//***************************************************************************
//
//  void ResetQualList
//
//  DESCRIPTION:
//
//  Resets CBMOFQualList stucture to point to the first entry.
//
//  PARAMETERS:
//
//  pql                 structure to be reset
//
//***************************************************************************

void ResetQualList(CBMOFQualList * pql)
{
   if(pql)
   {
      pql->m_CurrQual = 0;
      pql->m_pCurr = pql->m_pInfo;
   }
}

//***************************************************************************
//
//  BOOL NextQual
//
//  DESCRIPTION:
//
//  Gets the name and value of the next qualifier in the list.
//
//  PARAMETERS:
//
//  pql                 Input, points to CBMOFQualList object with data
//  ppName              Output, if functions succeeds, this points to a 
//                      WCHAR string that the caller must free.  May be
//                      NULL if caller doesnt want name.
//  pItem               Input/Output, if set, points to a data item structure
//                      which will be updated to point to the qualifier data.
//
//  RETURN VALUE:
//
//  TRUE if OK.
//
//
//***************************************************************************

BOOL NextQual(CBMOFQualList * pql,WCHAR ** ppName, CBMOFDataItem * pItem)
{
    return NextQualEx(pql, ppName, pItem, NULL, NULL, NULL);
}


//***************************************************************************
//
//  BOOL NextQualEx
//
//  DESCRIPTION:
//
//  Gets the name and value of the next qualifier in the list.
//
//  PARAMETERS:
//
//  pql                 Input, points to CBMOFQualList object with data
//  ppName              Output, if functions succeeds, this points to a 
//                      WCHAR string that the caller must free.  May be
//                      NULL if caller doesnt want name.
//  pItem               Input/Output, if set, points to a data item structure
//                      which will be updated to point to the qualifier data.
//  pdwFlavor           optional, pointer to where the flavor value is to be copied
//                      if not null, then pBuff must be set!
//  pBuff               Pointer to the starting byte of the whole blob.  Same as 
//                      whats copied to CreateObjList.
//  pToFar              Pointer to one past the end of the memory buffer
//
//  RETURN VALUE:
//
//  TRUE if OK.
//
//
//***************************************************************************

BOOL NextQualEx(CBMOFQualList * pql,WCHAR ** ppName, CBMOFDataItem * pItem,
                                            DWORD * pdwFlavor, BYTE * pBuff, BYTE * pToFar)
{
    BYTE * pInfo;
    BOOL bRet = TRUE;

    if(pql == NULL || pql->m_CurrQual++ >= pql->m_pql->dwNumQualifiers)
        return FALSE;

    if(pdwFlavor && pBuff)
        LookupFlavor((BYTE *)pql->m_pCurr, pdwFlavor, pBuff, pToFar);
 
    pInfo = (BYTE *)pql->m_pCurr + sizeof(WBEM_Qualifier);
    
    if(ppName)
      SetName(ppName, pInfo, pql->m_pCurr->dwOffsetName);

    if(pInfo)
      bRet = SetValue(pItem, pInfo, pql->m_pCurr->dwOffsetValue, 
                        pql->m_pCurr->dwType);

    // advance to next
    pql->m_pCurr = (UNALIGNED WBEM_Qualifier *)((BYTE *)pql->m_pCurr + pql->m_pCurr->dwLength);
    return bRet;
}

//***************************************************************************
//
//  BOOL FindQual
//
//  DESCRIPTION:
//
//  Searches for a qualifier with a given name.  The search is case 
//  insensitive
//
//  PARAMETERS:
//
//  pql                 Input, pointer to qualifier list
//  pName               Input, Name to be searched for.
//  pItem               Input/Output, if successful set to point to the data.
//
//  RETURN VALUE:
//
//  True if OK.
//
//***************************************************************************

BOOL FindQual(CBMOFQualList * pql,WCHAR * pName, CBMOFDataItem * pItem)
{
    return FindQualEx(pql, pName, pItem, NULL, NULL, NULL);
}

//***************************************************************************
//
//  BOOL FindQualEx
//
//  DESCRIPTION:
//
//  Searches for a qualifier with a given name.  The search is case 
//  insensitive
//
//  PARAMETERS:
//
//  pql                 Input, pointer to qualifier list
//  pName               Input, Name to be searched for.
//  pItem               Input/Output, if successful set to point to the data.
//  pdwFlavor           optional, pointer to where the flavor value is to be copied
//                      if not null, then pBuff must be set!
//  pBuff               Pointer to the starting byte of the whole blob.  Same as 
//                      whats copied to CreateObjList.
//
//  RETURN VALUE:
//
//  True if OK.
//
//***************************************************************************

BOOL FindQualEx(CBMOFQualList * pql,WCHAR * pName, CBMOFDataItem * pItem, 
                                          DWORD * pdwFlavor, BYTE * pBuff, BYTE * pToFar)
{
    DWORD dwCnt;
    UNALIGNED WBEM_Qualifier * pQual = pql->m_pInfo;
    for(dwCnt = 0; dwCnt < pql->m_pql->dwNumQualifiers; dwCnt++)
    {
        WCHAR * pTest;
        BOOL bMatch;
        BYTE * pInfo = (BYTE *)pQual + sizeof(WBEM_Qualifier);
        if(!SetName(&pTest, pInfo, pQual->dwOffsetName))
            return FALSE;

        bMatch = !_wcsicmp(pTest, pName);
        BMOFFree(pTest);
        if(bMatch)
        {
            if(pdwFlavor && pBuff)
                LookupFlavor((BYTE *)pQual, pdwFlavor, pBuff, pToFar);
            return SetValue(pItem, pInfo, pQual->dwOffsetValue, pQual->dwType);
        }
        pQual = (UNALIGNED WBEM_Qualifier *)((BYTE *)pQual + pQual->dwLength);
    }
    return FALSE;
}

//***************************************************************************
//
//  BOOL SetValue
//
//  DESCRIPTION:
//
//  Sets up a CBMOFDataItem structure to point to a value in the BMOF.
//
//  PARAMETERS:
//
//  pItem               Input/Output, item to be set
//  pInfo               Input, start of information block
//  dwOffset            Input, offset to actual data.
//  dwType              Input data type.
//
//  RETURN VALUE:
//
//  TRUE if OK.
//
//***************************************************************************

BOOL SetValue(CBMOFDataItem * pItem, BYTE * pInfo, DWORD dwOffset, DWORD dwType)
{

    if(pItem == NULL || pInfo == NULL)
        return FALSE;

    pItem->m_dwType = dwType;

    // Check for NULL case.  This is how uninitialized data is stored.

    if(dwOffset == 0xffffffff)
        pItem->m_pData = NULL;
    else
        pItem->m_pData = pInfo + dwOffset;

    return TRUE;
}

//***************************************************************************
//
//  BOOL SetName
//
//  DESCRIPTION:
//
//  Gets a name out of an information block.
//
//  PARAMETERS:
//
//  ppName              Input/Output.  On successful return, will point to a
//                      WCHAR string containing the name.  This MUST be freed
//                      by the caller via BMOFFree()!
//  pInfo               Input, start of information block
//  dwOffset            Input, offset to actual data.
//
//  RETURN VALUE:
//
//  TRUE if OK.
//***************************************************************************

BOOL SetName(WCHAR ** ppName, BYTE * pInfo, DWORD dwOffset)
{
    UNALIGNED WCHAR * pName;
    if(ppName == NULL || pInfo == NULL || dwOffset == 0xffffffff)
        return FALSE;

    pName = (UNALIGNED WCHAR *)(pInfo + dwOffset);   // point to string in info block
    *ppName = (WCHAR *)BMOFAlloc(2*(wmi_unaligned_wcslen((LPWSTR)pName) + 1));
    if(*ppName == NULL)
        return FALSE;
    wmi_unaligned_wcscpy(*ppName, (LPWSTR)pName);
    return TRUE;
}

//***************************************************************************
//
//  CBMOFObj * CreateObj
//
//  DESCRIPTION:
//
//  Create a CBMOFObj structure which wraps a WBEM_Object
//
//  PARAMETERS:
//
//  pwob                Input, structure to be wrapped
//
//  RETURN VALUE:
//
//  pointer to wrapper structure.  NULL if error.  This must be freed via
//  BMOFFree() when no longer needed.
//
//***************************************************************************

CBMOFObj * CreateObj(UNALIGNED WBEM_Object * pwob)
{
    CBMOFObj * pRet = (CBMOFObj *)BMOFAlloc(sizeof(CBMOFObj));
    if(pRet)
     {
        pRet->m_pob = pwob;
        pRet->m_pInfo = ((BYTE *)pwob) + sizeof(WBEM_Object);
        pRet->m_ppl = (WBEM_PropertyList*)(pRet->m_pInfo +
                                        pwob->dwOffsetPropertyList);
        pRet->m_pml = (WBEM_PropertyList*)(pRet->m_pInfo +
                                        pwob->dwOffsetMethodList);
        ResetObj(pRet);
     }
    return pRet;
}


//***************************************************************************
//
//  void ResetObj
//
//  DESCRIPTION:
//
//  Resets a CBMOFObj structure so that it points to its first property.
//
//  PARAMETERS:
//
//  pob                 Input/Output, stucture to be reset.
//
//***************************************************************************

void ResetObj(CBMOFObj * pob)
{
   if(pob)
   {
      pob->m_CurrProp = 0;
      pob->m_pCurrProp = (UNALIGNED WBEM_Property *) ((BYTE *)pob->m_ppl +
                                    sizeof(WBEM_PropertyList));
      pob->m_CurrMeth = 0;
      pob->m_pCurrMeth = (UNALIGNED WBEM_Property *) ((BYTE *)pob->m_pml +
                                    sizeof(WBEM_PropertyList));
   }
}

//***************************************************************************
//
//  CBMOFQualList * GetQualList
//
//  DESCRIPTION:
//
//  Returns a CBMOFQualList structure which wraps the objects qualifier list.
//
//  PARAMETERS:
//
//  pob                 Input.  Structure which wraps the object.
//
//  RETURN VALUE:
//
//  Pointer to CBMOFQualList stucture.  NULL if error.  Note that this must
//  be freed by the caller via BMOFFree()
//
//***************************************************************************

CBMOFQualList * GetQualList(CBMOFObj * pob)
{

    UNALIGNED WBEM_QualifierList *pql;
    if(pob->m_pob->dwOffsetQualifierList == 0xffffffff)
        return NULL;
    pql = (UNALIGNED WBEM_QualifierList *)((BYTE *)pob->m_pInfo+
                            pob->m_pob->dwOffsetQualifierList);
    return CreateQualList(pql);
}


//***************************************************************************
//
//  CBMOFQualList * GetPropQualList
//  CBMOFQualList * GetMethQualList
//
//  DESCRIPTION:
//
//  Returns a CBMOFQualList structure which wraps a property or
//  methods qualifier list.
//
//  PARAMETERS:
//
//  pob                 Input.  Structure which wraps the object.
//  pName               Input.  Property name.  Note that this is case
//                      insensitive.
//
//  RETURN VALUE:
//
//  Pointer to CBMOFQualList stucture.  NULL if error.  Note that this must
//  be freed by the caller via BMOFFree()
//
//***************************************************************************

CBMOFQualList * GetPropOrMethQualList(UNALIGNED WBEM_Property * pProp)
{
    if(pProp == NULL)
        return NULL;
    if(pProp->dwOffsetQualifierSet == 0xffffffff)
        return NULL;
    return CreateQualList((UNALIGNED WBEM_QualifierList *)(
                                    (BYTE *)pProp + sizeof(WBEM_Property)+
                                    pProp->dwOffsetQualifierSet));
}

CBMOFQualList * GetPropQualList(CBMOFObj * pob, WCHAR * pName)
{
    UNALIGNED WBEM_Property * pProp = FindPropPtr(pob, pName);
    return GetPropOrMethQualList(pProp);
}

CBMOFQualList * GetMethQualList(CBMOFObj * pob, WCHAR * pName)
{
    UNALIGNED WBEM_Property * pProp = FindMethPtr(pob, pName);
    return GetPropOrMethQualList(pProp);
}

//***************************************************************************
//
//  BOOL NextProp
//  BOOL NextMet
//
//  DESCRIPTION:
//
//
//  PARAMETERS:
//
//  pob                 Input.  Structure which wraps the object.
//  ppName              Output, if functions succeeds, this points to a
//                      WCHAR string that the caller must free.  May be
//                      NULL if caller doesnt want name.
//  pItem               Input/Output, if set, points to a data item structure
//                      which will be updated to point to the qualifier data.
///
//  RETURN VALUE:
//
//  TRUE if OK.
//***************************************************************************

BOOL Info(UNALIGNED WBEM_Property * pPropOrMeth, WCHAR ** ppName, CBMOFDataItem * pItem)
{
    BYTE * pInfo;
    BOOL bRet = TRUE;
    if(pPropOrMeth == NULL)
        return FALSE;

    pInfo = (BYTE *)pPropOrMeth + sizeof(WBEM_Property);
    if(ppName)
       SetName(ppName, pInfo, pPropOrMeth->dwOffsetName);
    if(pItem)
       bRet = SetValue(pItem, pInfo,
                        pPropOrMeth->dwOffsetValue,
                        pPropOrMeth->dwType);
    return bRet;
}

BOOL NextProp(CBMOFObj * pob, WCHAR ** ppName, CBMOFDataItem * pItem)
{
    BOOL bRet = TRUE;

    if(pob == FALSE || pob->m_CurrProp++ >= pob->m_ppl->dwNumberOfProperties)
        return FALSE;

    Info(pob->m_pCurrProp, ppName, pItem);

    // advance pointer to next property.

    pob->m_pCurrProp = (UNALIGNED WBEM_Property *)
                        ((BYTE *)pob->m_pCurrProp + pob->m_pCurrProp->dwLength);
    return bRet;
}

BOOL NextMeth(CBMOFObj * pob, WCHAR ** ppName, CBMOFDataItem * pItem)
{
    BOOL bRet = TRUE;

    if(pob == FALSE || pob->m_CurrMeth++ >= pob->m_pml->dwNumberOfProperties)
        return FALSE;

    Info(pob->m_pCurrMeth, ppName, pItem);

    // advance pointer to next method.

    pob->m_pCurrMeth = (UNALIGNED WBEM_Property *)
                        ((BYTE *)pob->m_pCurrMeth + pob->m_pCurrMeth->dwLength);
    return bRet;
}

//***************************************************************************
//
//  BOOL FindProp
//  BOOL FindMeth
//
//  DESCRIPTION:
//
//  Sets a CBMOFDataItem structure to point to a properties data.
//
//  PARAMETERS:
//
//  pob                 Input.  Structure which wraps the object.
//  pName               Input. Name to be use for case insensitve search.
//  pItem               Input/Output.  Data item stucture to be updated.
//
//  RETURN VALUE:
//
//  True if found.
//
//***************************************************************************

BOOL FindProp(CBMOFObj * pob, WCHAR * pName, CBMOFDataItem * pItem)
{
    UNALIGNED WBEM_Property * pProp = FindPropPtr(pob, pName);
    return Info(pProp, NULL, pItem);
}

BOOL FindMeth(CBMOFObj * pob, WCHAR * pName, CBMOFDataItem * pItem)
{
    UNALIGNED WBEM_Property * pProp = FindMethPtr(pob, pName);
    return Info(pProp, NULL, pItem);
}

//***************************************************************************
//
//  BOOL GetName
//
//  DESCRIPTION:
//
//  Gets the name of an object.  This is works be returning the "__Class"
//  property.
//
//  PARAMETERS:
//
//  pob                 Input.  Structure which wraps the object.
//  ppName              Input/Output.  Points to a WCHAR string which
//                      has the name.  The caller MUST free this via
//                      BMOFFree()
//
//  RETURN VALUE:
//
//  TRUE if OK.
//
//***************************************************************************

BOOL GetName(CBMOFObj * pob, WCHAR ** ppName)
{
    CBMOFDataItem Item;
    BOOL bRet = FALSE, bFound;
    if(pob == NULL || ppName == NULL)
        return FALSE;

    bFound = FindProp(pob, L"__Class", &Item);
    if(!bFound)
        return FALSE;
    if(Item.m_dwType == VT_BSTR  && ppName)
    {
        bRet = GetData(&Item, (BYTE *)ppName, NULL);
    }
    return bRet;
}


//***************************************************************************
//
//  DWORD GetType
//
//  DESCRIPTION:
//
//  Returns an objects type.  A 0 indicates a class while a 1 indicates an
//  instance.  A 0xffffffff if passed a null pointer.
//
//  PARAMETERS:
//
//  pob                 Input.  Structure which wraps the object.
//
//
//  RETURN VALUE:
//
//  See description.
//
//***************************************************************************

DWORD GetType(CBMOFObj * pob)
{
   if(pob)
      return pob->m_pob->dwType;
   else
      return 0xFFFFFFFF;
}

//***************************************************************************
//
//  WBEM_Property * FindPropPtr
//  WBEM_Property * FindMethPtr
//
//  DESCRIPTION:
//
//  Returns a WBEM_Property stucture pointer for a particular property or
//  method given its name.
//
//  PARAMETERS:
//
//  pob                 Input.  Structure which wraps the object.
//  pName               Input.  Name of property.  Comparison is case
//                      insensitive.
//
//  RETURN VALUE:
//
//  pointer to WBEM_Property, NULL if it cant be found.
//
//***************************************************************************

UNALIGNED WBEM_Property *  Search(BYTE * pList, DWORD dwListSize, WCHAR * pName)
{
    DWORD dwCnt;
    UNALIGNED WBEM_Property * pProp = NULL;

    // point to first property structure

    pProp = (UNALIGNED WBEM_Property *)(pList + sizeof(WBEM_PropertyList));

    for(dwCnt = 0; dwCnt < dwListSize; dwCnt++)
    {
        WCHAR * pTest;
        BOOL bMatch;

        // point to the property's name and retrieve it

        BYTE * pInfo = (BYTE *)pProp + sizeof(WBEM_Property);
        if(!SetName(&pTest, pInfo, pProp->dwOffsetName))
            return NULL;
        bMatch = !_wcsicmp(pTest, pName);
        BMOFFree(pTest);

        // If we have a match, return

        if(bMatch)
            return pProp;

        pProp = (UNALIGNED WBEM_Property *)((BYTE *)pProp + pProp->dwLength);
    }
    return NULL;
}

UNALIGNED WBEM_Property * FindPropPtr(CBMOFObj * pob, WCHAR * pName)
{
    if(pob == NULL || pName == NULL)
      return NULL;

    // point to first property structure

    return Search((BYTE *)pob->m_ppl, pob->m_ppl->dwNumberOfProperties, pName);
}

UNALIGNED WBEM_Property * FindMethPtr(CBMOFObj * pob, WCHAR * pName)
{
    if(pob == NULL || pName == NULL)
      return NULL;

    // point to first property structure

    return Search((BYTE *)pob->m_pml, pob->m_pml->dwNumberOfProperties, pName);
}


//***************************************************************************
//
//  CBMOFObjList * CreateObjList
//
//  DESCRIPTION:
//
//  Create a CBMOFObjList structure which wraps a BMOF file.
//
//  PARAMETERS:
//
//  pBuff                Input, points to start of BMOF file.
//
//  RETURN VALUE:
//
//  pointer to wrapper structure.  NULL if error.  This must be freed via
//  BMOFFree() when no longer needed.
//
//***************************************************************************

CBMOFObjList * CreateObjList(BYTE * pBuff)
{
    CBMOFObjList * pRet = (CBMOFObjList * )BMOFAlloc(sizeof(CBMOFObjList));
    if(pRet)
    {
        pRet->m_pol = (WBEM_Binary_MOF *)pBuff;
        pRet->m_pInfo = (WBEM_Object *)
                   ((BYTE *)pBuff + sizeof(WBEM_Binary_MOF));
        ResetObjList(pRet);
    }
    return pRet;
}


//***************************************************************************
//
//  void ResetObjList
//
//  DESCRIPTION:
//
//  Resets a CBMOFObjList structure so that it points to its first object.
//
//  PARAMETERS:
//
//  pol                 Input/Output, stucture to be reset.
//
//***************************************************************************

void ResetObjList(CBMOFObjList * pol)
{
   if(pol)
   {
      pol->m_pCurrObj = pol->m_pInfo;
      pol->m_CurrObj = 0;
   }
}

//***************************************************************************
//
//  CBMOFObj * NextObj
//
//  DESCRIPTION:
//
//  Gets the next object in the list.
//
//  PARAMETERS:
//
//  pol                 Input. Pointer to CBMOFObjList object
//
//  RETURN VALUE:
//
//  Pointer to a CBMOFObj stucture which can be use to access the object
//  information.  NULL if error.  Note that the caller MUST Free this via
//  BMOFFree().
//
//***************************************************************************

CBMOFObj * NextObj(CBMOFObjList *pol)
{
    CBMOFObj * pRet;

    if(pol == NULL || pol->m_CurrObj++ >= pol->m_pol->dwNumberOfObjects)
        return NULL;

    pRet = CreateObj(pol->m_pCurrObj);
    pol->m_pCurrObj = (UNALIGNED WBEM_Object *)((BYTE *)pol->m_pCurrObj + pol->m_pCurrObj->dwLength);
    return pRet;
}


//***************************************************************************
//
//  CBMOFObj * FindObj
//
//  DESCRIPTION:
//
//  Searches the object list for the first object which has a "__className"
//  property.  The search is case insensitive.
//
//  PARAMETERS:
//
//  pol                 Input. Pointer to CBMOFObjList object
//  pName               Input. Name of object being searched for
//
//  RETURN VALUE:
//
//  Pointer to a CBMOFObj stucture which can be use to access the object
//  information.  NULL if error.  Note that the caller MUST Free this via
//  BMOFFree().
//
//***************************************************************************

CBMOFObj * FindObj(CBMOFObjList *pol, WCHAR * pName)
{
    DWORD dwCnt;
    UNALIGNED WBEM_Object * pob;

    if(pol->m_pol == NULL || pName == NULL)
        return NULL;

    pob = pol->m_pInfo;
    for(dwCnt = 0; dwCnt < pol->m_pol->dwNumberOfObjects; dwCnt)
    {
        WCHAR * pwcName = NULL;
        BOOL bMatch = FALSE;

        CBMOFObj * pRet = CreateObj(pob);
         if(pRet == NULL)
            return NULL;
        if(GetName(pRet,&pwcName))
            bMatch = TRUE;
        if(pwcName)
            BMOFFree(pwcName);

        // If we found it, return it, otherwise free object and advance

        if(bMatch)
            return pRet;
        BMOFFree(pRet);
        pob = (UNALIGNED WBEM_Object *)((BYTE *)pob + pob->dwLength);
    }
    return NULL;
}


//***************************************************************************
//
//  int GetNumDimensions
//
//  DESCRIPTION:
//
//  Returns the number of dimensions for a data item.
//
//  PARAMETERS:
//
//  pItem               Input.  Item in question.
//
//  RETURN VALUE:
//  -1 if bogus argument, or if the data item doesnt hold data which would
//     be the case for uninitialized properties.
//  0  if non array argument
//  n  Number of dimensions.  Currently only single dimension arrays are
//     supported.
//
//***************************************************************************

int GetNumDimensions(CBMOFDataItem * pItem)
{
   UNALIGNED unsigned long * pdwTemp;
   if(pItem == NULL)
      return -1;
   if(!(pItem->m_dwType & VT_ARRAY))
      return 0;
   if(pItem->m_pData == NULL)
      return -1;

   pdwTemp = (unsigned long *)pItem->m_pData;
   pdwTemp++;        // skip past total size
   return *pdwTemp;
}


//***************************************************************************
//
//  int GetNumElements
//
//  DESCRIPTION:
//
//  Gets the number of elements for an array dimension.  Note that 1 is the
//  first dimenstion.  Currently, only scalars and 1 dimensional arrays are
//  supported.
//
//  PARAMETERS:
//
//  pItem               Input.  Data item in question.
//  lDim                Input.  Dimension in question.  The most significent
//                      (and for now only) dimension is 0.
//
//  RETURN VALUE:
//
//  Number of array elements.  Note that scalars will return -1.
//
//***************************************************************************

int GetNumElements(CBMOFDataItem * pItem, long lDim)
{
   int iCnt; UNALIGNED DWORD * pdwTemp;
   int lNumDim = GetNumDimensions(pItem);
   if(lNumDim == -1 || lDim > lNumDim)
      return -1;
   pdwTemp = (UNALIGNED unsigned long *)pItem->m_pData;
   pdwTemp++;                          // skip total size
   pdwTemp++;                          // skip number of dimensions
   for(iCnt = 0; iCnt < lDim; iCnt++)
      pdwTemp++;
   return *pdwTemp;
}


//***************************************************************************
//
//  BYTE * GetDataElemPtr
//
//  DESCRIPTION:
//
//  Used to get the pointer to a particular data element.  Note that this is
//  usually used internally.
//
//  PARAMETERS:
//
//  pItem               Input.  Data item to use.
//  plDims              Input.  Pointer to array of dimension values.  Note
//                      that currenly only a single dimension is supported.
//  vtSimple            Input.  Variant type of the data with the VT_ARRAY
//                      and VT_BYREF bits cleared.
//
//  RETURN VALUE:
//

//  pointer to the data.
//***************************************************************************

BYTE * GetDataElemPtr(CBMOFDataItem * pItem, long * plDims, DWORD vtSimple)
{
   int iNumDim;
   DWORD dwTotalDataSize;
   BYTE * pEndOfData;
   UNALIGNED DWORD * pdwCurr;
   UNALIGNED DWORD * pdwCurrObj;
   BYTE * pRow;
   int iCnt;

   // first check the number of dimensions.

   iNumDim = GetNumDimensions(pItem);
   if(iNumDim == -1)
      return NULL;
   if(iNumDim == 0)           // simple case of scalar argument
      return pItem->m_pData;

   // for arrays, the data block starts off with this form,
   // dwtotalsize, dwNumDimenstions, dwMostSigDimension... dwLeastSigDimension
   // Since currently only 1 dimensional arrays are supported, a 5 element
   // array would start with
   // dwSize, 1, 5

   pdwCurr = (UNALIGNED DWORD *)pItem->m_pData;
   dwTotalDataSize = *pdwCurr;
   pEndOfData = pItem->m_pData + dwTotalDataSize;
   pdwCurr+= 2;      // skip to dimension list
   pdwCurr += iNumDim;  // skip of dimension sizes.

   while((BYTE *)pdwCurr < pEndOfData)
   {
      // Each row has the format
      // dwSizeOfRow, MostSigDimension ... dwLeastSignificentDimension+1,data
      // For a one dimensional array, it would just be
      // dwSizeOfRow, data


      DWORD dwRowSize = *pdwCurr;

      // test if this row is ok.  Each row of data will have
      // a set of Indicies for each higher dimension.

      for(iCnt = 0; iCnt < iNumDim-1; iCnt++)
      {
         UNALIGNED DWORD * pRowInd = pdwCurr +1 + iCnt;
         if((long)*pRowInd != plDims[iCnt])
            break;

      }
      if(iCnt >= iNumDim -1)
      {
         break;                  // found the row.
      }

      // go to the next row

      pdwCurr = (UNALIGNED DWORD *)((BYTE *)pdwCurr + dwRowSize);

   }

   if((BYTE *)pdwCurr >= pEndOfData)
      return NULL;

   pRow = (BYTE *)(pdwCurr + 1 + iNumDim -1);
   for(iCnt = 0; iCnt < plDims[iNumDim-1]; iCnt++)
   {
      if(vtSimple == VT_BSTR)
         pRow += 2*(wmi_unaligned_wcslen((WCHAR *)pRow)+1);
      else if(vtSimple == VT_EMBEDDED_OBJECT)
      {
         // Each embedded object starts off with its own size

         pdwCurrObj = (DWORD *)pRow;
         pRow += *pdwCurrObj;

      }
      else
         pRow += iTypeSize(vtSimple);
   }

   return pRow;

}


//***************************************************************************
//
//  int GetData
//
//  DESCRIPTION:
//
//
//  PARAMETERS:
//
//  pItem               Input.  Data item to use.
//  pRet                Input/Output.  Pointer to where the data is to be
//                      copied.  For simple data, such as ints, this can just
//                      be a pointer to an int.  For BSTRs, or embedded
//                      objects, this is treated as a pointer to a pointer
//                      and it is the responsibility of the caller to free
//                      the strings via BMOFFree().
//  plDims              Input.  Pointer to array of dimension values.  Note
//                      that currenly only a single dimension is supported.
//                      The first element in any dimension is 0.
//  RETURN VALUE:
//
//  Number of bytes of data.
//***************************************************************************

int GetData(CBMOFDataItem * pItem, BYTE * pRet, long * plDims)
{
   DWORD dwSimple;
   BYTE * pData;
   CBMOFObj * pObjRet = NULL;
   dwSimple = pItem->m_dwType &~ VT_ARRAY &~VT_BYREF;
   pData = GetDataElemPtr(pItem, plDims, dwSimple);
   if(pData == NULL)
      return 0;
   if(dwSimple == VT_BSTR)
   {

      // For strings, a new WCHAR buffer is returned.  Note that
      // SysAllocString isnt used so as to avoid any Ole dependencies.

      BYTE * pStr;
      DWORD dwSize = 2*(wmi_unaligned_wcslen((WCHAR *)pData)+1);

      pStr = BMOFAlloc(dwSize);
      if(pStr == NULL)
      		return 0;
      memcpy((void *)pRet, &pStr, sizeof(void *));
      wmi_unaligned_wcscpy((WCHAR *)pStr, (WCHAR *)pData);
      return dwSize;
   }
   else if(dwSimple == VT_EMBEDDED_OBJECT)
   {

      // This is the embedded object case.
      pObjRet = CreateObj((UNALIGNED WBEM_Object *)pData);
      memcpy((void *)pRet, &pObjRet, sizeof(void *));

      return sizeof(void *);
   }
   else
   {
      memcpy((void *)pRet, (void *)pData, iTypeSize(dwSimple));
      return iTypeSize(dwSimple);
   }
}