////////////////////////////////////////////////////////////////////////////////
//
// stmio.c
//
// Property Set Stream I/O
//
// Change history:
//
// Date         Who             What
// --------------------------------------------------------------------------
// 07/30/94     B. Wentz        Created file
//
////////////////////////////////////////////////////////////////////////////////

#include "priv.h"
#pragma hdrstop



 // Globals from stmio.h

  // Max size of the write buffer.
#define BUFMAX  2048

  // Internal prototypes
static BOOL PASCAL FLpstmReadVT_VECTOR (LPSTREAM lpStm,
                                    DWORD irglpUnk,
                                    LPPROPIDTYPELP rglpUnk);


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadVT_LPTSTR
//
// Purpose:
//  Read a VT_LPSTR from the stream.
//  RickTu: also added conversion to UNICODE when appropriate
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmReadVT_LPTSTR
   (LPSTREAM   lpStm,            // Pointer to stream
    LPTSTR FAR *lplpstz,         // Pointer to string
    BOOL (*lpfnFCPConvert)(LPTSTR, DWORD, DWORD, BOOL),   // Code page converter
    DWORD      dwType)           // Indicates the type of information being read
{
    DWORD cch,cb;
    DWORD cbT;
    HRESULT hr;
    LPGLOBALS lpg = (LPGLOBALS)TlsGetValue( g_tls );

    //
    // Get size of string
    //
    if (!SUCCEEDED (lpStm->lpVtbl->Read (lpStm, &cch, sizeof (DWORD), NULL)))
    {
        return FALSE;
    }

    //
    // Check the degenerative case...
    //

    *lplpstz = NULL;
    if (cch == 0)
    {
        return TRUE;
    }


    //
    // For wide (UNICODE) strings, the size if not a cb, but a cw!
    //
    cb = cch;
    if (dwType == VT_LPWSTR)
    {
        cb *= sizeof(WCHAR);
    }

    //
    // If we're dealing with an "Office97" file, EVERYTHING is
    // 32-bit aligned.
    //
    if (lpg->gfUnicode)
    {
        cb += CBALIGN32 (cb);
    }

    cbT = cb+2*sizeof(DWORD);
    cbT += CBALIGN32 (cbT);


    if (((*lplpstz) = (TCHAR *) PvMemAlloc(cbT)) == NULL)
    {
        goto Fail;
    }

    CBBUF (*lplpstz) = cbT;
    CCHSTR (*lplpstz) = cch;

    hr = lpStm->lpVtbl->Read (lpStm, PSTR (*lplpstz), cb, NULL);
    if (!SUCCEEDED(hr))
    {
        DebugHr(hr);
        goto Fail;
    }

#ifdef UNICODE
    if (dwType == VT_LPSTR)
    {
        LPWSTR wszTmp;

        //
        // refigure size
        //
        cbT = (cch*sizeof(WCHAR)) + (2*sizeof(DWORD));
        cbT += CBALIGN32 (cbT);
        wszTmp = (LPWSTR) PvMemAlloc(cbT);

        if (wszTmp)
        {
            CBBUF(wszTmp) = cbT;
            CCHSTR(wszTmp) = cch;
            MultiByteToWideChar( lpg->gdwCurrentCP, 0,
                    PSTR(*lplpstz),
                    -1,
                    (LPWSTR)PSTR(wszTmp),
                    cch );
            VFreeMemP(*lplpstz,cbT);
            *lplpstz = wszTmp;
        }
        else
        {
            goto Fail;
        }

    }
#else
    if (dwType == VT_LPWSTR)
    {
        LPSTR szTmp;

        //
        // refigure size
        //
        cbT = (cch+ (2*sizeof(DWORD));
        cbT += CBALIGN32 (cbT);
        szTmp = (LPSTR) PvMemAlloc(cbT);

        if (szTmp)
        {
            CBBUF(szTmp) = cbT;
            CCHSTR(szTmp) = cch;
            WideCharToMultiByte( lpg->gdwCurrentCP, 0,
                    (LPWSTR)PSTR(*lplpstz),
                    -1,
                    (LPSTR)PSTR(wszTmp),
                    cch,
                    NULL,
                    NULL );
            VFreeMemP(*lplpstz,cbT);
            *lplpstz = szTmp;
        }
        else
        {
            goto Fail;
        }
    }

    if (lpg->gdwFileCP != lpg->gdwCurrentCP)
    {
        if (!(*lpfnFCPConvert)(PSTR (*lplpstz), lpg->gdwFileCP, lpg->gdwCurrentCP, lpg->gfMacintosh))
        {
            DebugSz ("Code page conversion failed");
            goto Fail;
        }
    }
#endif

    return TRUE;

Fail:
    if (*lplpstz != NULL)
    {
        VFreeMemP(*lplpstz, cbT);
    }
    return FALSE;

} // FLpstmReadVT_LPTSTR


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_LPTSTR
//
// Purpose:
//  Write a VT_LPSTR to the stream.
//  Ricktu: also added converstion FROM UNICODE when appropriate
//
// Notes: This does the NULL checking, it is OK to pass a null lpstz.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_LPTSTR
   (LPSTREAM lpStm,             // Stream pointer
    LPTSTR lpstz,               // String to write
    BOOL fAlign,                // Indicates the string should be 32-bit aligned
    DWORD dwType)               // Type indicator to write out
{
    DWORD dw,cch;
    DWORD cb;
    BOOL f = FALSE;
    LPGLOBALS lpg = (LPGLOBALS)TlsGetValue( g_tls );


    if (lpstz == NULL)
    {
        return TRUE;
    }

    //
    // Temporarily stuff the property type for the VT_LPSTR in the
    // first dword of the string to save an extra write.
    //
    cb = CBBUF (lpstz);
    CBBUF (lpstz) = dwType;

    //
    // Calculate the padding needed to align on boundary.
    //
    cch = CCHSTR (lpstz);
    if (dwType == VT_LPWSTR)
    {
        dw = cch*sizeof(WCHAR);
    }
    else
    {
        dw = cch;
    }
    dw += 2*sizeof(DWORD);

    if (fAlign)
    {
        dw += CBALIGN32 (dw);
    }
#ifdef UNICODE
    // Need to convert from UNICODE to ANSI before writing to the disk!
    if (dwType == VT_LPSTR)
    {
        LPSTR szStr = PvMemAlloc( dw );

        if (szStr)
        {
            WideCharToMultiByte( lpg->gdwCurrentCP, 0,
                    (LPTSTR)PSTR(lpstz),
                    CCHSTR(lpstz),
                    (LPSTR)PSTR(szStr),
                    CCHSTR(lpstz),
                    NULL,
                    NULL );
            CBBUF(szStr) = dwType;
            CCHSTR(szStr) = (DWORD)CCHSTR(lpstz);
            f = FLpstmWrite (lpStm, szStr, dw );
            VFreeMemP(szStr,dw);
      }
      else
      {
            f = FALSE;
            goto Exit;
      }
    }
    else
#endif
        f = FLpstmWrite (lpStm, &(lpstz[0]), dw);

Exit:
    CBBUF (lpstz) = cb;           // Put the buffer length back

    return(f);

} // FLpstmWriteVT_LPTSTR


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_FILETIME
//
// Purpose:
//  To write a VT_FILETIME to the given stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_FILETIME
  (LPSTREAM lpStm,                      // The stream to write to
   LPFILETIME lpFt)                     // The file time to write
{
  BYTE rgb[sizeof(DWORD)+sizeof(FILETIME)];
  BOOL f;

  *(DWORD *) &(rgb[0]) = VT_FILETIME;
  *(LPFILETIME) &(rgb[sizeof(DWORD)]) = *lpFt;

    // Write out the type and filetime
  f = FLpstmWrite (lpStm, rgb, sizeof(rgb));

    // Should be no need to 32-bit align since this already is.
  AssertSz (((sizeof(FILETIME)%4) == 0), "Huh? FILETIME size no longer multiple of 4");
  return(f);

} // FLpstmWriteVT_FILETIME


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_I4
//
// Purpose:
//  Write a VT_I4 to the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_I4
  (LPSTREAM lpStm,                      // Stream to write to
   DWORD dwI4)                          // The VT_I4 to write
{
  DWORD rgdw[2];

  rgdw[0] = VT_I4;
  rgdw[1] = dwI4;

    // VT_I4
  return(FLpstmWrite (lpStm, &(rgdw[0]), sizeof(rgdw)));

} // FLpstmWriteVT_I4


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadVT_CF
//
// Purpose:
//  Read a VT_CF from the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmReadVT_CF
  (LPSTREAM lpStm,
   LPSINAIL lpSINail)
{
  HRESULT hr;
  DWORD cftag;
  DWORD cbData;
  DWORD cbTag;

  hr = lpStm->lpVtbl->Read (lpStm, &cbData, sizeof (DWORD), NULL);
  if (!SUCCEEDED (hr))
  {
    DebugHr (hr);
    return FALSE;
  }

  hr = lpStm->lpVtbl->Read (lpStm, &cftag, sizeof (DWORD), NULL);
  if (!SUCCEEDED (hr))
  {
    DebugHr (hr);
    return FALSE;
  }

  cbTag = CbThumbNailFMTID(cftag);
  cbData = cbData-4-cbTag;

  if (cbData != 0)
      {
      lpSINail->pbData = PvMemAlloc(cbData);
      if (lpSINail->pbData == NULL)
         return(FALSE);
      }
  else
    lpSINail->pbData = NULL;


  if (cbTag != 0)
      {
      lpSINail->pbFMTID = PvMemAlloc(cbTag);
      if (lpSINail->pbFMTID == NULL)
         {
         if (cbData != 0)
            VFreeMemP(lpSINail->pbData, cbData);
         return(FALSE);
         }
      }
  else
    lpSINail->pbFMTID = NULL;


  lpSINail->cbData = cbData;
  lpSINail->cftag = cftag;

  if (cbTag != 0)
     {
     hr = lpStm->lpVtbl->Read (lpStm, lpSINail->pbFMTID, cbTag, NULL);
     if (!SUCCEEDED (hr))
       goto Error;
     }

  if (cbData != 0)
     {
     hr = lpStm->lpVtbl->Read (lpStm, lpSINail->pbData, cbData, NULL);
     if (!SUCCEEDED (hr))
        {
Error:
        DebugHr (hr);
        if (cbTag != 0)
           VFreeMemP(lpSINail->pbFMTID,cbTag);
        if (cbData != 0)
           VFreeMemP(lpSINail->pbData,cbData);
        return FALSE;
        }
     }

  return TRUE;

} // FLpstmReadVT_CF


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_CF
//
// Purpose:
//  Writes a VT_VF to the given stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_CF
  (LPSTREAM lpStm,                      // Stream to write to
   LPSINAIL lpSINail)                   // VT_CF to write out
{
  DWORD rgdw[3];
  DWORD cb;

        rgdw[0] = VT_CF;
   cb = CbThumbNailFMTID(lpSINail->cftag);
   // Bytes that follow, see page 875 in OLE 2 Programmer's Ref. Vol.1
        rgdw[1] = lpSINail->cbData+4+cb;
        rgdw[2] = lpSINail->cftag;

    // Write the type, size and clip fmt.
  if (!FLpstmWrite (lpStm, rgdw, sizeof(rgdw)))
                return FALSE;

  // Write out FMTID
  // Should be no need to 32-bit align since this already is.
  AssertSz (((cb%4) == 0), "Huh? cbFMTID size no longer multiple of 4");
  if (!FLpstmWrite (lpStm, lpSINail->pbFMTID, cb))
                return FALSE;

  // Write out the data
  if (lpSINail->cbData)    // Some PPT files have 0 data.  Are they old files?
     {
     cb = lpSINail->cbData;
     cb += CBALIGN32(cb);
     if (!FLpstmWrite (lpStm, lpSINail->pbData, cb))
             return FALSE;
     }

  return TRUE;

} // FLpstmWriteVT_CF


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_I2
//
// Purpose:
//  Writes a VT_I2 to the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_I2
  (LPSTREAM lpStm,                        // Pointer to stream
   WORD w)                                // Word to write out
{
  BYTE rgb[2*sizeof(DWORD)];

  *(DWORD *) &rgb[0] = VT_I2;           // The type
  *(DWORD *) &rgb[sizeof(DWORD)] = 0;   // clear out the space
  *(WORD *)  &rgb[sizeof(DWORD)] = w;   // The value itself, but on a 32-bit boundary

  return(FLpstmWrite (lpStm, rgb, sizeof(rgb)));

} // FLpstmWriteVT_I2


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadVT_I2
//
// Purpose:
//  Reads a VT_I2 from the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmReadVT_I2
  (LPSTREAM lpStm,                        // Pointer to stream
   WORD *pw)                              // Pointer to word to read data into
{
  HRESULT hr;
  WORD w;

    // Read the word itself
  hr = lpStm->lpVtbl->Read (lpStm, pw, sizeof(WORD), NULL);
  if (!SUCCEEDED (hr))
  {
    DebugHr (hr);
    return FALSE;
  }

    // Read the extra 2 byte padding
  lpStm->lpVtbl->Read (lpStm, &w, sizeof(WORD), NULL);
  if (!SUCCEEDED (hr))
  {
    DebugHr (hr);
    return FALSE;
  }

  return TRUE;

} // FLpstmReadVT_I2


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadVT_BOOL
//
// Purpose:
//  Read a VT_BOOL from the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmReadVT_BOOL
  (LPSTREAM lpStm,                      // Pointer to stream
   WORD *fBool)                         // Flag indicating value
{
  DWORD dw;
  HRESULT hr;

  hr = lpStm->lpVtbl->Read (lpStm, &dw, sizeof(DWORD), NULL);
  if (!SUCCEEDED (hr))
  {
    DebugHr (hr);
    return FALSE;
  }

  *fBool = (WORD) dw;

  return TRUE;

} // FLpstmReadVT_BOOL


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_BOOL
//
// Purpose:
//  Write a VT_BOOL to the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_BOOL
  (LPSTREAM lpStm,                      // Pointer to stream
   WORD fBool)                          // Flag indicating value
{
  DWORD rgdw[2];

  rgdw[0] = VT_BOOL;
  rgdw[1] = (DWORD) fBool;

    // Remember that Bools align on 32-bit boundary
  return(FLpstmWrite (lpStm, rgdw, sizeof(rgdw)));

} // FLpstmWriteVT_BOOL


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadVT_R8_DATE
//
// Purpose:
//  Read a VT_R8 or VT_DATE from the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmReadVT_R8_DATE
  (LPSTREAM lpStm,              // Pointer to stream
   NUM *pdbl)                 // double to read into
{
  HRESULT hr;

  hr = lpStm->lpVtbl->Read (lpStm, pdbl, sizeof(NUM), NULL);
  if (!SUCCEEDED (hr))
  {
    DebugHr (hr);
    return FALSE;
  }

  return TRUE;

} // FLpstmReadVT_R8_DATE


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_R8_DATE
//
// Purpose:
//  Write a VT_R8 or VT_DATE to the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_R8_DATE
  (LPSTREAM lpStm,              // Pointer to stream
   NUM *pdbl,                 // double or date to write out
   BOOL fDate)                  // Indicates if dbl is a date
{
  DWORD rgdw[3];

  rgdw[0] = (fDate) ? VT_DATE : VT_R8;
  *(NUM *) &(rgdw[1]) = *pdbl;

  return(FLpstmWrite (lpStm, &rgdw, sizeof(rgdw)));

} // FLpstmWriteVT_R8_DATE


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadVT_BLOB
//
// Purpose:
//  Read a VT_BLOB from the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmReadVT_BLOB
  (LPSTREAM lpStm,                      // Stream
   DWORD *pcb,                           // Number of bytes in blob
   BYTE FAR * FAR *ppbData)               // pointer to pointer to blob
{
  if (!SUCCEEDED (lpStm->lpVtbl->Read (lpStm, pcb, sizeof (DWORD), NULL)))
    return FALSE;

    // A blob of size 0 is legit.
  if (*pcb == 0)
  {
    *ppbData = NULL;
    return TRUE;
  }

  if ((*ppbData = PvMemAlloc(*pcb)) == NULL)
  {
// REVIEW: add alert
    return FALSE;
  }

  if (!SUCCEEDED (lpStm->lpVtbl->Read (lpStm, *ppbData, *pcb, NULL)))
  {
    VFreeMemP(*ppbData, *pcb);
    return FALSE;
  }

  return TRUE;

} // FLpstmReadVT_BLOB


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_BLOB
//
// Purpose:
//  Write a VT_BLOB to the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_BLOB
  (LPSTREAM lpStm,                              // Pointer to stream
   DWORD cb,                                    // Size of blob
   BYTE *bData)                                 // Pointer to blob data
{
  DWORD rgdw[2];

  rgdw[0] = VT_BLOB;
  rgdw[1] = cb;

  if (!FLpstmWrite (lpStm, &(rgdw[0]), 2*sizeof(DWORD)))
    return FALSE;

  return(FLpstmWrite (lpStm, bData, cb));

} // FLpstmWriteVT_BLOB


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadVT_CLSID
//
// Purpose:
//  Read a VT_CLSID from the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmReadVT_CLSID
  (LPSTREAM lpStm,                              // Pointer to stream
   CLSID *pClsId)                               // Pointer to CLSID
{
  HRESULT hr;

  hr = lpStm->lpVtbl->Read (lpStm, pClsId, sizeof(CLSID), NULL);
  if (!SUCCEEDED (hr))
  {
    DebugHr (hr);
    return FALSE;
  }

  return TRUE;

} // FLpstmReadVT_CLSID


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteVT_CLSID
//
// Purpose:
//  Write a VT_CLSID to the stream
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteVT_CLSID
  (LPSTREAM lpStm,
   CLSID *pClsId)
{
  DWORD rgdw[5];

  rgdw[0] = VT_CLSID;
  *(CLSID *) &(rgdw[1]) = *pClsId;

  return(FLpstmWrite (lpStm, rgdw, sizeof(rgdw)));

} // FLpstmWriteVT_CLSID


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadVT_VECTOR
//
// Purpose:
//  Reads a VT_VECTOR of unknown type in to the unknown data
//
////////////////////////////////////////////////////////////////////////////////
static BOOL PASCAL
FLpstmReadVT_VECTOR
  (LPSTREAM lpStm,                      // Pointer to stream
   DWORD irglpUnk,                      // Index of unknown data for vector
   LPPROPIDTYPELP rglpUnk)              // Array of unknowns
{

  // REVIEW: What is this?
  return TRUE;

} // FLpstmReadVT_VECTOR


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmReadUnknown
//
// Purpose:
//  Read in unknown data into the array
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmReadUnknown
  (LPSTREAM lpStm,
   DWORD dwType,                        // Type to read
   DWORD dwId,                          // The id being read
   DWORD *pirglpUnk,                    // Current index into rglpUnk
   LPPROPIDTYPELP rglpUnk)              // Array of unknowns

{
  HRESULT hr;
  TCHAR *lpstz;

  DebugSzdw ("Doh! Unknown PId %x in stream!", dwId);

  rglpUnk[*pirglpUnk].dwType = dwType;
  rglpUnk[*pirglpUnk].dwId = dwId;

  if (dwType & VT_VECTOR)
  {
    if (!FLpstmReadVT_VECTOR (lpStm, *pirglpUnk, rglpUnk))
    {
      DebugSz ("Failed reading VT_VECTOR");
      goto ReadFail;
    }
  }
  else
  {
    switch (dwType)
      {
      case VT_LPSTR           :
      case VT_BSTR            :
      case VT_LPWSTR          :
      case VT_STREAM          :
      case VT_STORAGE         :
      case VT_STREAMED_OBJECT :
      case VT_STORED_OBJECT   :
      case VT_BLOB_OBJECT     :
// BUGBUG: Fix to take codepage param.
        if (!FLpstmReadVT_LPTSTR (lpStm, &lpstz, NULL, dwType))
        {
          DebugSz ("Failed reading VT_LPSTR or similiar type");
          goto ReadFail;
        }
        rglpUnk[*pirglpUnk].dwSize = CBBUF (lpstz);
        rglpUnk[*pirglpUnk].lpvData = (void *) lpstz;
        break;

      case VT_FILETIME :
        rglpUnk[*pirglpUnk].dwSize = sizeof (FILETIME);
        rglpUnk[*pirglpUnk].lpvData = PvMemAlloc(sizeof (FILETIME));

        hr = lpStm->lpVtbl->Read (lpStm, rglpUnk[*pirglpUnk].lpvData, sizeof (FILETIME), NULL);
        if (!SUCCEEDED (hr))
        {
          DebugHr (hr);
          goto ReadFail;
        }
        break;

      case VT_I2 :
        rglpUnk[*pirglpUnk].dwSize = sizeof (WORD);
        if (!FLpstmReadVT_I2 (lpStm, (WORD *) &(rglpUnk[*pirglpUnk].lpvData)))
          goto ReadFail;
        break;

      case VT_I4 :
      case VT_R4 :
        rglpUnk[*pirglpUnk].dwSize = sizeof (DWORD);
        hr = lpStm->lpVtbl->Read (lpStm, &(rglpUnk[*pirglpUnk].lpvData), sizeof (DWORD), NULL);
        if (!SUCCEEDED (hr))
        {
          DebugHr (hr);
          goto ReadFail;
        }
        break;

      case VT_CF :
        rglpUnk[*pirglpUnk].lpvData = PvMemAlloc(sizeof(CLIPDATA));

        hr = lpStm->lpVtbl->Read (lpStm, rglpUnk[*pirglpUnk].lpvData, sizeof (DWORD), NULL);
        if (!SUCCEEDED (hr))
        {
          DebugHr (hr);
          goto ReadFail;
        }

        rglpUnk[*pirglpUnk].dwSize = sizeof (CLIPDATA) + rglpUnk[*pirglpUnk].dwSize;
        break;

      case VT_BOOL :
        rglpUnk[*pirglpUnk].dwSize = sizeof (DWORD);
        if (!FLpstmReadVT_BOOL (lpStm, (WORD *) &(rglpUnk[*pirglpUnk].lpvData)))
          goto ReadFail;
        break;

      case VT_R8   :
      case VT_CY   :
      case VT_I8   :
      case VT_DATE :

        if ((rglpUnk[*pirglpUnk].lpvData = PvMemAlloc(sizeof(NUM))) == NULL)
            goto ReadFail;
        rglpUnk[*pirglpUnk].dwSize = sizeof (NUM);

        if (!FLpstmReadVT_R8_DATE (lpStm, (NUM *)(rglpUnk[*pirglpUnk].lpvData)))
        {
          DebugSz ("Failed reading VT_R8 or VT_DATE");
          goto ReadFail;
        }
        break;

      case VT_BLOB :
        if (!FLpstmReadVT_BLOB (lpStm, &(rglpUnk[*pirglpUnk].dwSize),
                                  (BYTE **) &(rglpUnk[*pirglpUnk].lpvData)))
        {
          DebugSz ("Failed reading VT_BLOB or similiar type");
          goto ReadFail;
        }
        break;

      case VT_CLSID :
        rglpUnk[*pirglpUnk].dwSize = sizeof(CLSID);
        rglpUnk[*pirglpUnk].lpvData = PvMemAlloc(sizeof(CLSID));
        if (rglpUnk[*pirglpUnk].lpvData == NULL)
          goto ReadFail;
        if (!FLpstmReadVT_CLSID (lpStm, (CLSID *) rglpUnk[*pirglpUnk].lpvData))
                          goto ReadFail;
        break;
      case VT_EMPTY :
      case VT_NULL  :
        rglpUnk[*pirglpUnk].dwSize = 0;
        break;
    } // switch
  }

  (*pirglpUnk)++;
  return TRUE;

ReadFail :

  return FALSE;

} // FLpstmReadUnknown


////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWriteUnknowns
//
// Purpose:
//  Write out the unknown data in the array.
//
// Note:
//  Some of the stuff here is special-cased, and doesn't use the normal
//  write routines, because some of the types we don't normally use are
//  easier to lump together here.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWriteUnknowns
  (LPSTREAM lpStm,
   DWORD dwcUnk,
   LPPROPIDTYPELP rglpUnk)
{
  DWORD irg;
  DWORD rgdw[3];
  BOOL f;

// REVIEW: Need to update to do all of the types.....

  for (irg = 0; irg < dwcUnk; irg++)
  {
    switch (rglpUnk[irg].dwType)
    {
      case VT_LPSTR           :
      case VT_BSTR            :
      case VT_LPWSTR          :
      case VT_STREAM          :
      case VT_STORAGE         :
      case VT_STREAMED_OBJECT :
      case VT_STORED_OBJECT   :
      case VT_BLOB_OBJECT     :
        f = FLpstmWriteVT_LPTSTR (lpStm, (LPTSTR) rglpUnk[irg].lpvData, TRUE, rglpUnk[irg].dwType);
        break;
      case VT_I2 :
        f = FLpstmWriteVT_I2 (lpStm, (WORD) rglpUnk[irg].lpvData);
        break;
      case VT_I4 :
      case VT_R4 :
        rgdw[0] = rglpUnk[irg].dwType;
        rgdw[1] = (DWORD) rglpUnk[irg].lpvData;
        f = FLpstmWrite (lpStm, rgdw, 2*sizeof(DWORD));
        break;
      case VT_FILETIME :
        f = FLpstmWriteVT_FILETIME (lpStm, (LPFILETIME) rglpUnk[irg].lpvData);
        break;
      case VT_CF :
        f = FLpstmWriteVT_CF (lpStm, (LPSINAIL) rglpUnk[irg].lpvData);
        break;
      case VT_BOOL :
        f = FLpstmWriteVT_BOOL (lpStm, (WORD) rglpUnk[irg].lpvData);
        break;
      case VT_R8   :
      case VT_CY   :
      case VT_I8   :
      case VT_DATE :
        rgdw[0] = rglpUnk[irg].dwType;
        *(NUM *) &(rgdw[1]) = *(NUM *) rglpUnk[irg].lpvData;
        f = FLpstmWrite (lpStm, rgdw, sizeof(rgdw));
        break;
      case VT_BLOB :
        f = FLpstmWriteVT_BLOB (lpStm, rglpUnk[irg].dwSize, rglpUnk[irg].lpvData);
        break;
      case VT_CLSID :
        f = FLpstmWriteVT_CLSID (lpStm, (CLSID *) rglpUnk[irg].lpvData);
        break;
      case VT_EMPTY :
      case VT_NULL  :
        f = FLpstmWrite (lpStm, &(rglpUnk[irg].dwType), sizeof(DWORD));
        break;
      default:
        AssertSz (0, "Doh! Unknown type!");
    } // switch

    if (!f)
      return FALSE;
  }

  return TRUE;

} // FLpstmWriteUnknowns

static BYTE *lpOlePropBuf;              // Buffer to hold data
static DWORD cbOlePropBuf;              // Current cb into buffer
static ULARGE_INTEGER cbTotWritten;    // Total count of bytes written to the stream

////////////////////////////////////////////////////////////////////////////////
//
// VAllocWriteBuf
//
// Purpose:
//              Allocate a buffer to hold all the data that will eventually get written
//              out to the stream.
//
////////////////////////////////////////////////////////////////////////////////
void VAllocWriteBuf(void)
{
   // No need to check to see if this works since all other places
   // that access lpOlePropBuf need to check for NULL anyway.
    lpOlePropBuf = PvMemAlloc(BUFMAX);
    cbOlePropBuf = 0;
    cbTotWritten.LowPart = 0;
    cbTotWritten.HighPart = 0;
}

// Free the buffer
////////////////////////////////////////////////////////////////////////////////
//
// VFreeWriteBuf
//
// Purpose:
//              Free the buffer that holds all the data that will eventually get written
//              out to the stream.
//
////////////////////////////////////////////////////////////////////////////////
void VFreeWriteBuf(void)
{
  if(lpOlePropBuf!=NULL)
   {
    VFreeMemP(lpOlePropBuf, BUFMAX);
    lpOlePropBuf = NULL;
   }
}

////////////////////////////////////////////////////////////////////////////////
//
// FFlushWriteBuf
//
// Purpose:
//              Actually write data to the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL FFlushWriteBuf(LPSTREAM lpStm)
{
HRESULT hr;
  if ((lpOlePropBuf != NULL) && (cbOlePropBuf))
    {
      hr = lpStm->lpVtbl->Write (lpStm, lpOlePropBuf, cbOlePropBuf, NULL);
      if (!SUCCEEDED (hr))
      {
        DebugHr (hr);
        return fFalse;
      }
      cbOlePropBuf = 0;
    }
   return fTrue;
}

//
// VSetRealStmSize
//
// Sets the stream size to be the actual count of bytes written.
//
// This should only be done if we are doing a normal save, i.e. not if
// we are doing a Simple Doc File save.
//
void VSetRealStmSize(LPSTREAM lpStm)
{
   lpStm->lpVtbl->SetSize(lpStm, cbTotWritten);
   cbTotWritten.LowPart = 0;
   cbTotWritten.HighPart = 0;
}
////////////////////////////////////////////////////////////////////////////////
//
// FLpstmWrite
//
// Purpose:
//  Writes data to the buffer, eventually writing to the stream.
//
//  In low memory conditions where lpfnAlloc fails, this will write
//  directly to the stream.
//
////////////////////////////////////////////////////////////////////////////////
BOOL PASCAL
FLpstmWrite
  (LPSTREAM lpStm,                      // Pointer to stream
   LPVOID lpv,                          // Pointer to data to write
   DWORD cb)                                                               // Size of lpv
{
  HRESULT hr;

  Assert ((lpStm != NULL));
  Assert ((lpv != NULL));
  Assert ((cb > 0));

  if (lpOlePropBuf != NULL)
  {
      // We're not going to bother with splitting data across a buffer,
      // we'll just flush the current buffer if the new data would fill it.
    if (cbOlePropBuf+cb > BUFMAX)
    {
      hr = lpStm->lpVtbl->Write (lpStm, lpOlePropBuf, cbOlePropBuf, NULL);

        // We're not going to allow a retry, so reset the buffer
        // regardless of if we fail.
      cbOlePropBuf = 0;

      if (!SUCCEEDED (hr))
      {
        DebugHr (hr);
        return FALSE;
      }
    }

    if (cb < BUFMAX)
    {
      PbMemCopy ((lpOlePropBuf+cbOlePropBuf), lpv, cb);
      cbOlePropBuf += cb;
    }
    else
    {
        // The size of the data is bigger than our buffer, so write
        // the data directly.
      hr = lpStm->lpVtbl->Write (lpStm, lpv, cb, NULL);
      if (!SUCCEEDED (hr))
      {
        DebugHr (hr);
        return FALSE;
      }
    }
  }
  else
  {
    DebugSz ("Either memory is really low or the buffer wasn't init'd by the client");

      // Low memory save case (or someone forgot to init the buffer)
    hr = lpStm->lpVtbl->Write (lpStm, lpv, cb, NULL);
    if (!SUCCEEDED (hr))
    {
      DebugHr (hr);
      return FALSE;
    }
  }

  cbTotWritten.LowPart += cb;
  return TRUE;

} // FLpstmWrite