ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

stg_prop.c
Go to the documentation of this file.
00001 /*
00002  * Compound Storage (32 bit version)
00003  * Storage implementation
00004  *
00005  * This file contains the compound file implementation
00006  * of the storage interface.
00007  *
00008  * Copyright 1999 Francis Beaudet
00009  * Copyright 1999 Sylvain St-Germain
00010  * Copyright 1999 Thuy Nguyen
00011  * Copyright 2005 Mike McCormack
00012  * Copyright 2005 Juan Lang
00013  *
00014  * This library is free software; you can redistribute it and/or
00015  * modify it under the terms of the GNU Lesser General Public
00016  * License as published by the Free Software Foundation; either
00017  * version 2.1 of the License, or (at your option) any later version.
00018  *
00019  * This library is distributed in the hope that it will be useful,
00020  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00022  * Lesser General Public License for more details.
00023  *
00024  * You should have received a copy of the GNU Lesser General Public
00025  * License along with this library; if not, write to the Free Software
00026  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00027  *
00028  * TODO:
00029  * - I don't honor the maximum property set size.
00030  * - Certain bogus files could result in reading past the end of a buffer.
00031  * - Mac-generated files won't be read correctly, even if they're little
00032  *   endian, because I disregard whether the generator was a Mac.  This means
00033  *   strings will probably be munged (as I don't understand Mac scripts.)
00034  * - Not all PROPVARIANT types are supported.
00035  * - User defined properties are not supported, see comment in
00036  *   PropertyStorage_ReadFromStream
00037  */
00038 
00039 #include <assert.h>
00040 #include <stdarg.h>
00041 #include <stdio.h>
00042 #include <stdlib.h>
00043 #include <string.h>
00044 
00045 #define COBJMACROS
00046 #define NONAMELESSUNION
00047 #define NONAMELESSSTRUCT
00048 
00049 #include "windef.h"
00050 #include "winbase.h"
00051 #include "winnls.h"
00052 #include "winuser.h"
00053 #include "wine/unicode.h"
00054 #include "wine/debug.h"
00055 #include "dictionary.h"
00056 #include "storage32.h"
00057 #include "enumx.h"
00058 
00059 WINE_DEFAULT_DEBUG_CHANNEL(storage);
00060 
00061 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
00062 {
00063     return (StorageImpl *)((char*)iface - FIELD_OFFSET(StorageImpl, base.pssVtbl));
00064 }
00065 
00066 /* These are documented in MSDN,
00067  * but they don't seem to be in any header file.
00068  */
00069 #define PROPSETHDR_BYTEORDER_MAGIC      0xfffe
00070 #define PROPSETHDR_OSVER_KIND_WIN16     0
00071 #define PROPSETHDR_OSVER_KIND_MAC       1
00072 #define PROPSETHDR_OSVER_KIND_WIN32     2
00073 
00074 #define CP_UNICODE 1200
00075 
00076 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
00077 
00078 #define CFTAG_WINDOWS   (-1L)
00079 #define CFTAG_MACINTOSH (-2L)
00080 #define CFTAG_FMTID     (-3L)
00081 #define CFTAG_NODATA      0L
00082 
00083 typedef struct tagPROPERTYSETHEADER
00084 {
00085     WORD  wByteOrder; /* always 0xfffe */
00086     WORD  wFormat;    /* can be zero or one */
00087     DWORD dwOSVer;    /* OS version of originating system */
00088     CLSID clsid;      /* application CLSID */
00089     DWORD reserved;   /* always 1 */
00090 } PROPERTYSETHEADER;
00091 
00092 typedef struct tagFORMATIDOFFSET
00093 {
00094     FMTID fmtid;
00095     DWORD dwOffset; /* from beginning of stream */
00096 } FORMATIDOFFSET;
00097 
00098 typedef struct tagPROPERTYSECTIONHEADER
00099 {
00100     DWORD cbSection;
00101     DWORD cProperties;
00102 } PROPERTYSECTIONHEADER;
00103 
00104 typedef struct tagPROPERTYIDOFFSET
00105 {
00106     DWORD propid;
00107     DWORD dwOffset; /* from beginning of section */
00108 } PROPERTYIDOFFSET;
00109 
00110 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
00111 
00112 /* Initializes the property storage from the stream (and undoes any uncommitted
00113  * changes in the process.)  Returns an error if there is an error reading or
00114  * if the stream format doesn't match what's expected.
00115  */
00116 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
00117 
00118 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
00119 
00120 /* Creates the dictionaries used by the property storage.  If successful, all
00121  * the dictionaries have been created.  If failed, none has been.  (This makes
00122  * it a bit easier to deal with destroying them.)
00123  */
00124 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
00125 
00126 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
00127 
00128 /* Copies from propvar to prop.  If propvar's type is VT_LPSTR, copies the
00129  * string using PropertyStorage_StringCopy.
00130  */
00131 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
00132  const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
00133 
00134 /* Copies the string src, which is encoded using code page srcCP, and returns
00135  * it in *dst, in the code page specified by targetCP.  The returned string is
00136  * allocated using CoTaskMemAlloc.
00137  * If srcCP is CP_UNICODE, src is in fact an LPCWSTR.  Similarly, if targetCP
00138  * is CP_UNICODE, the returned string is in fact an LPWSTR.
00139  * Returns S_OK on success, something else on failure.
00140  */
00141 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
00142  LCID targetCP);
00143 
00144 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
00145 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl;
00146 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl;
00147 static HRESULT create_EnumSTATPROPSETSTG(StorageImpl *, IEnumSTATPROPSETSTG**);
00148 static HRESULT create_EnumSTATPROPSTG(PropertyStorage_impl *, IEnumSTATPROPSTG**);
00149 
00150 /***********************************************************************
00151  * Implementation of IPropertyStorage
00152  */
00153 struct tagPropertyStorage_impl
00154 {
00155     IPropertyStorage IPropertyStorage_iface;
00156     LONG ref;
00157     CRITICAL_SECTION cs;
00158     IStream *stm;
00159     BOOL  dirty;
00160     FMTID fmtid;
00161     CLSID clsid;
00162     WORD  format;
00163     DWORD originatorOS;
00164     DWORD grfFlags;
00165     DWORD grfMode;
00166     UINT  codePage;
00167     LCID  locale;
00168     PROPID highestProp;
00169     struct dictionary *name_to_propid;
00170     struct dictionary *propid_to_name;
00171     struct dictionary *propid_to_prop;
00172 };
00173 
00174 static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface)
00175 {
00176     return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface);
00177 }
00178 
00179 /************************************************************************
00180  * IPropertyStorage_fnQueryInterface (IPropertyStorage)
00181  */
00182 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
00183     IPropertyStorage *iface,
00184     REFIID riid,
00185     void** ppvObject)
00186 {
00187     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00188 
00189     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
00190 
00191     if (!ppvObject)
00192         return E_INVALIDARG;
00193 
00194     *ppvObject = 0;
00195 
00196     if (IsEqualGUID(&IID_IUnknown, riid) ||
00197         IsEqualGUID(&IID_IPropertyStorage, riid))
00198     {
00199         IPropertyStorage_AddRef(iface);
00200         *ppvObject = iface;
00201         return S_OK;
00202     }
00203 
00204     return E_NOINTERFACE;
00205 }
00206 
00207 /************************************************************************
00208  * IPropertyStorage_fnAddRef (IPropertyStorage)
00209  */
00210 static ULONG WINAPI IPropertyStorage_fnAddRef(
00211     IPropertyStorage *iface)
00212 {
00213     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00214     return InterlockedIncrement(&This->ref);
00215 }
00216 
00217 /************************************************************************
00218  * IPropertyStorage_fnRelease (IPropertyStorage)
00219  */
00220 static ULONG WINAPI IPropertyStorage_fnRelease(
00221     IPropertyStorage *iface)
00222 {
00223     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00224     ULONG ref;
00225 
00226     ref = InterlockedDecrement(&This->ref);
00227     if (ref == 0)
00228     {
00229         TRACE("Destroying %p\n", This);
00230         if (This->dirty)
00231             IPropertyStorage_Commit(iface, STGC_DEFAULT);
00232         IStream_Release(This->stm);
00233         This->cs.DebugInfo->Spare[0] = 0;
00234         DeleteCriticalSection(&This->cs);
00235         PropertyStorage_DestroyDictionaries(This);
00236         HeapFree(GetProcessHeap(), 0, This);
00237     }
00238     return ref;
00239 }
00240 
00241 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
00242  DWORD propid)
00243 {
00244     PROPVARIANT *ret = NULL;
00245 
00246     dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret);
00247     TRACE("returning %p\n", ret);
00248     return ret;
00249 }
00250 
00251 /* Returns NULL if name is NULL. */
00252 static PROPVARIANT *PropertyStorage_FindPropertyByName(
00253  PropertyStorage_impl *This, LPCWSTR name)
00254 {
00255     PROPVARIANT *ret = NULL;
00256     void *propid;
00257 
00258     if (!name)
00259         return NULL;
00260     if (This->codePage == CP_UNICODE)
00261     {
00262         if (dictionary_find(This->name_to_propid, name, &propid))
00263             ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
00264     }
00265     else
00266     {
00267         LPSTR ansiName;
00268         HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
00269          &ansiName, This->codePage);
00270 
00271         if (SUCCEEDED(hr))
00272         {
00273             if (dictionary_find(This->name_to_propid, ansiName, &propid))
00274                 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
00275             CoTaskMemFree(ansiName);
00276         }
00277     }
00278     TRACE("returning %p\n", ret);
00279     return ret;
00280 }
00281 
00282 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
00283  DWORD propid)
00284 {
00285     LPWSTR ret = NULL;
00286 
00287     dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret);
00288     TRACE("returning %p\n", ret);
00289     return ret;
00290 }
00291 
00292 /************************************************************************
00293  * IPropertyStorage_fnReadMultiple (IPropertyStorage)
00294  */
00295 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
00296     IPropertyStorage* iface,
00297     ULONG cpspec,
00298     const PROPSPEC rgpspec[],
00299     PROPVARIANT rgpropvar[])
00300 {
00301     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00302     HRESULT hr = S_OK;
00303     ULONG i;
00304 
00305     TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
00306 
00307     if (!cpspec)
00308         return S_FALSE;
00309     if (!rgpspec || !rgpropvar)
00310         return E_INVALIDARG;
00311     EnterCriticalSection(&This->cs);
00312     for (i = 0; i < cpspec; i++)
00313     {
00314         PropVariantInit(&rgpropvar[i]);
00315         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
00316         {
00317             PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
00318              rgpspec[i].u.lpwstr);
00319 
00320             if (prop)
00321                 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
00322                  This->codePage);
00323         }
00324         else
00325         {
00326             switch (rgpspec[i].u.propid)
00327             {
00328                 case PID_CODEPAGE:
00329                     rgpropvar[i].vt = VT_I2;
00330                     rgpropvar[i].u.iVal = This->codePage;
00331                     break;
00332                 case PID_LOCALE:
00333                     rgpropvar[i].vt = VT_I4;
00334                     rgpropvar[i].u.lVal = This->locale;
00335                     break;
00336                 default:
00337                 {
00338                     PROPVARIANT *prop = PropertyStorage_FindProperty(This,
00339                      rgpspec[i].u.propid);
00340 
00341                     if (prop)
00342                         PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
00343                          GetACP(), This->codePage);
00344                     else
00345                         hr = S_FALSE;
00346                 }
00347             }
00348         }
00349     }
00350     LeaveCriticalSection(&This->cs);
00351     return hr;
00352 }
00353 
00354 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
00355  LCID dstCP)
00356 {
00357     HRESULT hr = S_OK;
00358     int len;
00359 
00360     TRACE("%s, %p, %d, %d\n",
00361      srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
00362      dstCP, srcCP);
00363     assert(src);
00364     assert(dst);
00365     *dst = NULL;
00366     if (dstCP == srcCP)
00367     {
00368         size_t len;
00369 
00370         if (dstCP == CP_UNICODE)
00371             len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
00372         else
00373             len = strlen(src) + 1;
00374         *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
00375         if (!*dst)
00376             hr = STG_E_INSUFFICIENTMEMORY;
00377         else
00378             memcpy(*dst, src, len);
00379     }
00380     else
00381     {
00382         if (dstCP == CP_UNICODE)
00383         {
00384             len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
00385             *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
00386             if (!*dst)
00387                 hr = STG_E_INSUFFICIENTMEMORY;
00388             else
00389                 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
00390         }
00391         else
00392         {
00393             LPCWSTR wideStr = NULL;
00394             LPWSTR wideStr_tmp = NULL;
00395 
00396             if (srcCP == CP_UNICODE)
00397                 wideStr = (LPCWSTR)src;
00398             else
00399             {
00400                 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
00401                 wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
00402                 if (wideStr_tmp)
00403                 {
00404                     MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
00405                     wideStr = wideStr_tmp;
00406                 }
00407                 else
00408                     hr = STG_E_INSUFFICIENTMEMORY;
00409             }
00410             if (SUCCEEDED(hr))
00411             {
00412                 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
00413                  NULL, NULL);
00414                 *dst = CoTaskMemAlloc(len);
00415                 if (!*dst)
00416                     hr = STG_E_INSUFFICIENTMEMORY;
00417                 else
00418                 {
00419                     BOOL defCharUsed = FALSE;
00420 
00421                     if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
00422                      NULL, &defCharUsed) == 0 || defCharUsed)
00423                     {
00424                         CoTaskMemFree(*dst);
00425                         *dst = NULL;
00426                         hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
00427                     }
00428                 }
00429             }
00430             HeapFree(GetProcessHeap(), 0, wideStr_tmp);
00431         }
00432     }
00433     TRACE("returning 0x%08x (%s)\n", hr,
00434      dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
00435     return hr;
00436 }
00437 
00438 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
00439  const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
00440 {
00441     HRESULT hr = S_OK;
00442 
00443     assert(prop);
00444     assert(propvar);
00445     if (propvar->vt == VT_LPSTR)
00446     {
00447         hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
00448          &prop->u.pszVal, targetCP);
00449         if (SUCCEEDED(hr))
00450             prop->vt = VT_LPSTR;
00451     }
00452     else
00453         PropVariantCopy(prop, propvar);
00454     return hr;
00455 }
00456 
00457 /* Stores the property with id propid and value propvar into this property
00458  * storage.  lcid is ignored if propvar's type is not VT_LPSTR.  If propvar's
00459  * type is VT_LPSTR, converts the string using lcid as the source code page
00460  * and This->codePage as the target code page before storing.
00461  * As a side effect, may change This->format to 1 if the type of propvar is
00462  * a version 1-only property.
00463  */
00464 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
00465  PROPID propid, const PROPVARIANT *propvar, LCID lcid)
00466 {
00467     HRESULT hr = S_OK;
00468     PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
00469 
00470     assert(propvar);
00471     if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
00472         This->format = 1;
00473     switch (propvar->vt)
00474     {
00475     case VT_DECIMAL:
00476     case VT_I1:
00477     case VT_INT:
00478     case VT_UINT:
00479     case VT_VECTOR|VT_I1:
00480         This->format = 1;
00481     }
00482     TRACE("Setting 0x%08x to type %d\n", propid, propvar->vt);
00483     if (prop)
00484     {
00485         PropVariantClear(prop);
00486         hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
00487          lcid);
00488     }
00489     else
00490     {
00491         prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
00492          sizeof(PROPVARIANT));
00493         if (prop)
00494         {
00495             hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
00496              lcid);
00497             if (SUCCEEDED(hr))
00498             {
00499                 dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop);
00500                 if (propid > This->highestProp)
00501                     This->highestProp = propid;
00502             }
00503             else
00504                 HeapFree(GetProcessHeap(), 0, prop);
00505         }
00506         else
00507             hr = STG_E_INSUFFICIENTMEMORY;
00508     }
00509     return hr;
00510 }
00511 
00512 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
00513  * srcName is encoded in code page cp, and is converted to This->codePage.
00514  * If cp is CP_UNICODE, srcName is actually a unicode string.
00515  * As a side effect, may change This->format to 1 if srcName is too long for
00516  * a version 0 property storage.
00517  * Doesn't validate id.
00518  */
00519 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
00520  LPCSTR srcName, LCID cp, PROPID id)
00521 {
00522     LPSTR name;
00523     HRESULT hr;
00524 
00525     assert(srcName);
00526 
00527     hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage);
00528     if (SUCCEEDED(hr))
00529     {
00530         if (This->codePage == CP_UNICODE)
00531         {
00532             if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
00533                 This->format = 1;
00534         }
00535         else
00536         {
00537             if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
00538                 This->format = 1;
00539         }
00540         TRACE("Adding prop name %s, propid %d\n",
00541          This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
00542          debugstr_a(name), id);
00543         dictionary_insert(This->name_to_propid, name, UlongToPtr(id));
00544         dictionary_insert(This->propid_to_name, UlongToPtr(id), name);
00545     }
00546     return hr;
00547 }
00548 
00549 /************************************************************************
00550  * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
00551  */
00552 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
00553     IPropertyStorage* iface,
00554     ULONG cpspec,
00555     const PROPSPEC rgpspec[],
00556     const PROPVARIANT rgpropvar[],
00557     PROPID propidNameFirst)
00558 {
00559     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00560     HRESULT hr = S_OK;
00561     ULONG i;
00562 
00563     TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
00564 
00565     if (cpspec && (!rgpspec || !rgpropvar))
00566         return E_INVALIDARG;
00567     if (!(This->grfMode & STGM_READWRITE))
00568         return STG_E_ACCESSDENIED;
00569     EnterCriticalSection(&This->cs);
00570     This->dirty = TRUE;
00571     This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
00572      PROPSETHDR_OSVER_KIND_WIN32) ;
00573     for (i = 0; i < cpspec; i++)
00574     {
00575         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
00576         {
00577             PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
00578              rgpspec[i].u.lpwstr);
00579 
00580             if (prop)
00581                 PropVariantCopy(prop, &rgpropvar[i]);
00582             else
00583             {
00584                 /* Note that I don't do the special cases here that I do below,
00585                  * because naming the special PIDs isn't supported.
00586                  */
00587                 if (propidNameFirst < PID_FIRST_USABLE ||
00588                  propidNameFirst >= PID_MIN_READONLY)
00589                     hr = STG_E_INVALIDPARAMETER;
00590                 else
00591                 {
00592                     PROPID nextId = max(propidNameFirst, This->highestProp + 1);
00593 
00594                     hr = PropertyStorage_StoreNameWithId(This,
00595                      (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
00596                     if (SUCCEEDED(hr))
00597                         hr = PropertyStorage_StorePropWithId(This, nextId,
00598                          &rgpropvar[i], GetACP());
00599                 }
00600             }
00601         }
00602         else
00603         {
00604             switch (rgpspec[i].u.propid)
00605             {
00606             case PID_DICTIONARY:
00607                 /* Can't set the dictionary */
00608                 hr = STG_E_INVALIDPARAMETER;
00609                 break;
00610             case PID_CODEPAGE:
00611                 /* Can only set the code page if nothing else has been set */
00612                 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
00613                  rgpropvar[i].vt == VT_I2)
00614                 {
00615                     This->codePage = rgpropvar[i].u.iVal;
00616                     if (This->codePage == CP_UNICODE)
00617                         This->grfFlags &= ~PROPSETFLAG_ANSI;
00618                     else
00619                         This->grfFlags |= PROPSETFLAG_ANSI;
00620                 }
00621                 else
00622                     hr = STG_E_INVALIDPARAMETER;
00623                 break;
00624             case PID_LOCALE:
00625                 /* Can only set the locale if nothing else has been set */
00626                 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
00627                  rgpropvar[i].vt == VT_I4)
00628                     This->locale = rgpropvar[i].u.lVal;
00629                 else
00630                     hr = STG_E_INVALIDPARAMETER;
00631                 break;
00632             case PID_ILLEGAL:
00633                 /* silently ignore like MSDN says */
00634                 break;
00635             default:
00636                 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
00637                     hr = STG_E_INVALIDPARAMETER;
00638                 else
00639                     hr = PropertyStorage_StorePropWithId(This,
00640                      rgpspec[i].u.propid, &rgpropvar[i], GetACP());
00641             }
00642         }
00643     }
00644     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
00645         IPropertyStorage_Commit(iface, STGC_DEFAULT);
00646     LeaveCriticalSection(&This->cs);
00647     return hr;
00648 }
00649 
00650 /************************************************************************
00651  * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
00652  */
00653 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
00654     IPropertyStorage* iface,
00655     ULONG cpspec,
00656     const PROPSPEC rgpspec[])
00657 {
00658     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00659     ULONG i;
00660     HRESULT hr;
00661 
00662     TRACE("(%p, %d, %p)\n", iface, cpspec, rgpspec);
00663 
00664     if (cpspec && !rgpspec)
00665         return E_INVALIDARG;
00666     if (!(This->grfMode & STGM_READWRITE))
00667         return STG_E_ACCESSDENIED;
00668     hr = S_OK;
00669     EnterCriticalSection(&This->cs);
00670     This->dirty = TRUE;
00671     for (i = 0; i < cpspec; i++)
00672     {
00673         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
00674         {
00675             void *propid;
00676 
00677             if (dictionary_find(This->name_to_propid, rgpspec[i].u.lpwstr, &propid))
00678                 dictionary_remove(This->propid_to_prop, propid);
00679         }
00680         else
00681         {
00682             if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
00683              rgpspec[i].u.propid < PID_MIN_READONLY)
00684                 dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].u.propid));
00685             else
00686                 hr = STG_E_INVALIDPARAMETER;
00687         }
00688     }
00689     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
00690         IPropertyStorage_Commit(iface, STGC_DEFAULT);
00691     LeaveCriticalSection(&This->cs);
00692     return hr;
00693 }
00694 
00695 /************************************************************************
00696  * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
00697  */
00698 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
00699     IPropertyStorage* iface,
00700     ULONG cpropid,
00701     const PROPID rgpropid[],
00702     LPOLESTR rglpwstrName[])
00703 {
00704     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00705     ULONG i;
00706     HRESULT hr = S_FALSE;
00707 
00708     TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
00709 
00710     if (cpropid && (!rgpropid || !rglpwstrName))
00711         return E_INVALIDARG;
00712     EnterCriticalSection(&This->cs);
00713     for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
00714     {
00715         LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
00716 
00717         if (name)
00718         {
00719             size_t len = lstrlenW(name);
00720 
00721             hr = S_OK;
00722             rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
00723             if (rglpwstrName[i])
00724                 memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR));
00725             else
00726                 hr = STG_E_INSUFFICIENTMEMORY;
00727         }
00728         else
00729             rglpwstrName[i] = NULL;
00730     }
00731     LeaveCriticalSection(&This->cs);
00732     return hr;
00733 }
00734 
00735 /************************************************************************
00736  * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
00737  */
00738 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
00739     IPropertyStorage* iface,
00740     ULONG cpropid,
00741     const PROPID rgpropid[],
00742     const LPOLESTR rglpwstrName[])
00743 {
00744     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00745     ULONG i;
00746     HRESULT hr;
00747 
00748     TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
00749 
00750     if (cpropid && (!rgpropid || !rglpwstrName))
00751         return E_INVALIDARG;
00752     if (!(This->grfMode & STGM_READWRITE))
00753         return STG_E_ACCESSDENIED;
00754     hr = S_OK;
00755     EnterCriticalSection(&This->cs);
00756     This->dirty = TRUE;
00757     for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
00758     {
00759         if (rgpropid[i] != PID_ILLEGAL)
00760             hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
00761              CP_UNICODE, rgpropid[i]);
00762     }
00763     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
00764         IPropertyStorage_Commit(iface, STGC_DEFAULT);
00765     LeaveCriticalSection(&This->cs);
00766     return hr;
00767 }
00768 
00769 /************************************************************************
00770  * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
00771  */
00772 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
00773     IPropertyStorage* iface,
00774     ULONG cpropid,
00775     const PROPID rgpropid[])
00776 {
00777     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00778     ULONG i;
00779     HRESULT hr;
00780 
00781     TRACE("(%p, %d, %p)\n", iface, cpropid, rgpropid);
00782 
00783     if (cpropid && !rgpropid)
00784         return E_INVALIDARG;
00785     if (!(This->grfMode & STGM_READWRITE))
00786         return STG_E_ACCESSDENIED;
00787     hr = S_OK;
00788     EnterCriticalSection(&This->cs);
00789     This->dirty = TRUE;
00790     for (i = 0; i < cpropid; i++)
00791     {
00792         LPWSTR name = NULL;
00793 
00794         if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name))
00795         {
00796             dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i]));
00797             dictionary_remove(This->name_to_propid, name);
00798         }
00799     }
00800     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
00801         IPropertyStorage_Commit(iface, STGC_DEFAULT);
00802     LeaveCriticalSection(&This->cs);
00803     return hr;
00804 }
00805 
00806 /************************************************************************
00807  * IPropertyStorage_fnCommit (IPropertyStorage)
00808  */
00809 static HRESULT WINAPI IPropertyStorage_fnCommit(
00810     IPropertyStorage* iface,
00811     DWORD grfCommitFlags)
00812 {
00813     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00814     HRESULT hr;
00815 
00816     TRACE("(%p, 0x%08x)\n", iface, grfCommitFlags);
00817 
00818     if (!(This->grfMode & STGM_READWRITE))
00819         return STG_E_ACCESSDENIED;
00820     EnterCriticalSection(&This->cs);
00821     if (This->dirty)
00822         hr = PropertyStorage_WriteToStream(This);
00823     else
00824         hr = S_OK;
00825     LeaveCriticalSection(&This->cs);
00826     return hr;
00827 }
00828 
00829 /************************************************************************
00830  * IPropertyStorage_fnRevert (IPropertyStorage)
00831  */
00832 static HRESULT WINAPI IPropertyStorage_fnRevert(
00833     IPropertyStorage* iface)
00834 {
00835     HRESULT hr;
00836     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00837 
00838     TRACE("%p\n", iface);
00839 
00840     EnterCriticalSection(&This->cs);
00841     if (This->dirty)
00842     {
00843         PropertyStorage_DestroyDictionaries(This);
00844         hr = PropertyStorage_CreateDictionaries(This);
00845         if (SUCCEEDED(hr))
00846             hr = PropertyStorage_ReadFromStream(This);
00847     }
00848     else
00849         hr = S_OK;
00850     LeaveCriticalSection(&This->cs);
00851     return hr;
00852 }
00853 
00854 /************************************************************************
00855  * IPropertyStorage_fnEnum (IPropertyStorage)
00856  */
00857 static HRESULT WINAPI IPropertyStorage_fnEnum(
00858     IPropertyStorage* iface,
00859     IEnumSTATPROPSTG** ppenum)
00860 {
00861     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00862     return create_EnumSTATPROPSTG(This, ppenum);
00863 }
00864 
00865 /************************************************************************
00866  * IPropertyStorage_fnSetTimes (IPropertyStorage)
00867  */
00868 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
00869     IPropertyStorage* iface,
00870     const FILETIME* pctime,
00871     const FILETIME* patime,
00872     const FILETIME* pmtime)
00873 {
00874     FIXME("\n");
00875     return E_NOTIMPL;
00876 }
00877 
00878 /************************************************************************
00879  * IPropertyStorage_fnSetClass (IPropertyStorage)
00880  */
00881 static HRESULT WINAPI IPropertyStorage_fnSetClass(
00882     IPropertyStorage* iface,
00883     REFCLSID clsid)
00884 {
00885     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00886 
00887     TRACE("%p, %s\n", iface, debugstr_guid(clsid));
00888 
00889     if (!clsid)
00890         return E_INVALIDARG;
00891     if (!(This->grfMode & STGM_READWRITE))
00892         return STG_E_ACCESSDENIED;
00893     This->clsid = *clsid;
00894     This->dirty = TRUE;
00895     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
00896         IPropertyStorage_Commit(iface, STGC_DEFAULT);
00897     return S_OK;
00898 }
00899 
00900 /************************************************************************
00901  * IPropertyStorage_fnStat (IPropertyStorage)
00902  */
00903 static HRESULT WINAPI IPropertyStorage_fnStat(
00904     IPropertyStorage* iface,
00905     STATPROPSETSTG* statpsstg)
00906 {
00907     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
00908     STATSTG stat;
00909     HRESULT hr;
00910 
00911     TRACE("%p, %p\n", iface, statpsstg);
00912 
00913     if (!statpsstg)
00914         return E_INVALIDARG;
00915 
00916     hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
00917     if (SUCCEEDED(hr))
00918     {
00919         statpsstg->fmtid = This->fmtid;
00920         statpsstg->clsid = This->clsid;
00921         statpsstg->grfFlags = This->grfFlags;
00922         statpsstg->mtime = stat.mtime;
00923         statpsstg->ctime = stat.ctime;
00924         statpsstg->atime = stat.atime;
00925         statpsstg->dwOSVersion = This->originatorOS;
00926     }
00927     return hr;
00928 }
00929 
00930 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
00931  void *extra)
00932 {
00933     PropertyStorage_impl *This = extra;
00934 
00935     if (This->codePage == CP_UNICODE)
00936     {
00937         TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
00938         if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
00939             return lstrcmpW(a, b);
00940         else
00941             return lstrcmpiW(a, b);
00942     }
00943     else
00944     {
00945         TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
00946         if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
00947             return lstrcmpA(a, b);
00948         else
00949             return lstrcmpiA(a, b);
00950     }
00951 }
00952 
00953 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
00954 {
00955     CoTaskMemFree(k);
00956 }
00957 
00958 static int PropertyStorage_PropCompare(const void *a, const void *b,
00959  void *extra)
00960 {
00961     TRACE("(%d, %d)\n", PtrToUlong(a), PtrToUlong(b));
00962     return PtrToUlong(a) - PtrToUlong(b);
00963 }
00964 
00965 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
00966 {
00967     PropVariantClear(d);
00968     HeapFree(GetProcessHeap(), 0, d);
00969 }
00970 
00971 #ifdef WORDS_BIGENDIAN
00972 /* Swaps each character in str to or from little endian; assumes the conversion
00973  * is symmetric, that is, that lendian16toh is equivalent to htole16.
00974  */
00975 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
00976 {
00977     DWORD i;
00978 
00979     /* Swap characters to host order.
00980      * FIXME: alignment?
00981      */
00982     for (i = 0; i < len; i++)
00983         str[i] = lendian16toh(str[i]);
00984 }
00985 #else
00986 #define PropertyStorage_ByteSwapString(s, l)
00987 #endif
00988 
00989 /* Reads the dictionary from the memory buffer beginning at ptr.  Interprets
00990  * the entries according to the values of This->codePage and This->locale.
00991  * FIXME: there isn't any checking whether the read property extends past the
00992  * end of the buffer.
00993  */
00994 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
00995  BYTE *ptr)
00996 {
00997     DWORD numEntries, i;
00998     HRESULT hr = S_OK;
00999 
01000     assert(This->name_to_propid);
01001     assert(This->propid_to_name);
01002 
01003     StorageUtl_ReadDWord(ptr, 0, &numEntries);
01004     TRACE("Reading %d entries:\n", numEntries);
01005     ptr += sizeof(DWORD);
01006     for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
01007     {
01008         PROPID propid;
01009         DWORD cbEntry;
01010 
01011         StorageUtl_ReadDWord(ptr, 0, &propid);
01012         ptr += sizeof(PROPID);
01013         StorageUtl_ReadDWord(ptr, 0, &cbEntry);
01014         ptr += sizeof(DWORD);
01015         TRACE("Reading entry with ID 0x%08x, %d bytes\n", propid, cbEntry);
01016         /* Make sure the source string is NULL-terminated */
01017         if (This->codePage != CP_UNICODE)
01018             ptr[cbEntry - 1] = '\0';
01019         else
01020             *((LPWSTR)ptr + cbEntry / sizeof(WCHAR)) = '\0';
01021         hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
01022         if (This->codePage == CP_UNICODE)
01023         {
01024             /* Unicode entries are padded to DWORD boundaries */
01025             if (cbEntry % sizeof(DWORD))
01026                 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
01027         }
01028         ptr += sizeof(DWORD) + cbEntry;
01029     }
01030     return hr;
01031 }
01032 
01033 /* FIXME: there isn't any checking whether the read property extends past the
01034  * end of the buffer.
01035  */
01036 static HRESULT PropertyStorage_ReadProperty(PropertyStorage_impl *This,
01037  PROPVARIANT *prop, const BYTE *data)
01038 {
01039     HRESULT hr = S_OK;
01040 
01041     assert(prop);
01042     assert(data);
01043     StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
01044     data += sizeof(DWORD);
01045     switch (prop->vt)
01046     {
01047     case VT_EMPTY:
01048     case VT_NULL:
01049         break;
01050     case VT_I1:
01051         prop->u.cVal = *(const char *)data;
01052         TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
01053         break;
01054     case VT_UI1:
01055         prop->u.bVal = *data;
01056         TRACE("Read byte 0x%x\n", prop->u.bVal);
01057         break;
01058     case VT_I2:
01059         StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
01060         TRACE("Read short %d\n", prop->u.iVal);
01061         break;
01062     case VT_UI2:
01063         StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
01064         TRACE("Read ushort %d\n", prop->u.uiVal);
01065         break;
01066     case VT_INT:
01067     case VT_I4:
01068         StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
01069         TRACE("Read long %d\n", prop->u.lVal);
01070         break;
01071     case VT_UINT:
01072     case VT_UI4:
01073         StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
01074         TRACE("Read ulong %d\n", prop->u.ulVal);
01075         break;
01076     case VT_LPSTR:
01077     {
01078         DWORD count;
01079        
01080         StorageUtl_ReadDWord(data, 0, &count);
01081         if (This->codePage == CP_UNICODE && count / 2)
01082         {
01083             WARN("Unicode string has odd number of bytes\n");
01084             hr = STG_E_INVALIDHEADER;
01085         }
01086         else
01087         {
01088             prop->u.pszVal = CoTaskMemAlloc(count);
01089             if (prop->u.pszVal)
01090             {
01091                 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
01092                 /* This is stored in the code page specified in This->codePage.
01093                  * Don't convert it, the caller will just store it as-is.
01094                  */
01095                 if (This->codePage == CP_UNICODE)
01096                 {
01097                     /* Make sure it's NULL-terminated */
01098                     prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
01099                     TRACE("Read string value %s\n",
01100                      debugstr_w(prop->u.pwszVal));
01101                 }
01102                 else
01103                 {
01104                     /* Make sure it's NULL-terminated */
01105                     prop->u.pszVal[count - 1] = '\0';
01106                     TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
01107                 }
01108             }
01109             else
01110                 hr = STG_E_INSUFFICIENTMEMORY;
01111         }
01112         break;
01113     }
01114     case VT_BLOB:
01115     {
01116         DWORD count;
01117 
01118         StorageUtl_ReadDWord(data, 0, &count);
01119         prop->u.blob.cbSize = count;
01120         prop->u.blob.pBlobData = CoTaskMemAlloc(count);
01121         if (prop->u.blob.pBlobData)
01122         {
01123             memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count);
01124             TRACE("Read blob value of size %d\n", count);
01125         }
01126         else
01127             hr = STG_E_INSUFFICIENTMEMORY;
01128         break;
01129     }
01130     case VT_LPWSTR:
01131     {
01132         DWORD count;
01133 
01134         StorageUtl_ReadDWord(data, 0, &count);
01135         prop->u.pwszVal = CoTaskMemAlloc(count * sizeof(WCHAR));
01136         if (prop->u.pwszVal)
01137         {
01138             memcpy(prop->u.pwszVal, data + sizeof(DWORD),
01139              count * sizeof(WCHAR));
01140             /* make sure string is NULL-terminated */
01141             prop->u.pwszVal[count - 1] = '\0';
01142             PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
01143             TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
01144         }
01145         else
01146             hr = STG_E_INSUFFICIENTMEMORY;
01147         break;
01148     }
01149     case VT_FILETIME:
01150         StorageUtl_ReadULargeInteger(data, 0,
01151          (ULARGE_INTEGER *)&prop->u.filetime);
01152         break;
01153     case VT_CF:
01154         {
01155             DWORD len = 0, tag = 0;
01156 
01157             StorageUtl_ReadDWord(data, 0, &len);
01158             StorageUtl_ReadDWord(data, 4, &tag);
01159             if (len > 8)
01160             {
01161                 len -= 8;
01162                 prop->u.pclipdata = CoTaskMemAlloc(sizeof (CLIPDATA));
01163                 prop->u.pclipdata->cbSize = len;
01164                 prop->u.pclipdata->ulClipFmt = tag;
01165                 prop->u.pclipdata->pClipData = CoTaskMemAlloc(len - sizeof(prop->u.pclipdata->ulClipFmt));
01166                 memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt));
01167             }
01168             else
01169                 hr = STG_E_INVALIDPARAMETER;
01170         }
01171         break;
01172     default:
01173         FIXME("unsupported type %d\n", prop->vt);
01174         hr = STG_E_INVALIDPARAMETER;
01175     }
01176     return hr;
01177 }
01178 
01179 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
01180  PROPERTYSETHEADER *hdr)
01181 {
01182     BYTE buf[sizeof(PROPERTYSETHEADER)];
01183     ULONG count = 0;
01184     HRESULT hr;
01185 
01186     assert(stm);
01187     assert(hdr);
01188     hr = IStream_Read(stm, buf, sizeof(buf), &count);
01189     if (SUCCEEDED(hr))
01190     {
01191         if (count != sizeof(buf))
01192         {
01193             WARN("read only %d\n", count);
01194             hr = STG_E_INVALIDHEADER;
01195         }
01196         else
01197         {
01198             StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
01199              &hdr->wByteOrder);
01200             StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
01201              &hdr->wFormat);
01202             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
01203              &hdr->dwOSVer);
01204             StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
01205              &hdr->clsid);
01206             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
01207              &hdr->reserved);
01208         }
01209     }
01210     TRACE("returning 0x%08x\n", hr);
01211     return hr;
01212 }
01213 
01214 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
01215  FORMATIDOFFSET *fmt)
01216 {
01217     BYTE buf[sizeof(FORMATIDOFFSET)];
01218     ULONG count = 0;
01219     HRESULT hr;
01220 
01221     assert(stm);
01222     assert(fmt);
01223     hr = IStream_Read(stm, buf, sizeof(buf), &count);
01224     if (SUCCEEDED(hr))
01225     {
01226         if (count != sizeof(buf))
01227         {
01228             WARN("read only %d\n", count);
01229             hr = STG_E_INVALIDHEADER;
01230         }
01231         else
01232         {
01233             StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
01234              &fmt->fmtid);
01235             StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
01236              &fmt->dwOffset);
01237         }
01238     }
01239     TRACE("returning 0x%08x\n", hr);
01240     return hr;
01241 }
01242 
01243 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
01244  PROPERTYSECTIONHEADER *hdr)
01245 {
01246     BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
01247     ULONG count = 0;
01248     HRESULT hr;
01249 
01250     assert(stm);
01251     assert(hdr);
01252     hr = IStream_Read(stm, buf, sizeof(buf), &count);
01253     if (SUCCEEDED(hr))
01254     {
01255         if (count != sizeof(buf))
01256         {
01257             WARN("read only %d\n", count);
01258             hr = STG_E_INVALIDHEADER;
01259         }
01260         else
01261         {
01262             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
01263              cbSection), &hdr->cbSection);
01264             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
01265              cProperties), &hdr->cProperties);
01266         }
01267     }
01268     TRACE("returning 0x%08x\n", hr);
01269     return hr;
01270 }
01271 
01272 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
01273 {
01274     PROPERTYSETHEADER hdr;
01275     FORMATIDOFFSET fmtOffset;
01276     PROPERTYSECTIONHEADER sectionHdr;
01277     LARGE_INTEGER seek;
01278     ULONG i;
01279     STATSTG stat;
01280     HRESULT hr;
01281     BYTE *buf = NULL;
01282     ULONG count = 0;
01283     DWORD dictOffset = 0;
01284 
01285     This->dirty = FALSE;
01286     This->highestProp = 0;
01287     hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
01288     if (FAILED(hr))
01289         goto end;
01290     if (stat.cbSize.u.HighPart)
01291     {
01292         WARN("stream too big\n");
01293         /* maximum size varies, but it can't be this big */
01294         hr = STG_E_INVALIDHEADER;
01295         goto end;
01296     }
01297     if (stat.cbSize.u.LowPart == 0)
01298     {
01299         /* empty stream is okay */
01300         hr = S_OK;
01301         goto end;
01302     }
01303     else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
01304      sizeof(FORMATIDOFFSET))
01305     {
01306         WARN("stream too small\n");
01307         hr = STG_E_INVALIDHEADER;
01308         goto end;
01309     }
01310     seek.QuadPart = 0;
01311     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
01312     if (FAILED(hr))
01313         goto end;
01314     hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
01315     /* I've only seen reserved == 1, but the article says I shouldn't disallow
01316      * higher values.
01317      */
01318     if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
01319     {
01320         WARN("bad magic in prop set header\n");
01321         hr = STG_E_INVALIDHEADER;
01322         goto end;
01323     }
01324     if (hdr.wFormat != 0 && hdr.wFormat != 1)
01325     {
01326         WARN("bad format version %d\n", hdr.wFormat);
01327         hr = STG_E_INVALIDHEADER;
01328         goto end;
01329     }
01330     This->format = hdr.wFormat;
01331     This->clsid = hdr.clsid;
01332     This->originatorOS = hdr.dwOSVer;
01333     if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
01334         WARN("File comes from a Mac, strings will probably be screwed up\n");
01335     hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
01336     if (FAILED(hr))
01337         goto end;
01338     if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
01339     {
01340         WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset,
01341          stat.cbSize.u.LowPart);
01342         hr = STG_E_INVALIDHEADER;
01343         goto end;
01344     }
01345     /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
01346      * follow not one, but two sections.  The first is the standard properties
01347      * for the document summary information, and the second is user-defined
01348      * properties.  This is the only case in which multiple sections are
01349      * allowed.
01350      * Reading the second stream isn't implemented yet.
01351      */
01352     hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
01353     if (FAILED(hr))
01354         goto end;
01355     /* The section size includes the section header, so check it */
01356     if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
01357     {
01358         WARN("section header too small, got %d\n", sectionHdr.cbSection);
01359         hr = STG_E_INVALIDHEADER;
01360         goto end;
01361     }
01362     buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
01363      sizeof(PROPERTYSECTIONHEADER));
01364     if (!buf)
01365     {
01366         hr = STG_E_INSUFFICIENTMEMORY;
01367         goto end;
01368     }
01369     hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
01370      sizeof(PROPERTYSECTIONHEADER), &count);
01371     if (FAILED(hr))
01372         goto end;
01373     TRACE("Reading %d properties:\n", sectionHdr.cProperties);
01374     for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
01375     {
01376         PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
01377          i * sizeof(PROPERTYIDOFFSET));
01378 
01379         if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
01380          idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
01381             hr = STG_E_INVALIDPOINTER;
01382         else
01383         {
01384             if (idOffset->propid >= PID_FIRST_USABLE &&
01385              idOffset->propid < PID_MIN_READONLY && idOffset->propid >
01386              This->highestProp)
01387                 This->highestProp = idOffset->propid;
01388             if (idOffset->propid == PID_DICTIONARY)
01389             {
01390                 /* Don't read the dictionary yet, its entries depend on the
01391                  * code page.  Just store the offset so we know to read it
01392                  * later.
01393                  */
01394                 dictOffset = idOffset->dwOffset;
01395                 TRACE("Dictionary offset is %d\n", dictOffset);
01396             }
01397             else
01398             {
01399                 PROPVARIANT prop;
01400 
01401                 PropVariantInit(&prop);
01402                 if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
01403                  buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
01404                 {
01405                     TRACE("Read property with ID 0x%08x, type %d\n",
01406                      idOffset->propid, prop.vt);
01407                     switch(idOffset->propid)
01408                     {
01409                     case PID_CODEPAGE:
01410                         if (prop.vt == VT_I2)
01411                             This->codePage = (UINT)prop.u.iVal;
01412                         break;
01413                     case PID_LOCALE:
01414                         if (prop.vt == VT_I4)
01415                             This->locale = (LCID)prop.u.lVal;
01416                         break;
01417                     case PID_BEHAVIOR:
01418                         if (prop.vt == VT_I4 && prop.u.lVal)
01419                             This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
01420                         /* The format should already be 1, but just in case */
01421                         This->format = 1;
01422                         break;
01423                     default:
01424                         hr = PropertyStorage_StorePropWithId(This,
01425                          idOffset->propid, &prop, This->codePage);
01426                     }
01427                 }
01428                 PropVariantClear(&prop);
01429             }
01430         }
01431     }
01432     if (!This->codePage)
01433     {
01434         /* default to Unicode unless told not to, as specified on msdn */
01435         if (This->grfFlags & PROPSETFLAG_ANSI)
01436             This->codePage = GetACP();
01437         else
01438             This->codePage = CP_UNICODE;
01439     }
01440     if (!This->locale)
01441         This->locale = LOCALE_SYSTEM_DEFAULT;
01442     TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
01443     if (dictOffset)
01444         hr = PropertyStorage_ReadDictionary(This,
01445          buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
01446 
01447 end:
01448     HeapFree(GetProcessHeap(), 0, buf);
01449     if (FAILED(hr))
01450     {
01451         dictionary_destroy(This->name_to_propid);
01452         This->name_to_propid = NULL;
01453         dictionary_destroy(This->propid_to_name);
01454         This->propid_to_name = NULL;
01455         dictionary_destroy(This->propid_to_prop);
01456         This->propid_to_prop = NULL;
01457     }
01458     return hr;
01459 }
01460 
01461 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
01462  PROPERTYSETHEADER *hdr)
01463 {
01464     assert(hdr);
01465     StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
01466      PROPSETHDR_BYTEORDER_MAGIC);
01467     StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
01468     StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
01469     StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
01470     StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
01471 }
01472 
01473 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
01474  FORMATIDOFFSET *fmtOffset)
01475 {
01476     assert(fmtOffset);
01477     StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
01478     StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
01479      sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
01480 }
01481 
01482 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
01483  PROPERTYSECTIONHEADER *hdr)
01484 {
01485     assert(hdr);
01486     StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
01487     StorageUtl_WriteDWord((BYTE *)hdr,
01488      offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
01489 }
01490 
01491 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
01492  PROPERTYIDOFFSET *propIdOffset)
01493 {
01494     assert(propIdOffset);
01495     StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
01496     StorageUtl_WriteDWord((BYTE *)propIdOffset,
01497      offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
01498 }
01499 
01500 static inline HRESULT PropertStorage_WriteWStringToStream(IStream *stm,
01501  LPCWSTR str, DWORD len, DWORD *written)
01502 {
01503 #ifdef WORDS_BIGENDIAN
01504     WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
01505     HRESULT hr;
01506 
01507     if (!leStr)
01508         return E_OUTOFMEMORY;
01509     memcpy(leStr, str, len * sizeof(WCHAR));
01510     PropertyStorage_ByteSwapString(leStr, len);
01511     hr = IStream_Write(stm, leStr, len, written);
01512     HeapFree(GetProcessHeap(), 0, leStr);
01513     return hr;
01514 #else
01515     return IStream_Write(stm, str, len, written);
01516 #endif
01517 }
01518 
01519 struct DictionaryClosure
01520 {
01521     HRESULT hr;
01522     DWORD bytesWritten;
01523 };
01524 
01525 static BOOL PropertyStorage_DictionaryWriter(const void *key,
01526  const void *value, void *extra, void *closure)
01527 {
01528     PropertyStorage_impl *This = extra;
01529     struct DictionaryClosure *c = closure;
01530     DWORD propid;
01531     ULONG count;
01532 
01533     assert(key);
01534     assert(closure);
01535     StorageUtl_WriteDWord((LPBYTE)&propid, 0, PtrToUlong(value));
01536     c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
01537     if (FAILED(c->hr))
01538         goto end;
01539     c->bytesWritten += sizeof(DWORD);
01540     if (This->codePage == CP_UNICODE)
01541     {
01542         DWORD keyLen, pad = 0;
01543 
01544         StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
01545          (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
01546         c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
01547         if (FAILED(c->hr))
01548             goto end;
01549         c->bytesWritten += sizeof(DWORD);
01550         c->hr = PropertStorage_WriteWStringToStream(This->stm, key, keyLen,
01551          &count);
01552         if (FAILED(c->hr))
01553             goto end;
01554         c->bytesWritten += keyLen * sizeof(WCHAR);
01555         if (keyLen % sizeof(DWORD))
01556         {
01557             c->hr = IStream_Write(This->stm, &pad,
01558              sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
01559             if (FAILED(c->hr))
01560                 goto end;
01561             c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
01562         }
01563     }
01564     else
01565     {
01566         DWORD keyLen;
01567 
01568         StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
01569         c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
01570         if (FAILED(c->hr))
01571             goto end;
01572         c->bytesWritten += sizeof(DWORD);
01573         c->hr = IStream_Write(This->stm, key, keyLen, &count);
01574         if (FAILED(c->hr))
01575             goto end;
01576         c->bytesWritten += keyLen;
01577     }
01578 end:
01579     return SUCCEEDED(c->hr);
01580 }
01581 
01582 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
01583 
01584 /* Writes the dictionary to the stream.  Assumes without checking that the
01585  * dictionary isn't empty.
01586  */
01587 static HRESULT PropertyStorage_WriteDictionaryToStream(
01588  PropertyStorage_impl *This, DWORD *sectionOffset)
01589 {
01590     HRESULT hr;
01591     LARGE_INTEGER seek;
01592     PROPERTYIDOFFSET propIdOffset;
01593     ULONG count;
01594     DWORD dwTemp;
01595     struct DictionaryClosure closure;
01596 
01597     assert(sectionOffset);
01598 
01599     /* The dictionary's always the first property written, so seek to its
01600      * spot.
01601      */
01602     seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
01603     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
01604     if (FAILED(hr))
01605         goto end;
01606     PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
01607      &propIdOffset);
01608     hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
01609     if (FAILED(hr))
01610         goto end;
01611 
01612     seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
01613     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
01614     if (FAILED(hr))
01615         goto end;
01616     StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
01617      dictionary_num_entries(This->name_to_propid));
01618     hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
01619     if (FAILED(hr))
01620         goto end;
01621     *sectionOffset += sizeof(dwTemp);
01622 
01623     closure.hr = S_OK;
01624     closure.bytesWritten = 0;
01625     dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
01626      &closure);
01627     hr = closure.hr;
01628     if (FAILED(hr))
01629         goto end;
01630     *sectionOffset += closure.bytesWritten;
01631     if (closure.bytesWritten % sizeof(DWORD))
01632     {
01633         DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
01634         TRACE("adding %d bytes of padding\n", padding);
01635         *sectionOffset += padding;
01636     }
01637 
01638 end:
01639     return hr;
01640 }
01641 
01642 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
01643  DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
01644 {
01645     HRESULT hr;
01646     LARGE_INTEGER seek;
01647     PROPERTYIDOFFSET propIdOffset;
01648     ULONG count;
01649     DWORD dwType, bytesWritten;
01650 
01651     assert(var);
01652     assert(sectionOffset);
01653 
01654     TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
01655      *sectionOffset);
01656 
01657     seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
01658      propNum * sizeof(PROPERTYIDOFFSET);
01659     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
01660     if (FAILED(hr))
01661         goto end;
01662     PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
01663     hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
01664     if (FAILED(hr))
01665         goto end;
01666 
01667     seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
01668     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
01669     if (FAILED(hr))
01670         goto end;
01671     StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
01672     hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
01673     if (FAILED(hr))
01674         goto end;
01675     *sectionOffset += sizeof(dwType);
01676 
01677     switch (var->vt)
01678     {
01679     case VT_EMPTY:
01680     case VT_NULL:
01681         bytesWritten = 0;
01682         break;
01683     case VT_I1:
01684     case VT_UI1:
01685         hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
01686          &count);
01687         bytesWritten = count;
01688         break;
01689     case VT_I2:
01690     case VT_UI2:
01691     {
01692         WORD wTemp;
01693 
01694         StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
01695         hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
01696         bytesWritten = count;
01697         break;
01698     }
01699     case VT_I4:
01700     case VT_UI4:
01701     {
01702         DWORD dwTemp;
01703 
01704         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
01705         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
01706         bytesWritten = count;
01707         break;
01708     }
01709     case VT_LPSTR:
01710     {
01711         DWORD len, dwTemp;
01712 
01713         if (This->codePage == CP_UNICODE)
01714             len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
01715         else
01716             len = lstrlenA(var->u.pszVal) + 1;
01717         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
01718         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
01719         if (FAILED(hr))
01720             goto end;
01721         hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
01722         bytesWritten = count + sizeof(DWORD);
01723         break;
01724     }
01725     case VT_LPWSTR:
01726     {
01727         DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
01728 
01729         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
01730         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
01731         if (FAILED(hr))
01732             goto end;
01733         hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
01734          &count);
01735         bytesWritten = count + sizeof(DWORD);
01736         break;
01737     }
01738     case VT_FILETIME:
01739     {
01740         FILETIME temp;
01741 
01742         StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
01743          (const ULARGE_INTEGER *)&var->u.filetime);
01744         hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
01745         bytesWritten = count;
01746         break;
01747     }
01748     case VT_CF:
01749     {
01750         DWORD cf_hdr[2], len;
01751 
01752         len = var->u.pclipdata->cbSize;
01753         StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
01754         StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
01755         hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
01756         if (FAILED(hr))
01757             goto end;
01758         hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
01759                            len - sizeof(var->u.pclipdata->ulClipFmt), &count);
01760         if (FAILED(hr))
01761             goto end;
01762         bytesWritten = count + sizeof cf_hdr;
01763         break;
01764     }
01765     case VT_CLSID:
01766     {
01767         CLSID temp;
01768 
01769         StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
01770         hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
01771         bytesWritten = count;
01772         break;
01773     }
01774     default:
01775         FIXME("unsupported type: %d\n", var->vt);
01776         return STG_E_INVALIDPARAMETER;
01777     }
01778 
01779     if (SUCCEEDED(hr))
01780     {
01781         *sectionOffset += bytesWritten;
01782         if (bytesWritten % sizeof(DWORD))
01783         {
01784             DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
01785             TRACE("adding %d bytes of padding\n", padding);
01786             *sectionOffset += padding;
01787         }
01788     }
01789 
01790 end:
01791     return hr;
01792 }
01793 
01794 struct PropertyClosure
01795 {
01796     HRESULT hr;
01797     DWORD   propNum;
01798     DWORD  *sectionOffset;
01799 };
01800 
01801 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
01802  void *extra, void *closure)
01803 {
01804     PropertyStorage_impl *This = extra;
01805     struct PropertyClosure *c = closure;
01806 
01807     assert(key);
01808     assert(value);
01809     assert(extra);
01810     assert(closure);
01811     c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
01812                                                   PtrToUlong(key), value, c->sectionOffset);
01813     return SUCCEEDED(c->hr);
01814 }
01815 
01816 static HRESULT PropertyStorage_WritePropertiesToStream(
01817  PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
01818 {
01819     struct PropertyClosure closure;
01820 
01821     assert(sectionOffset);
01822     closure.hr = S_OK;
01823     closure.propNum = startingPropNum;
01824     closure.sectionOffset = sectionOffset;
01825     dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
01826      &closure);
01827     return closure.hr;
01828 }
01829 
01830 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
01831 {
01832     HRESULT hr;
01833     ULONG count = 0;
01834     LARGE_INTEGER seek = { {0} };
01835     PROPERTYSETHEADER hdr;
01836     FORMATIDOFFSET fmtOffset;
01837 
01838     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
01839     if (FAILED(hr))
01840         goto end;
01841     PropertyStorage_MakeHeader(This, &hdr);
01842     hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
01843     if (FAILED(hr))
01844         goto end;
01845     if (count != sizeof(hdr))
01846     {
01847         hr = STG_E_WRITEFAULT;
01848         goto end;
01849     }
01850 
01851     PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
01852     hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
01853     if (FAILED(hr))
01854         goto end;
01855     if (count != sizeof(fmtOffset))
01856     {
01857         hr = STG_E_WRITEFAULT;
01858         goto end;
01859     }
01860     hr = S_OK;
01861 
01862 end:
01863     return hr;
01864 }
01865 
01866 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
01867 {
01868     PROPERTYSECTIONHEADER sectionHdr;
01869     HRESULT hr;
01870     ULONG count;
01871     LARGE_INTEGER seek;
01872     DWORD numProps, prop, sectionOffset, dwTemp;
01873     PROPVARIANT var;
01874 
01875     PropertyStorage_WriteHeadersToStream(This);
01876 
01877     /* Count properties.  Always at least one property, the code page */
01878     numProps = 1;
01879     if (dictionary_num_entries(This->name_to_propid))
01880         numProps++;
01881     if (This->locale != LOCALE_SYSTEM_DEFAULT)
01882         numProps++;
01883     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
01884         numProps++;
01885     numProps += dictionary_num_entries(This->propid_to_prop);
01886 
01887     /* Write section header with 0 bytes right now, I'll adjust it after
01888      * writing properties.
01889      */
01890     PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
01891     seek.QuadPart = SECTIONHEADER_OFFSET;
01892     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
01893     if (FAILED(hr))
01894         goto end;
01895     hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
01896     if (FAILED(hr))
01897         goto end;
01898 
01899     prop = 0;
01900     sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
01901      numProps * sizeof(PROPERTYIDOFFSET);
01902 
01903     if (dictionary_num_entries(This->name_to_propid))
01904     {
01905         prop++;
01906         hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
01907         if (FAILED(hr))
01908             goto end;
01909     }
01910 
01911     PropVariantInit(&var);
01912 
01913     var.vt = VT_I2;
01914     var.u.iVal = This->codePage;
01915     hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
01916      &var, &sectionOffset);
01917     if (FAILED(hr))
01918         goto end;
01919 
01920     if (This->locale != LOCALE_SYSTEM_DEFAULT)
01921     {
01922         var.vt = VT_I4;
01923         var.u.lVal = This->locale;
01924         hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
01925          &var, &sectionOffset);
01926         if (FAILED(hr))
01927             goto end;
01928     }
01929 
01930     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
01931     {
01932         var.vt = VT_I4;
01933         var.u.lVal = 1;
01934         hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
01935          &var, &sectionOffset);
01936         if (FAILED(hr))
01937             goto end;
01938     }
01939 
01940     hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
01941     if (FAILED(hr))
01942         goto end;
01943 
01944     /* Now write the byte count of the section */
01945     seek.QuadPart = SECTIONHEADER_OFFSET;
01946     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
01947     if (FAILED(hr))
01948         goto end;
01949     StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
01950     hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
01951 
01952 end:
01953     return hr;
01954 }
01955 
01956 /***********************************************************************
01957  * PropertyStorage_Construct
01958  */
01959 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
01960 {
01961     dictionary_destroy(This->name_to_propid);
01962     This->name_to_propid = NULL;
01963     dictionary_destroy(This->propid_to_name);
01964     This->propid_to_name = NULL;
01965     dictionary_destroy(This->propid_to_prop);
01966     This->propid_to_prop = NULL;
01967 }
01968 
01969 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
01970 {
01971     HRESULT hr = S_OK;
01972 
01973     This->name_to_propid = dictionary_create(
01974      PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
01975      This);
01976     if (!This->name_to_propid)
01977     {
01978         hr = STG_E_INSUFFICIENTMEMORY;
01979         goto end;
01980     }
01981     This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
01982      NULL, This);
01983     if (!This->propid_to_name)
01984     {
01985         hr = STG_E_INSUFFICIENTMEMORY;
01986         goto end;
01987     }
01988     This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
01989      PropertyStorage_PropertyDestroy, This);
01990     if (!This->propid_to_prop)
01991     {
01992         hr = STG_E_INSUFFICIENTMEMORY;
01993         goto end;
01994     }
01995 end:
01996     if (FAILED(hr))
01997         PropertyStorage_DestroyDictionaries(This);
01998     return hr;
01999 }
02000 
02001 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
02002  REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
02003 {
02004     HRESULT hr = S_OK;
02005 
02006     assert(pps);
02007     assert(rfmtid);
02008     *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
02009     if (!*pps)
02010         return E_OUTOFMEMORY;
02011 
02012     (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
02013     (*pps)->ref = 1;
02014     InitializeCriticalSection(&(*pps)->cs);
02015     (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
02016     (*pps)->stm = stm;
02017     (*pps)->fmtid = *rfmtid;
02018     (*pps)->grfMode = grfMode;
02019 
02020     hr = PropertyStorage_CreateDictionaries(*pps);
02021     if (FAILED(hr))
02022     {
02023         IStream_Release(stm);
02024         (*pps)->cs.DebugInfo->Spare[0] = 0;
02025         DeleteCriticalSection(&(*pps)->cs);
02026         HeapFree(GetProcessHeap(), 0, *pps);
02027         *pps = NULL;
02028     }
02029 
02030     return hr;
02031 }
02032 
02033 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
02034  REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
02035 {
02036     PropertyStorage_impl *ps;
02037     HRESULT hr;
02038 
02039     assert(pps);
02040     hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
02041     if (SUCCEEDED(hr))
02042     {
02043         hr = PropertyStorage_ReadFromStream(ps);
02044         if (SUCCEEDED(hr))
02045         {
02046             *pps = &ps->IPropertyStorage_iface;
02047             TRACE("PropertyStorage %p constructed\n", ps);
02048             hr = S_OK;
02049         }
02050         else
02051         {
02052             PropertyStorage_DestroyDictionaries(ps);
02053             HeapFree(GetProcessHeap(), 0, ps);
02054         }
02055     }
02056     return hr;
02057 }
02058 
02059 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
02060  REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
02061 {
02062     PropertyStorage_impl *ps;
02063     HRESULT hr;
02064 
02065     assert(pps);
02066     hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
02067     if (SUCCEEDED(hr))
02068     {
02069         ps->format = 0;
02070         ps->grfFlags = grfFlags;
02071         if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
02072             ps->format = 1;
02073         /* default to Unicode unless told not to, as specified on msdn */
02074         if (ps->grfFlags & PROPSETFLAG_ANSI)
02075             ps->codePage = GetACP();
02076         else
02077             ps->codePage = CP_UNICODE;
02078         ps->locale = LOCALE_SYSTEM_DEFAULT;
02079         TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
02080         *pps = &ps->IPropertyStorage_iface;
02081         TRACE("PropertyStorage %p constructed\n", ps);
02082         hr = S_OK;
02083     }
02084     return hr;
02085 }
02086 
02087 
02088 /***********************************************************************
02089  * Implementation of IPropertySetStorage
02090  */
02091 
02092 /************************************************************************
02093  * IPropertySetStorage_fnQueryInterface (IUnknown)
02094  *
02095  *  This method forwards to the common QueryInterface implementation
02096  */
02097 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
02098     IPropertySetStorage *ppstg,
02099     REFIID riid,
02100     void** ppvObject)
02101 {
02102     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
02103     return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
02104 }
02105 
02106 /************************************************************************
02107  * IPropertySetStorage_fnAddRef (IUnknown)
02108  *
02109  *  This method forwards to the common AddRef implementation
02110  */
02111 static ULONG WINAPI IPropertySetStorage_fnAddRef(
02112     IPropertySetStorage *ppstg)
02113 {
02114     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
02115     return IStorage_AddRef( (IStorage*)This );
02116 }
02117 
02118 /************************************************************************
02119  * IPropertySetStorage_fnRelease (IUnknown)
02120  *
02121  *  This method forwards to the common Release implementation
02122  */
02123 static ULONG WINAPI IPropertySetStorage_fnRelease(
02124     IPropertySetStorage *ppstg)
02125 {
02126     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
02127     return IStorage_Release( (IStorage*)This );
02128 }
02129 
02130 /************************************************************************
02131  * IPropertySetStorage_fnCreate (IPropertySetStorage)
02132  */
02133 static HRESULT WINAPI IPropertySetStorage_fnCreate(
02134     IPropertySetStorage *ppstg,
02135     REFFMTID rfmtid,
02136     const CLSID* pclsid,
02137     DWORD grfFlags,
02138     DWORD grfMode,
02139     IPropertyStorage** ppprstg)
02140 {
02141     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
02142     WCHAR name[CCH_MAX_PROPSTG_NAME];
02143     IStream *stm = NULL;
02144     HRESULT r;
02145 
02146     TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
02147      grfMode, ppprstg);
02148 
02149     /* be picky */
02150     if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
02151     {
02152         r = STG_E_INVALIDFLAG;
02153         goto end;
02154     }
02155 
02156     if (!rfmtid)
02157     {
02158         r = E_INVALIDARG;
02159         goto end;
02160     }
02161 
02162     /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
02163      * storage, not a stream.  For now, disallow it.
02164      */
02165     if (grfFlags & PROPSETFLAG_NONSIMPLE)
02166     {
02167         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
02168         r = STG_E_INVALIDFLAG;
02169         goto end;
02170     }
02171 
02172     r = FmtIdToPropStgName(rfmtid, name);
02173     if (FAILED(r))
02174         goto end;
02175 
02176     r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
02177     if (FAILED(r))
02178         goto end;
02179 
02180     r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
02181 
02182 end:
02183     TRACE("returning 0x%08x\n", r);
02184     return r;
02185 }
02186 
02187 /************************************************************************
02188  * IPropertySetStorage_fnOpen (IPropertySetStorage)
02189  */
02190 static HRESULT WINAPI IPropertySetStorage_fnOpen(
02191     IPropertySetStorage *ppstg,
02192     REFFMTID rfmtid,
02193     DWORD grfMode,
02194     IPropertyStorage** ppprstg)
02195 {
02196     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
02197     IStream *stm = NULL;
02198     WCHAR name[CCH_MAX_PROPSTG_NAME];
02199     HRESULT r;
02200 
02201     TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
02202 
02203     /* be picky */
02204     if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
02205         grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
02206     {
02207         r = STG_E_INVALIDFLAG;
02208         goto end;
02209     }
02210 
02211     if (!rfmtid)
02212     {
02213         r = E_INVALIDARG;
02214         goto end;
02215     }
02216 
02217     r = FmtIdToPropStgName(rfmtid, name);
02218     if (FAILED(r))
02219         goto end;
02220 
02221     r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
02222     if (FAILED(r))
02223         goto end;
02224 
02225     r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
02226 
02227 end:
02228     TRACE("returning 0x%08x\n", r);
02229     return r;
02230 }
02231 
02232 /************************************************************************
02233  * IPropertySetStorage_fnDelete (IPropertySetStorage)
02234  */
02235 static HRESULT WINAPI IPropertySetStorage_fnDelete(
02236     IPropertySetStorage *ppstg,
02237     REFFMTID rfmtid)
02238 {
02239     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
02240     IStorage *stg = NULL;
02241     WCHAR name[CCH_MAX_PROPSTG_NAME];
02242     HRESULT r;
02243 
02244     TRACE("%p %s\n", This, debugstr_guid(rfmtid));
02245 
02246     if (!rfmtid)
02247         return E_INVALIDARG;
02248 
02249     r = FmtIdToPropStgName(rfmtid, name);
02250     if (FAILED(r))
02251         return r;
02252 
02253     stg = (IStorage*) This;
02254     return IStorage_DestroyElement(stg, name);
02255 }
02256 
02257 /************************************************************************
02258  * IPropertySetStorage_fnEnum (IPropertySetStorage)
02259  */
02260 static HRESULT WINAPI IPropertySetStorage_fnEnum(
02261     IPropertySetStorage *ppstg,
02262     IEnumSTATPROPSETSTG** ppenum)
02263 {
02264     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
02265     return create_EnumSTATPROPSETSTG(This, ppenum);
02266 }
02267 
02268 /************************************************************************
02269  * Implement IEnumSTATPROPSETSTG using enumx
02270  */
02271 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
02272     IEnumSTATPROPSETSTG *iface,
02273     REFIID riid,
02274     void** ppvObject)
02275 {
02276     return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
02277 }
02278 
02279 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
02280     IEnumSTATPROPSETSTG *iface)
02281 {
02282     return enumx_AddRef((enumx_impl*)iface);
02283 }
02284 
02285 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
02286     IEnumSTATPROPSETSTG *iface)
02287 {
02288     return enumx_Release((enumx_impl*)iface);
02289 }
02290 
02291 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
02292     IEnumSTATPROPSETSTG *iface,
02293     ULONG celt,
02294     STATPROPSETSTG *rgelt,
02295     ULONG *pceltFetched)
02296 {
02297     return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
02298 }
02299 
02300 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
02301     IEnumSTATPROPSETSTG *iface,
02302     ULONG celt)
02303 {
02304     return enumx_Skip((enumx_impl*)iface, celt);
02305 }
02306 
02307 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
02308     IEnumSTATPROPSETSTG *iface)
02309 {
02310     return enumx_Reset((enumx_impl*)iface);
02311 }
02312 
02313 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
02314     IEnumSTATPROPSETSTG *iface,
02315     IEnumSTATPROPSETSTG **ppenum)
02316 {
02317     return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
02318 }
02319 
02320 static HRESULT create_EnumSTATPROPSETSTG(
02321     StorageImpl *This,
02322     IEnumSTATPROPSETSTG** ppenum)
02323 {
02324     IStorage *stg = (IStorage*) &This->base.lpVtbl;
02325     IEnumSTATSTG *penum = NULL;
02326     STATSTG stat;
02327     ULONG count;
02328     HRESULT r;
02329     STATPROPSETSTG statpss;
02330     enumx_impl *enumx;
02331 
02332     TRACE("%p %p\n", This, ppenum);
02333 
02334     enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
02335                            &IEnumSTATPROPSETSTG_Vtbl,
02336                            sizeof (STATPROPSETSTG));
02337 
02338     /* add all the property set elements into a list */
02339     r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
02340     if (FAILED(r))
02341         return E_OUTOFMEMORY;
02342 
02343     while (1)
02344     {
02345         count = 0;
02346         r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
02347         if (FAILED(r))
02348             break;
02349         if (!count)
02350             break;
02351         if (!stat.pwcsName)
02352             continue;
02353         if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
02354         {
02355             PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
02356             TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
02357                   debugstr_guid(&statpss.fmtid));
02358             statpss.mtime = stat.mtime;
02359             statpss.atime = stat.atime;
02360             statpss.ctime = stat.ctime;
02361             statpss.grfFlags = stat.grfMode;
02362             statpss.clsid = stat.clsid;
02363             enumx_add_element(enumx, &statpss);
02364         }
02365         CoTaskMemFree(stat.pwcsName);
02366     }
02367     IEnumSTATSTG_Release(penum);
02368 
02369     *ppenum = (IEnumSTATPROPSETSTG*) enumx;
02370 
02371     return S_OK;
02372 }
02373 
02374 /************************************************************************
02375  * Implement IEnumSTATPROPSTG using enumx
02376  */
02377 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
02378     IEnumSTATPROPSTG *iface,
02379     REFIID riid,
02380     void** ppvObject)
02381 {
02382     return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
02383 }
02384 
02385 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
02386     IEnumSTATPROPSTG *iface)
02387 {
02388     return enumx_AddRef((enumx_impl*)iface);
02389 }
02390 
02391 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
02392     IEnumSTATPROPSTG *iface)
02393 {
02394     return enumx_Release((enumx_impl*)iface);
02395 }
02396 
02397 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
02398     IEnumSTATPROPSTG *iface,
02399     ULONG celt,
02400     STATPROPSTG *rgelt,
02401     ULONG *pceltFetched)
02402 {
02403     return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
02404 }
02405 
02406 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
02407     IEnumSTATPROPSTG *iface,
02408     ULONG celt)
02409 {
02410     return enumx_Skip((enumx_impl*)iface, celt);
02411 }
02412 
02413 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
02414     IEnumSTATPROPSTG *iface)
02415 {
02416     return enumx_Reset((enumx_impl*)iface);
02417 }
02418 
02419 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
02420     IEnumSTATPROPSTG *iface,
02421     IEnumSTATPROPSTG **ppenum)
02422 {
02423     return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
02424 }
02425 
02426 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
02427 {
02428     enumx_impl *enumx = arg;
02429     PROPID propid = PtrToUlong(k);
02430     const PROPVARIANT *prop = v;
02431     STATPROPSTG stat;
02432 
02433     stat.lpwstrName = NULL;
02434     stat.propid = propid;
02435     stat.vt = prop->vt;
02436 
02437     enumx_add_element(enumx, &stat);
02438 
02439     return TRUE;
02440 }
02441 
02442 static HRESULT create_EnumSTATPROPSTG(
02443     PropertyStorage_impl *This,
02444     IEnumSTATPROPSTG** ppenum)
02445 {
02446     enumx_impl *enumx;
02447 
02448     TRACE("%p %p\n", This, ppenum);
02449 
02450     enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
02451                            &IEnumSTATPROPSTG_Vtbl,
02452                            sizeof (STATPROPSTG));
02453 
02454     dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
02455 
02456     *ppenum = (IEnumSTATPROPSTG*) enumx;
02457 
02458     return S_OK;
02459 }
02460 
02461 /***********************************************************************
02462  * vtables
02463  */
02464 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
02465 {
02466     IPropertySetStorage_fnQueryInterface,
02467     IPropertySetStorage_fnAddRef,
02468     IPropertySetStorage_fnRelease,
02469     IPropertySetStorage_fnCreate,
02470     IPropertySetStorage_fnOpen,
02471     IPropertySetStorage_fnDelete,
02472     IPropertySetStorage_fnEnum
02473 };
02474 
02475 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
02476 {
02477     IPropertyStorage_fnQueryInterface,
02478     IPropertyStorage_fnAddRef,
02479     IPropertyStorage_fnRelease,
02480     IPropertyStorage_fnReadMultiple,
02481     IPropertyStorage_fnWriteMultiple,
02482     IPropertyStorage_fnDeleteMultiple,
02483     IPropertyStorage_fnReadPropertyNames,
02484     IPropertyStorage_fnWritePropertyNames,
02485     IPropertyStorage_fnDeletePropertyNames,
02486     IPropertyStorage_fnCommit,
02487     IPropertyStorage_fnRevert,
02488     IPropertyStorage_fnEnum,
02489     IPropertyStorage_fnSetTimes,
02490     IPropertyStorage_fnSetClass,
02491     IPropertyStorage_fnStat,
02492 };
02493 
02494 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
02495 {
02496     IEnumSTATPROPSETSTG_fnQueryInterface,
02497     IEnumSTATPROPSETSTG_fnAddRef,
02498     IEnumSTATPROPSETSTG_fnRelease,
02499     IEnumSTATPROPSETSTG_fnNext,
02500     IEnumSTATPROPSETSTG_fnSkip,
02501     IEnumSTATPROPSETSTG_fnReset,
02502     IEnumSTATPROPSETSTG_fnClone,
02503 };
02504 
02505 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
02506 {
02507     IEnumSTATPROPSTG_fnQueryInterface,
02508     IEnumSTATPROPSTG_fnAddRef,
02509     IEnumSTATPROPSTG_fnRelease,
02510     IEnumSTATPROPSTG_fnNext,
02511     IEnumSTATPROPSTG_fnSkip,
02512     IEnumSTATPROPSTG_fnReset,
02513     IEnumSTATPROPSTG_fnClone,
02514 };
02515 
02516 /***********************************************************************
02517  * Format ID <-> name conversion
02518  */
02519 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
02520  'I','n','f','o','r','m','a','t','i','o','n',0 };
02521 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
02522  'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
02523 
02524 #define BITS_PER_BYTE    8
02525 #define CHARMASK         0x1f
02526 #define BITS_IN_CHARMASK 5
02527 #define NUM_ALPHA_CHARS  26
02528 
02529 /***********************************************************************
02530  * FmtIdToPropStgName                   [ole32.@]
02531  * Returns the storage name of the format ID rfmtid.
02532  * PARAMS
02533  *  rfmtid [I] Format ID for which to return a storage name
02534  *  str    [O] Storage name associated with rfmtid.
02535  *
02536  * RETURNS
02537  *  E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
02538  *
02539  * NOTES
02540  * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
02541  */
02542 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
02543 {
02544     static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
02545 
02546     TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
02547 
02548     if (!rfmtid) return E_INVALIDARG;
02549     if (!str) return E_INVALIDARG;
02550 
02551     if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
02552         lstrcpyW(str, szSummaryInfo);
02553     else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
02554         lstrcpyW(str, szDocSummaryInfo);
02555     else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
02556         lstrcpyW(str, szDocSummaryInfo);
02557     else
02558     {
02559         const BYTE *fmtptr;
02560         WCHAR *pstr = str;
02561         ULONG bitsRemaining = BITS_PER_BYTE;
02562 
02563         *pstr++ = 5;
02564         for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
02565         {
02566             ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
02567 
02568             if (bitsRemaining >= BITS_IN_CHARMASK)
02569             {
02570                 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
02571                 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
02572                  *pstr <= 'z')
02573                     *pstr += 'A' - 'a';
02574                 pstr++;
02575                 bitsRemaining -= BITS_IN_CHARMASK;
02576                 if (bitsRemaining == 0)
02577                 {
02578                     fmtptr++;
02579                     bitsRemaining = BITS_PER_BYTE;
02580                 }
02581             }
02582             else
02583             {
02584                 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
02585                     i |= *fmtptr << bitsRemaining;
02586                 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
02587                 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
02588             }
02589         }
02590         *pstr = 0;
02591     }
02592     TRACE("returning %s\n", debugstr_w(str));
02593     return S_OK;
02594 }
02595 
02596 /***********************************************************************
02597  * PropStgNameToFmtId                   [ole32.@]
02598  * Returns the format ID corresponding to the given name.
02599  * PARAMS
02600  *  str    [I] Storage name to convert to a format ID.
02601  *  rfmtid [O] Format ID corresponding to str.
02602  *
02603  * RETURNS
02604  *  E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
02605  *  a format ID, S_OK otherwise.
02606  */
02607 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
02608 {
02609     HRESULT hr = STG_E_INVALIDNAME;
02610 
02611     TRACE("%s, %p\n", debugstr_w(str), rfmtid);
02612 
02613     if (!rfmtid) return E_INVALIDARG;
02614     if (!str) return STG_E_INVALIDNAME;
02615 
02616     if (!lstrcmpiW(str, szDocSummaryInfo))
02617     {
02618         *rfmtid = FMTID_DocSummaryInformation;
02619         hr = S_OK;
02620     }
02621     else if (!lstrcmpiW(str, szSummaryInfo))
02622     {
02623         *rfmtid = FMTID_SummaryInformation;
02624         hr = S_OK;
02625     }
02626     else
02627     {
02628         ULONG bits;
02629         BYTE *fmtptr = (BYTE *)rfmtid - 1;
02630         const WCHAR *pstr = str;
02631 
02632         memset(rfmtid, 0, sizeof(*rfmtid));
02633         for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
02634          bits += BITS_IN_CHARMASK)
02635         {
02636             ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
02637             WCHAR wc;
02638 
02639             if (bitsUsed == 0)
02640                 fmtptr++;
02641             wc = *++pstr - 'A';
02642             if (wc > NUM_ALPHA_CHARS)
02643             {
02644                 wc += 'A' - 'a';
02645                 if (wc > NUM_ALPHA_CHARS)
02646                 {
02647                     wc += 'a' - '0' + NUM_ALPHA_CHARS;
02648                     if (wc > CHARMASK)
02649                     {
02650                         WARN("invalid character (%d)\n", *pstr);
02651                         goto end;
02652                     }
02653                 }
02654             }
02655             *fmtptr |= wc << bitsUsed;
02656             bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
02657             if (bitsStored < BITS_IN_CHARMASK)
02658             {
02659                 wc >>= BITS_PER_BYTE - bitsUsed;
02660                 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
02661                 {
02662                     if (wc != 0)
02663                     {
02664                         WARN("extra bits\n");
02665                         goto end;
02666                     }
02667                     break;
02668                 }
02669                 fmtptr++;
02670                 *fmtptr |= (BYTE)wc;
02671             }
02672         }
02673         hr = S_OK;
02674     }
02675 end:
02676     return hr;
02677 }

Generated on Sun May 27 2012 04:25:40 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.