xref: /reactos/dll/win32/ole32/stg_prop.c (revision 8c2e9189)
1 /*
2  * Compound Storage (32 bit version)
3  * Storage implementation
4  *
5  * This file contains the compound file implementation
6  * of the storage interface.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Sylvain St-Germain
10  * Copyright 1999 Thuy Nguyen
11  * Copyright 2005 Mike McCormack
12  * Copyright 2005 Juan Lang
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27  *
28  * TODO:
29  * - I don't honor the maximum property set size.
30  * - Certain bogus files could result in reading past the end of a buffer.
31  * - Mac-generated files won't be read correctly, even if they're little
32  *   endian, because I disregard whether the generator was a Mac.  This means
33  *   strings will probably be munged (as I don't understand Mac scripts.)
34  * - Not all PROPVARIANT types are supported.
35  * - User defined properties are not supported, see comment in
36  *   PropertyStorage_ReadFromStream
37  */
38 
39 #include "config.h"
40 #include "wine/port.h"
41 
42 #include <assert.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #define COBJMACROS
49 #define NONAMELESSUNION
50 
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winnls.h"
54 #include "winuser.h"
55 #include "wine/unicode.h"
56 #include "wine/debug.h"
57 #include "dictionary.h"
58 #include "storage32.h"
59 #include "enumx.h"
60 #include "oleauto.h"
61 
62 WINE_DEFAULT_DEBUG_CHANNEL(storage);
63 
64 #ifdef _MSC_VER
65 #define __ASM_STDCALL_FUNC(name,args,code)
66 #endif
67 
68 static inline StorageImpl *impl_from_IPropertySetStorage( IPropertySetStorage *iface )
69 {
70     return CONTAINING_RECORD(iface, StorageImpl, base.IPropertySetStorage_iface);
71 }
72 
73 /* These are documented in MSDN,
74  * but they don't seem to be in any header file.
75  */
76 #define PROPSETHDR_BYTEORDER_MAGIC      0xfffe
77 #define PROPSETHDR_OSVER_KIND_WIN16     0
78 #define PROPSETHDR_OSVER_KIND_MAC       1
79 #define PROPSETHDR_OSVER_KIND_WIN32     2
80 
81 #define CP_UNICODE 1200
82 
83 #define MAX_VERSION_0_PROP_NAME_LENGTH 256
84 
85 #define CFTAG_WINDOWS   (-1L)
86 #define CFTAG_MACINTOSH (-2L)
87 #define CFTAG_FMTID     (-3L)
88 #define CFTAG_NODATA      0L
89 
90 typedef struct tagPROPERTYSETHEADER
91 {
92     WORD  wByteOrder; /* always 0xfffe */
93     WORD  wFormat;    /* can be zero or one */
94     DWORD dwOSVer;    /* OS version of originating system */
95     CLSID clsid;      /* application CLSID */
96     DWORD reserved;   /* always 1 */
97 } PROPERTYSETHEADER;
98 
99 typedef struct tagFORMATIDOFFSET
100 {
101     FMTID fmtid;
102     DWORD dwOffset; /* from beginning of stream */
103 } FORMATIDOFFSET;
104 
105 typedef struct tagPROPERTYSECTIONHEADER
106 {
107     DWORD cbSection;
108     DWORD cProperties;
109 } PROPERTYSECTIONHEADER;
110 
111 typedef struct tagPROPERTYIDOFFSET
112 {
113     DWORD propid;
114     DWORD dwOffset; /* from beginning of section */
115 } PROPERTYIDOFFSET;
116 
117 typedef struct tagPropertyStorage_impl PropertyStorage_impl;
118 
119 /* Initializes the property storage from the stream (and undoes any uncommitted
120  * changes in the process.)  Returns an error if there is an error reading or
121  * if the stream format doesn't match what's expected.
122  */
123 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *);
124 
125 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *);
126 
127 /* Creates the dictionaries used by the property storage.  If successful, all
128  * the dictionaries have been created.  If failed, none has been.  (This makes
129  * it a bit easier to deal with destroying them.)
130  */
131 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *);
132 
133 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *);
134 
135 /* Copies from propvar to prop.  If propvar's type is VT_LPSTR, copies the
136  * string using PropertyStorage_StringCopy.
137  */
138 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
139  const PROPVARIANT *propvar, LCID targetCP, LCID srcCP);
140 
141 /* Copies the string src, which is encoded using code page srcCP, and returns
142  * it in *dst, in the code page specified by targetCP.  The returned string is
143  * allocated using CoTaskMemAlloc.
144  * If srcCP is CP_UNICODE, src is in fact an LPCWSTR.  Similarly, if targetCP
145  * is CP_UNICODE, the returned string is in fact an LPWSTR.
146  * Returns S_OK on success, something else on failure.
147  */
148 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
149  LCID targetCP);
150 
151 static const IPropertyStorageVtbl IPropertyStorage_Vtbl;
152 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl;
153 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl;
154 static HRESULT create_EnumSTATPROPSETSTG(StorageImpl *, IEnumSTATPROPSETSTG**);
155 static HRESULT create_EnumSTATPROPSTG(PropertyStorage_impl *, IEnumSTATPROPSTG**);
156 
157 /***********************************************************************
158  * Implementation of IPropertyStorage
159  */
160 struct tagPropertyStorage_impl
161 {
162     IPropertyStorage IPropertyStorage_iface;
163     LONG ref;
164     CRITICAL_SECTION cs;
165     IStream *stm;
166     BOOL  dirty;
167     FMTID fmtid;
168     CLSID clsid;
169     WORD  format;
170     DWORD originatorOS;
171     DWORD grfFlags;
172     DWORD grfMode;
173     UINT  codePage;
174     LCID  locale;
175     PROPID highestProp;
176     struct dictionary *name_to_propid;
177     struct dictionary *propid_to_name;
178     struct dictionary *propid_to_prop;
179 };
180 
181 static inline PropertyStorage_impl *impl_from_IPropertyStorage(IPropertyStorage *iface)
182 {
183     return CONTAINING_RECORD(iface, PropertyStorage_impl, IPropertyStorage_iface);
184 }
185 
186 /************************************************************************
187  * IPropertyStorage_fnQueryInterface (IPropertyStorage)
188  */
189 static HRESULT WINAPI IPropertyStorage_fnQueryInterface(
190     IPropertyStorage *iface,
191     REFIID riid,
192     void** ppvObject)
193 {
194     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
195 
196     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
197 
198     if (!ppvObject)
199         return E_INVALIDARG;
200 
201     *ppvObject = 0;
202 
203     if (IsEqualGUID(&IID_IUnknown, riid) ||
204         IsEqualGUID(&IID_IPropertyStorage, riid))
205     {
206         IPropertyStorage_AddRef(iface);
207         *ppvObject = iface;
208         return S_OK;
209     }
210 
211     return E_NOINTERFACE;
212 }
213 
214 /************************************************************************
215  * IPropertyStorage_fnAddRef (IPropertyStorage)
216  */
217 static ULONG WINAPI IPropertyStorage_fnAddRef(
218     IPropertyStorage *iface)
219 {
220     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
221     return InterlockedIncrement(&This->ref);
222 }
223 
224 /************************************************************************
225  * IPropertyStorage_fnRelease (IPropertyStorage)
226  */
227 static ULONG WINAPI IPropertyStorage_fnRelease(
228     IPropertyStorage *iface)
229 {
230     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
231     ULONG ref;
232 
233     ref = InterlockedDecrement(&This->ref);
234     if (ref == 0)
235     {
236         TRACE("Destroying %p\n", This);
237         if (This->dirty)
238             IPropertyStorage_Commit(iface, STGC_DEFAULT);
239         IStream_Release(This->stm);
240         This->cs.DebugInfo->Spare[0] = 0;
241         DeleteCriticalSection(&This->cs);
242         PropertyStorage_DestroyDictionaries(This);
243         HeapFree(GetProcessHeap(), 0, This);
244     }
245     return ref;
246 }
247 
248 static PROPVARIANT *PropertyStorage_FindProperty(PropertyStorage_impl *This,
249  DWORD propid)
250 {
251     PROPVARIANT *ret = NULL;
252 
253     dictionary_find(This->propid_to_prop, UlongToPtr(propid), (void **)&ret);
254     TRACE("returning %p\n", ret);
255     return ret;
256 }
257 
258 /* Returns NULL if name is NULL. */
259 static PROPVARIANT *PropertyStorage_FindPropertyByName(
260  PropertyStorage_impl *This, LPCWSTR name)
261 {
262     PROPVARIANT *ret = NULL;
263     void *propid;
264 
265     if (!name)
266         return NULL;
267     if (This->codePage == CP_UNICODE)
268     {
269         if (dictionary_find(This->name_to_propid, name, &propid))
270             ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
271     }
272     else
273     {
274         LPSTR ansiName;
275         HRESULT hr = PropertyStorage_StringCopy((LPCSTR)name, CP_UNICODE,
276          &ansiName, This->codePage);
277 
278         if (SUCCEEDED(hr))
279         {
280             if (dictionary_find(This->name_to_propid, ansiName, &propid))
281                 ret = PropertyStorage_FindProperty(This, PtrToUlong(propid));
282             CoTaskMemFree(ansiName);
283         }
284     }
285     TRACE("returning %p\n", ret);
286     return ret;
287 }
288 
289 static LPWSTR PropertyStorage_FindPropertyNameById(PropertyStorage_impl *This,
290  DWORD propid)
291 {
292     LPWSTR ret = NULL;
293 
294     dictionary_find(This->propid_to_name, UlongToPtr(propid), (void **)&ret);
295     TRACE("returning %p\n", ret);
296     return ret;
297 }
298 
299 /************************************************************************
300  * IPropertyStorage_fnReadMultiple (IPropertyStorage)
301  */
302 static HRESULT WINAPI IPropertyStorage_fnReadMultiple(
303     IPropertyStorage* iface,
304     ULONG cpspec,
305     const PROPSPEC rgpspec[],
306     PROPVARIANT rgpropvar[])
307 {
308     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
309     HRESULT hr = S_OK;
310     ULONG i;
311 
312     TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
313 
314     if (!cpspec)
315         return S_FALSE;
316     if (!rgpspec || !rgpropvar)
317         return E_INVALIDARG;
318     EnterCriticalSection(&This->cs);
319     for (i = 0; i < cpspec; i++)
320     {
321         PropVariantInit(&rgpropvar[i]);
322         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
323         {
324             PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
325              rgpspec[i].u.lpwstr);
326 
327             if (prop)
328                 PropertyStorage_PropVariantCopy(&rgpropvar[i], prop, GetACP(),
329                  This->codePage);
330         }
331         else
332         {
333             switch (rgpspec[i].u.propid)
334             {
335                 case PID_CODEPAGE:
336                     rgpropvar[i].vt = VT_I2;
337                     rgpropvar[i].u.iVal = This->codePage;
338                     break;
339                 case PID_LOCALE:
340                     rgpropvar[i].vt = VT_I4;
341                     rgpropvar[i].u.lVal = This->locale;
342                     break;
343                 default:
344                 {
345                     PROPVARIANT *prop = PropertyStorage_FindProperty(This,
346                      rgpspec[i].u.propid);
347 
348                     if (prop)
349                         PropertyStorage_PropVariantCopy(&rgpropvar[i], prop,
350                          GetACP(), This->codePage);
351                     else
352                         hr = S_FALSE;
353                 }
354             }
355         }
356     }
357     LeaveCriticalSection(&This->cs);
358     return hr;
359 }
360 
361 static HRESULT PropertyStorage_StringCopy(LPCSTR src, LCID srcCP, LPSTR *dst,
362  LCID dstCP)
363 {
364     HRESULT hr = S_OK;
365     int len;
366 
367     TRACE("%s, %p, %d, %d\n",
368      srcCP == CP_UNICODE ? debugstr_w((LPCWSTR)src) : debugstr_a(src), dst,
369      dstCP, srcCP);
370     assert(src);
371     assert(dst);
372     *dst = NULL;
373     if (dstCP == srcCP)
374     {
375         size_t len;
376 
377         if (dstCP == CP_UNICODE)
378             len = (strlenW((LPCWSTR)src) + 1) * sizeof(WCHAR);
379         else
380             len = strlen(src) + 1;
381         *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
382         if (!*dst)
383             hr = STG_E_INSUFFICIENTMEMORY;
384         else
385             memcpy(*dst, src, len);
386     }
387     else
388     {
389         if (dstCP == CP_UNICODE)
390         {
391             len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
392             *dst = CoTaskMemAlloc(len * sizeof(WCHAR));
393             if (!*dst)
394                 hr = STG_E_INSUFFICIENTMEMORY;
395             else
396                 MultiByteToWideChar(srcCP, 0, src, -1, (LPWSTR)*dst, len);
397         }
398         else
399         {
400             LPCWSTR wideStr = NULL;
401             LPWSTR wideStr_tmp = NULL;
402 
403             if (srcCP == CP_UNICODE)
404                 wideStr = (LPCWSTR)src;
405             else
406             {
407                 len = MultiByteToWideChar(srcCP, 0, src, -1, NULL, 0);
408                 wideStr_tmp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
409                 if (wideStr_tmp)
410                 {
411                     MultiByteToWideChar(srcCP, 0, src, -1, wideStr_tmp, len);
412                     wideStr = wideStr_tmp;
413                 }
414                 else
415                     hr = STG_E_INSUFFICIENTMEMORY;
416             }
417             if (SUCCEEDED(hr))
418             {
419                 len = WideCharToMultiByte(dstCP, 0, wideStr, -1, NULL, 0,
420                  NULL, NULL);
421                 *dst = CoTaskMemAlloc(len);
422                 if (!*dst)
423                     hr = STG_E_INSUFFICIENTMEMORY;
424                 else
425                 {
426                     BOOL defCharUsed = FALSE;
427 
428                     if (WideCharToMultiByte(dstCP, 0, wideStr, -1, *dst, len,
429                      NULL, &defCharUsed) == 0 || defCharUsed)
430                     {
431                         CoTaskMemFree(*dst);
432                         *dst = NULL;
433                         hr = HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
434                     }
435                 }
436             }
437             HeapFree(GetProcessHeap(), 0, wideStr_tmp);
438         }
439     }
440     TRACE("returning 0x%08x (%s)\n", hr,
441      dstCP == CP_UNICODE ? debugstr_w((LPCWSTR)*dst) : debugstr_a(*dst));
442     return hr;
443 }
444 
445 static HRESULT PropertyStorage_PropVariantCopy(PROPVARIANT *prop,
446  const PROPVARIANT *propvar, LCID targetCP, LCID srcCP)
447 {
448     HRESULT hr = S_OK;
449 
450     assert(prop);
451     assert(propvar);
452     if (propvar->vt == VT_LPSTR)
453     {
454         hr = PropertyStorage_StringCopy(propvar->u.pszVal, srcCP,
455          &prop->u.pszVal, targetCP);
456         if (SUCCEEDED(hr))
457             prop->vt = VT_LPSTR;
458     }
459     else
460         PropVariantCopy(prop, propvar);
461     return hr;
462 }
463 
464 /* Stores the property with id propid and value propvar into this property
465  * storage.  lcid is ignored if propvar's type is not VT_LPSTR.  If propvar's
466  * type is VT_LPSTR, converts the string using lcid as the source code page
467  * and This->codePage as the target code page before storing.
468  * As a side effect, may change This->format to 1 if the type of propvar is
469  * a version 1-only property.
470  */
471 static HRESULT PropertyStorage_StorePropWithId(PropertyStorage_impl *This,
472  PROPID propid, const PROPVARIANT *propvar, LCID lcid)
473 {
474     HRESULT hr = S_OK;
475     PROPVARIANT *prop = PropertyStorage_FindProperty(This, propid);
476 
477     assert(propvar);
478     if (propvar->vt & VT_BYREF || propvar->vt & VT_ARRAY)
479         This->format = 1;
480     switch (propvar->vt)
481     {
482     case VT_DECIMAL:
483     case VT_I1:
484     case VT_INT:
485     case VT_UINT:
486     case VT_VECTOR|VT_I1:
487         This->format = 1;
488     }
489     TRACE("Setting 0x%08x to type %d\n", propid, propvar->vt);
490     if (prop)
491     {
492         PropVariantClear(prop);
493         hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
494          lcid);
495     }
496     else
497     {
498         prop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
499          sizeof(PROPVARIANT));
500         if (prop)
501         {
502             hr = PropertyStorage_PropVariantCopy(prop, propvar, This->codePage,
503              lcid);
504             if (SUCCEEDED(hr))
505             {
506                 dictionary_insert(This->propid_to_prop, UlongToPtr(propid), prop);
507                 if (propid > This->highestProp)
508                     This->highestProp = propid;
509             }
510             else
511                 HeapFree(GetProcessHeap(), 0, prop);
512         }
513         else
514             hr = STG_E_INSUFFICIENTMEMORY;
515     }
516     return hr;
517 }
518 
519 /* Adds the name srcName to the name dictionaries, mapped to property ID id.
520  * srcName is encoded in code page cp, and is converted to This->codePage.
521  * If cp is CP_UNICODE, srcName is actually a unicode string.
522  * As a side effect, may change This->format to 1 if srcName is too long for
523  * a version 0 property storage.
524  * Doesn't validate id.
525  */
526 static HRESULT PropertyStorage_StoreNameWithId(PropertyStorage_impl *This,
527  LPCSTR srcName, LCID cp, PROPID id)
528 {
529     LPSTR name;
530     HRESULT hr;
531 
532     assert(srcName);
533 
534     hr = PropertyStorage_StringCopy(srcName, cp, &name, This->codePage);
535     if (SUCCEEDED(hr))
536     {
537         if (This->codePage == CP_UNICODE)
538         {
539             if (lstrlenW((LPWSTR)name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
540                 This->format = 1;
541         }
542         else
543         {
544             if (strlen(name) >= MAX_VERSION_0_PROP_NAME_LENGTH)
545                 This->format = 1;
546         }
547         TRACE("Adding prop name %s, propid %d\n",
548          This->codePage == CP_UNICODE ? debugstr_w((LPCWSTR)name) :
549          debugstr_a(name), id);
550         dictionary_insert(This->name_to_propid, name, UlongToPtr(id));
551         dictionary_insert(This->propid_to_name, UlongToPtr(id), name);
552     }
553     return hr;
554 }
555 
556 /************************************************************************
557  * IPropertyStorage_fnWriteMultiple (IPropertyStorage)
558  */
559 static HRESULT WINAPI IPropertyStorage_fnWriteMultiple(
560     IPropertyStorage* iface,
561     ULONG cpspec,
562     const PROPSPEC rgpspec[],
563     const PROPVARIANT rgpropvar[],
564     PROPID propidNameFirst)
565 {
566     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
567     HRESULT hr = S_OK;
568     ULONG i;
569 
570     TRACE("(%p, %d, %p, %p)\n", iface, cpspec, rgpspec, rgpropvar);
571 
572     if (cpspec && (!rgpspec || !rgpropvar))
573         return E_INVALIDARG;
574     if (!(This->grfMode & STGM_READWRITE))
575         return STG_E_ACCESSDENIED;
576     EnterCriticalSection(&This->cs);
577     This->dirty = TRUE;
578     This->originatorOS = (DWORD)MAKELONG(LOWORD(GetVersion()),
579      PROPSETHDR_OSVER_KIND_WIN32) ;
580     for (i = 0; i < cpspec; i++)
581     {
582         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
583         {
584             PROPVARIANT *prop = PropertyStorage_FindPropertyByName(This,
585              rgpspec[i].u.lpwstr);
586 
587             if (prop)
588                 PropVariantCopy(prop, &rgpropvar[i]);
589             else
590             {
591                 /* Note that I don't do the special cases here that I do below,
592                  * because naming the special PIDs isn't supported.
593                  */
594                 if (propidNameFirst < PID_FIRST_USABLE ||
595                  propidNameFirst >= PID_MIN_READONLY)
596                     hr = STG_E_INVALIDPARAMETER;
597                 else
598                 {
599                     PROPID nextId = max(propidNameFirst, This->highestProp + 1);
600 
601                     hr = PropertyStorage_StoreNameWithId(This,
602                      (LPCSTR)rgpspec[i].u.lpwstr, CP_UNICODE, nextId);
603                     if (SUCCEEDED(hr))
604                         hr = PropertyStorage_StorePropWithId(This, nextId,
605                          &rgpropvar[i], GetACP());
606                 }
607             }
608         }
609         else
610         {
611             switch (rgpspec[i].u.propid)
612             {
613             case PID_DICTIONARY:
614                 /* Can't set the dictionary */
615                 hr = STG_E_INVALIDPARAMETER;
616                 break;
617             case PID_CODEPAGE:
618                 /* Can only set the code page if nothing else has been set */
619                 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
620                  rgpropvar[i].vt == VT_I2)
621                 {
622                     This->codePage = rgpropvar[i].u.iVal;
623                     if (This->codePage == CP_UNICODE)
624                         This->grfFlags &= ~PROPSETFLAG_ANSI;
625                     else
626                         This->grfFlags |= PROPSETFLAG_ANSI;
627                 }
628                 else
629                     hr = STG_E_INVALIDPARAMETER;
630                 break;
631             case PID_LOCALE:
632                 /* Can only set the locale if nothing else has been set */
633                 if (dictionary_num_entries(This->propid_to_prop) == 0 &&
634                  rgpropvar[i].vt == VT_I4)
635                     This->locale = rgpropvar[i].u.lVal;
636                 else
637                     hr = STG_E_INVALIDPARAMETER;
638                 break;
639             case PID_ILLEGAL:
640                 /* silently ignore like MSDN says */
641                 break;
642             default:
643                 if (rgpspec[i].u.propid >= PID_MIN_READONLY)
644                     hr = STG_E_INVALIDPARAMETER;
645                 else
646                     hr = PropertyStorage_StorePropWithId(This,
647                      rgpspec[i].u.propid, &rgpropvar[i], GetACP());
648             }
649         }
650     }
651     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
652         IPropertyStorage_Commit(iface, STGC_DEFAULT);
653     LeaveCriticalSection(&This->cs);
654     return hr;
655 }
656 
657 /************************************************************************
658  * IPropertyStorage_fnDeleteMultiple (IPropertyStorage)
659  */
660 static HRESULT WINAPI IPropertyStorage_fnDeleteMultiple(
661     IPropertyStorage* iface,
662     ULONG cpspec,
663     const PROPSPEC rgpspec[])
664 {
665     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
666     ULONG i;
667     HRESULT hr;
668 
669     TRACE("(%p, %d, %p)\n", iface, cpspec, rgpspec);
670 
671     if (cpspec && !rgpspec)
672         return E_INVALIDARG;
673     if (!(This->grfMode & STGM_READWRITE))
674         return STG_E_ACCESSDENIED;
675     hr = S_OK;
676     EnterCriticalSection(&This->cs);
677     This->dirty = TRUE;
678     for (i = 0; i < cpspec; i++)
679     {
680         if (rgpspec[i].ulKind == PRSPEC_LPWSTR)
681         {
682             void *propid;
683 
684             if (dictionary_find(This->name_to_propid, rgpspec[i].u.lpwstr, &propid))
685                 dictionary_remove(This->propid_to_prop, propid);
686         }
687         else
688         {
689             if (rgpspec[i].u.propid >= PID_FIRST_USABLE &&
690              rgpspec[i].u.propid < PID_MIN_READONLY)
691                 dictionary_remove(This->propid_to_prop, UlongToPtr(rgpspec[i].u.propid));
692             else
693                 hr = STG_E_INVALIDPARAMETER;
694         }
695     }
696     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
697         IPropertyStorage_Commit(iface, STGC_DEFAULT);
698     LeaveCriticalSection(&This->cs);
699     return hr;
700 }
701 
702 /************************************************************************
703  * IPropertyStorage_fnReadPropertyNames (IPropertyStorage)
704  */
705 static HRESULT WINAPI IPropertyStorage_fnReadPropertyNames(
706     IPropertyStorage* iface,
707     ULONG cpropid,
708     const PROPID rgpropid[],
709     LPOLESTR rglpwstrName[])
710 {
711     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
712     ULONG i;
713     HRESULT hr = S_FALSE;
714 
715     TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
716 
717     if (cpropid && (!rgpropid || !rglpwstrName))
718         return E_INVALIDARG;
719     EnterCriticalSection(&This->cs);
720     for (i = 0; i < cpropid && SUCCEEDED(hr); i++)
721     {
722         LPWSTR name = PropertyStorage_FindPropertyNameById(This, rgpropid[i]);
723 
724         if (name)
725         {
726             size_t len = lstrlenW(name);
727 
728             hr = S_OK;
729             rglpwstrName[i] = CoTaskMemAlloc((len + 1) * sizeof(WCHAR));
730             if (rglpwstrName[i])
731                 memcpy(rglpwstrName[i], name, (len + 1) * sizeof(WCHAR));
732             else
733                 hr = STG_E_INSUFFICIENTMEMORY;
734         }
735         else
736             rglpwstrName[i] = NULL;
737     }
738     LeaveCriticalSection(&This->cs);
739     return hr;
740 }
741 
742 /************************************************************************
743  * IPropertyStorage_fnWritePropertyNames (IPropertyStorage)
744  */
745 static HRESULT WINAPI IPropertyStorage_fnWritePropertyNames(
746     IPropertyStorage* iface,
747     ULONG cpropid,
748     const PROPID rgpropid[],
749     const LPOLESTR rglpwstrName[])
750 {
751     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
752     ULONG i;
753     HRESULT hr;
754 
755     TRACE("(%p, %d, %p, %p)\n", iface, cpropid, rgpropid, rglpwstrName);
756 
757     if (cpropid && (!rgpropid || !rglpwstrName))
758         return E_INVALIDARG;
759     if (!(This->grfMode & STGM_READWRITE))
760         return STG_E_ACCESSDENIED;
761     hr = S_OK;
762     EnterCriticalSection(&This->cs);
763     This->dirty = TRUE;
764     for (i = 0; SUCCEEDED(hr) && i < cpropid; i++)
765     {
766         if (rgpropid[i] != PID_ILLEGAL)
767             hr = PropertyStorage_StoreNameWithId(This, (LPCSTR)rglpwstrName[i],
768              CP_UNICODE, rgpropid[i]);
769     }
770     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
771         IPropertyStorage_Commit(iface, STGC_DEFAULT);
772     LeaveCriticalSection(&This->cs);
773     return hr;
774 }
775 
776 /************************************************************************
777  * IPropertyStorage_fnDeletePropertyNames (IPropertyStorage)
778  */
779 static HRESULT WINAPI IPropertyStorage_fnDeletePropertyNames(
780     IPropertyStorage* iface,
781     ULONG cpropid,
782     const PROPID rgpropid[])
783 {
784     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
785     ULONG i;
786     HRESULT hr;
787 
788     TRACE("(%p, %d, %p)\n", iface, cpropid, rgpropid);
789 
790     if (cpropid && !rgpropid)
791         return E_INVALIDARG;
792     if (!(This->grfMode & STGM_READWRITE))
793         return STG_E_ACCESSDENIED;
794     hr = S_OK;
795     EnterCriticalSection(&This->cs);
796     This->dirty = TRUE;
797     for (i = 0; i < cpropid; i++)
798     {
799         LPWSTR name = NULL;
800 
801         if (dictionary_find(This->propid_to_name, UlongToPtr(rgpropid[i]), (void **)&name))
802         {
803             dictionary_remove(This->propid_to_name, UlongToPtr(rgpropid[i]));
804             dictionary_remove(This->name_to_propid, name);
805         }
806     }
807     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
808         IPropertyStorage_Commit(iface, STGC_DEFAULT);
809     LeaveCriticalSection(&This->cs);
810     return hr;
811 }
812 
813 /************************************************************************
814  * IPropertyStorage_fnCommit (IPropertyStorage)
815  */
816 static HRESULT WINAPI IPropertyStorage_fnCommit(
817     IPropertyStorage* iface,
818     DWORD grfCommitFlags)
819 {
820     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
821     HRESULT hr;
822 
823     TRACE("(%p, 0x%08x)\n", iface, grfCommitFlags);
824 
825     if (!(This->grfMode & STGM_READWRITE))
826         return STG_E_ACCESSDENIED;
827     EnterCriticalSection(&This->cs);
828     if (This->dirty)
829         hr = PropertyStorage_WriteToStream(This);
830     else
831         hr = S_OK;
832     LeaveCriticalSection(&This->cs);
833     return hr;
834 }
835 
836 /************************************************************************
837  * IPropertyStorage_fnRevert (IPropertyStorage)
838  */
839 static HRESULT WINAPI IPropertyStorage_fnRevert(
840     IPropertyStorage* iface)
841 {
842     HRESULT hr;
843     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
844 
845     TRACE("%p\n", iface);
846 
847     EnterCriticalSection(&This->cs);
848     if (This->dirty)
849     {
850         PropertyStorage_DestroyDictionaries(This);
851         hr = PropertyStorage_CreateDictionaries(This);
852         if (SUCCEEDED(hr))
853             hr = PropertyStorage_ReadFromStream(This);
854     }
855     else
856         hr = S_OK;
857     LeaveCriticalSection(&This->cs);
858     return hr;
859 }
860 
861 /************************************************************************
862  * IPropertyStorage_fnEnum (IPropertyStorage)
863  */
864 static HRESULT WINAPI IPropertyStorage_fnEnum(
865     IPropertyStorage* iface,
866     IEnumSTATPROPSTG** ppenum)
867 {
868     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
869     return create_EnumSTATPROPSTG(This, ppenum);
870 }
871 
872 /************************************************************************
873  * IPropertyStorage_fnSetTimes (IPropertyStorage)
874  */
875 static HRESULT WINAPI IPropertyStorage_fnSetTimes(
876     IPropertyStorage* iface,
877     const FILETIME* pctime,
878     const FILETIME* patime,
879     const FILETIME* pmtime)
880 {
881     FIXME("\n");
882     return E_NOTIMPL;
883 }
884 
885 /************************************************************************
886  * IPropertyStorage_fnSetClass (IPropertyStorage)
887  */
888 static HRESULT WINAPI IPropertyStorage_fnSetClass(
889     IPropertyStorage* iface,
890     REFCLSID clsid)
891 {
892     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
893 
894     TRACE("%p, %s\n", iface, debugstr_guid(clsid));
895 
896     if (!clsid)
897         return E_INVALIDARG;
898     if (!(This->grfMode & STGM_READWRITE))
899         return STG_E_ACCESSDENIED;
900     This->clsid = *clsid;
901     This->dirty = TRUE;
902     if (This->grfFlags & PROPSETFLAG_UNBUFFERED)
903         IPropertyStorage_Commit(iface, STGC_DEFAULT);
904     return S_OK;
905 }
906 
907 /************************************************************************
908  * IPropertyStorage_fnStat (IPropertyStorage)
909  */
910 static HRESULT WINAPI IPropertyStorage_fnStat(
911     IPropertyStorage* iface,
912     STATPROPSETSTG* statpsstg)
913 {
914     PropertyStorage_impl *This = impl_from_IPropertyStorage(iface);
915     STATSTG stat;
916     HRESULT hr;
917 
918     TRACE("%p, %p\n", iface, statpsstg);
919 
920     if (!statpsstg)
921         return E_INVALIDARG;
922 
923     hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
924     if (SUCCEEDED(hr))
925     {
926         statpsstg->fmtid = This->fmtid;
927         statpsstg->clsid = This->clsid;
928         statpsstg->grfFlags = This->grfFlags;
929         statpsstg->mtime = stat.mtime;
930         statpsstg->ctime = stat.ctime;
931         statpsstg->atime = stat.atime;
932         statpsstg->dwOSVersion = This->originatorOS;
933     }
934     return hr;
935 }
936 
937 static int PropertyStorage_PropNameCompare(const void *a, const void *b,
938  void *extra)
939 {
940     PropertyStorage_impl *This = extra;
941 
942     if (This->codePage == CP_UNICODE)
943     {
944         TRACE("(%s, %s)\n", debugstr_w(a), debugstr_w(b));
945         if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
946             return lstrcmpW(a, b);
947         else
948             return lstrcmpiW(a, b);
949     }
950     else
951     {
952         TRACE("(%s, %s)\n", debugstr_a(a), debugstr_a(b));
953         if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
954             return lstrcmpA(a, b);
955         else
956             return lstrcmpiA(a, b);
957     }
958 }
959 
960 static void PropertyStorage_PropNameDestroy(void *k, void *d, void *extra)
961 {
962     CoTaskMemFree(k);
963 }
964 
965 static int PropertyStorage_PropCompare(const void *a, const void *b,
966  void *extra)
967 {
968     TRACE("(%d, %d)\n", PtrToUlong(a), PtrToUlong(b));
969     return PtrToUlong(a) - PtrToUlong(b);
970 }
971 
972 static void PropertyStorage_PropertyDestroy(void *k, void *d, void *extra)
973 {
974     PropVariantClear(d);
975     HeapFree(GetProcessHeap(), 0, d);
976 }
977 
978 #ifdef WORDS_BIGENDIAN
979 /* Swaps each character in str to or from little endian; assumes the conversion
980  * is symmetric, that is, that lendian16toh is equivalent to htole16.
981  */
982 static void PropertyStorage_ByteSwapString(LPWSTR str, size_t len)
983 {
984     DWORD i;
985 
986     /* Swap characters to host order.
987      * FIXME: alignment?
988      */
989     for (i = 0; i < len; i++)
990         str[i] = lendian16toh(str[i]);
991 }
992 #else
993 #define PropertyStorage_ByteSwapString(s, l)
994 #endif
995 
996 /* Reads the dictionary from the memory buffer beginning at ptr.  Interprets
997  * the entries according to the values of This->codePage and This->locale.
998  * FIXME: there isn't any checking whether the read property extends past the
999  * end of the buffer.
1000  */
1001 static HRESULT PropertyStorage_ReadDictionary(PropertyStorage_impl *This,
1002  BYTE *ptr)
1003 {
1004     DWORD numEntries, i;
1005     HRESULT hr = S_OK;
1006 
1007     assert(This->name_to_propid);
1008     assert(This->propid_to_name);
1009 
1010     StorageUtl_ReadDWord(ptr, 0, &numEntries);
1011     TRACE("Reading %d entries:\n", numEntries);
1012     ptr += sizeof(DWORD);
1013     for (i = 0; SUCCEEDED(hr) && i < numEntries; i++)
1014     {
1015         PROPID propid;
1016         DWORD cbEntry;
1017 
1018         StorageUtl_ReadDWord(ptr, 0, &propid);
1019         ptr += sizeof(PROPID);
1020         StorageUtl_ReadDWord(ptr, 0, &cbEntry);
1021         ptr += sizeof(DWORD);
1022         TRACE("Reading entry with ID 0x%08x, %d bytes\n", propid, cbEntry);
1023         /* Make sure the source string is NULL-terminated */
1024         if (This->codePage != CP_UNICODE)
1025             ptr[cbEntry - 1] = '\0';
1026         else
1027             ((LPWSTR)ptr)[cbEntry - 1] = 0;
1028         hr = PropertyStorage_StoreNameWithId(This, (char*)ptr, This->codePage, propid);
1029         if (This->codePage == CP_UNICODE)
1030         {
1031             /* cbEntry is the number of characters */
1032             cbEntry *= 2;
1033 
1034             /* Unicode entries are padded to DWORD boundaries */
1035             if (cbEntry % sizeof(DWORD))
1036                 ptr += sizeof(DWORD) - (cbEntry % sizeof(DWORD));
1037         }
1038         ptr += cbEntry;
1039     }
1040     return hr;
1041 }
1042 
1043 #ifdef __i386__
1044 #define __thiscall_wrapper __stdcall
1045 #else
1046 #define __thiscall_wrapper __cdecl
1047 #endif
1048 
1049 static void* __thiscall_wrapper Allocate_CoTaskMemAlloc(void *this, ULONG size)
1050 {
1051     return CoTaskMemAlloc(size);
1052 }
1053 
1054 /* FIXME: there isn't any checking whether the read property extends past the
1055  * end of the buffer.
1056  */
1057 static HRESULT PropertyStorage_ReadProperty(PROPVARIANT *prop, const BYTE *data,
1058     UINT codepage, void* (__thiscall_wrapper *allocate)(void *this, ULONG size), void *allocate_data)
1059 {
1060     HRESULT hr = S_OK;
1061 
1062     assert(prop);
1063     assert(data);
1064     StorageUtl_ReadDWord(data, 0, (DWORD *)&prop->vt);
1065     data += sizeof(DWORD);
1066     switch (prop->vt)
1067     {
1068     case VT_EMPTY:
1069     case VT_NULL:
1070         break;
1071     case VT_I1:
1072         prop->u.cVal = *(const char *)data;
1073         TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1074         break;
1075     case VT_UI1:
1076         prop->u.bVal = *data;
1077         TRACE("Read byte 0x%x\n", prop->u.bVal);
1078         break;
1079     case VT_BOOL:
1080         StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.boolVal);
1081         TRACE("Read bool %d\n", prop->u.boolVal);
1082         break;
1083     case VT_I2:
1084         StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1085         TRACE("Read short %d\n", prop->u.iVal);
1086         break;
1087     case VT_UI2:
1088         StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1089         TRACE("Read ushort %d\n", prop->u.uiVal);
1090         break;
1091     case VT_INT:
1092     case VT_I4:
1093         StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1094         TRACE("Read long %d\n", prop->u.lVal);
1095         break;
1096     case VT_UINT:
1097     case VT_UI4:
1098         StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1099         TRACE("Read ulong %d\n", prop->u.ulVal);
1100         break;
1101     case VT_I8:
1102         StorageUtl_ReadULargeInteger(data, 0, (ULARGE_INTEGER *)&prop->u.hVal);
1103         TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->u.hVal.QuadPart));
1104         break;
1105     case VT_UI8:
1106         StorageUtl_ReadULargeInteger(data, 0, &prop->u.uhVal);
1107         TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->u.uhVal.QuadPart));
1108         break;
1109     case VT_R8:
1110         memcpy(&prop->u.dblVal, data, sizeof(double));
1111         TRACE("Read double %f\n", prop->u.dblVal);
1112         break;
1113     case VT_LPSTR:
1114     {
1115         DWORD count;
1116 
1117         StorageUtl_ReadDWord(data, 0, &count);
1118         if (codepage == CP_UNICODE && count % 2)
1119         {
1120             WARN("Unicode string has odd number of bytes\n");
1121             hr = STG_E_INVALIDHEADER;
1122         }
1123         else
1124         {
1125             prop->u.pszVal = allocate(allocate_data, count);
1126             if (prop->u.pszVal)
1127             {
1128                 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1129                 /* This is stored in the code page specified in codepage.
1130                  * Don't convert it, the caller will just store it as-is.
1131                  */
1132                 if (codepage == CP_UNICODE)
1133                 {
1134                     /* Make sure it's NULL-terminated */
1135                     prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1136                     TRACE("Read string value %s\n",
1137                      debugstr_w(prop->u.pwszVal));
1138                 }
1139                 else
1140                 {
1141                     /* Make sure it's NULL-terminated */
1142                     prop->u.pszVal[count - 1] = '\0';
1143                     TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1144                 }
1145             }
1146             else
1147                 hr = STG_E_INSUFFICIENTMEMORY;
1148         }
1149         break;
1150     }
1151     case VT_BSTR:
1152     {
1153         DWORD count, wcount;
1154 
1155         StorageUtl_ReadDWord(data, 0, &count);
1156         if (codepage == CP_UNICODE && count % 2)
1157         {
1158             WARN("Unicode string has odd number of bytes\n");
1159             hr = STG_E_INVALIDHEADER;
1160         }
1161         else
1162         {
1163             if (codepage == CP_UNICODE)
1164                 wcount = count / 2;
1165             else
1166                 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0);
1167 
1168             prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1169 
1170             if (prop->u.bstrVal)
1171             {
1172                 if (codepage == CP_UNICODE)
1173                     memcpy(prop->u.bstrVal, data + sizeof(DWORD), count);
1174                 else
1175                     MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, prop->u.bstrVal, wcount);
1176 
1177                 prop->u.bstrVal[wcount - 1] = '\0';
1178                 TRACE("Read string value %s\n", debugstr_w(prop->u.bstrVal));
1179             }
1180             else
1181                 hr = STG_E_INSUFFICIENTMEMORY;
1182         }
1183         break;
1184     }
1185     case VT_BLOB:
1186     {
1187         DWORD count;
1188 
1189         StorageUtl_ReadDWord(data, 0, &count);
1190         prop->u.blob.cbSize = count;
1191         prop->u.blob.pBlobData = allocate(allocate_data, count);
1192         if (prop->u.blob.pBlobData)
1193         {
1194             memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count);
1195             TRACE("Read blob value of size %d\n", count);
1196         }
1197         else
1198             hr = STG_E_INSUFFICIENTMEMORY;
1199         break;
1200     }
1201     case VT_LPWSTR:
1202     {
1203         DWORD count;
1204 
1205         StorageUtl_ReadDWord(data, 0, &count);
1206         prop->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1207         if (prop->u.pwszVal)
1208         {
1209             memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1210              count * sizeof(WCHAR));
1211             /* make sure string is NULL-terminated */
1212             prop->u.pwszVal[count - 1] = '\0';
1213             PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1214             TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1215         }
1216         else
1217             hr = STG_E_INSUFFICIENTMEMORY;
1218         break;
1219     }
1220     case VT_FILETIME:
1221         StorageUtl_ReadULargeInteger(data, 0,
1222          (ULARGE_INTEGER *)&prop->u.filetime);
1223         break;
1224     case VT_CF:
1225         {
1226             DWORD len = 0, tag = 0;
1227 
1228             StorageUtl_ReadDWord(data, 0, &len);
1229             StorageUtl_ReadDWord(data, 4, &tag);
1230             if (len > 8)
1231             {
1232                 len -= 8;
1233                 prop->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1234                 prop->u.pclipdata->cbSize = len;
1235                 prop->u.pclipdata->ulClipFmt = tag;
1236                 prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt));
1237                 memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt));
1238             }
1239             else
1240                 hr = STG_E_INVALIDPARAMETER;
1241         }
1242         break;
1243     default:
1244         FIXME("unsupported type %d\n", prop->vt);
1245         hr = STG_E_INVALIDPARAMETER;
1246     }
1247     return hr;
1248 }
1249 
1250 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1251  PROPERTYSETHEADER *hdr)
1252 {
1253     BYTE buf[sizeof(PROPERTYSETHEADER)];
1254     ULONG count = 0;
1255     HRESULT hr;
1256 
1257     assert(stm);
1258     assert(hdr);
1259     hr = IStream_Read(stm, buf, sizeof(buf), &count);
1260     if (SUCCEEDED(hr))
1261     {
1262         if (count != sizeof(buf))
1263         {
1264             WARN("read only %d\n", count);
1265             hr = STG_E_INVALIDHEADER;
1266         }
1267         else
1268         {
1269             StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1270              &hdr->wByteOrder);
1271             StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1272              &hdr->wFormat);
1273             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1274              &hdr->dwOSVer);
1275             StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1276              &hdr->clsid);
1277             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1278              &hdr->reserved);
1279         }
1280     }
1281     TRACE("returning 0x%08x\n", hr);
1282     return hr;
1283 }
1284 
1285 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1286  FORMATIDOFFSET *fmt)
1287 {
1288     BYTE buf[sizeof(FORMATIDOFFSET)];
1289     ULONG count = 0;
1290     HRESULT hr;
1291 
1292     assert(stm);
1293     assert(fmt);
1294     hr = IStream_Read(stm, buf, sizeof(buf), &count);
1295     if (SUCCEEDED(hr))
1296     {
1297         if (count != sizeof(buf))
1298         {
1299             WARN("read only %d\n", count);
1300             hr = STG_E_INVALIDHEADER;
1301         }
1302         else
1303         {
1304             StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1305              &fmt->fmtid);
1306             StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1307              &fmt->dwOffset);
1308         }
1309     }
1310     TRACE("returning 0x%08x\n", hr);
1311     return hr;
1312 }
1313 
1314 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1315  PROPERTYSECTIONHEADER *hdr)
1316 {
1317     BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1318     ULONG count = 0;
1319     HRESULT hr;
1320 
1321     assert(stm);
1322     assert(hdr);
1323     hr = IStream_Read(stm, buf, sizeof(buf), &count);
1324     if (SUCCEEDED(hr))
1325     {
1326         if (count != sizeof(buf))
1327         {
1328             WARN("read only %d\n", count);
1329             hr = STG_E_INVALIDHEADER;
1330         }
1331         else
1332         {
1333             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1334              cbSection), &hdr->cbSection);
1335             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1336              cProperties), &hdr->cProperties);
1337         }
1338     }
1339     TRACE("returning 0x%08x\n", hr);
1340     return hr;
1341 }
1342 
1343 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1344 {
1345     PROPERTYSETHEADER hdr;
1346     FORMATIDOFFSET fmtOffset;
1347     PROPERTYSECTIONHEADER sectionHdr;
1348     LARGE_INTEGER seek;
1349     ULONG i;
1350     STATSTG stat;
1351     HRESULT hr;
1352     BYTE *buf = NULL;
1353     ULONG count = 0;
1354     DWORD dictOffset = 0;
1355 
1356     This->dirty = FALSE;
1357     This->highestProp = 0;
1358     hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1359     if (FAILED(hr))
1360         goto end;
1361     if (stat.cbSize.u.HighPart)
1362     {
1363         WARN("stream too big\n");
1364         /* maximum size varies, but it can't be this big */
1365         hr = STG_E_INVALIDHEADER;
1366         goto end;
1367     }
1368     if (stat.cbSize.u.LowPart == 0)
1369     {
1370         /* empty stream is okay */
1371         hr = S_OK;
1372         goto end;
1373     }
1374     else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1375      sizeof(FORMATIDOFFSET))
1376     {
1377         WARN("stream too small\n");
1378         hr = STG_E_INVALIDHEADER;
1379         goto end;
1380     }
1381     seek.QuadPart = 0;
1382     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1383     if (FAILED(hr))
1384         goto end;
1385     hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1386     /* I've only seen reserved == 1, but the article says I shouldn't disallow
1387      * higher values.
1388      */
1389     if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1390     {
1391         WARN("bad magic in prop set header\n");
1392         hr = STG_E_INVALIDHEADER;
1393         goto end;
1394     }
1395     if (hdr.wFormat != 0 && hdr.wFormat != 1)
1396     {
1397         WARN("bad format version %d\n", hdr.wFormat);
1398         hr = STG_E_INVALIDHEADER;
1399         goto end;
1400     }
1401     This->format = hdr.wFormat;
1402     This->clsid = hdr.clsid;
1403     This->originatorOS = hdr.dwOSVer;
1404     if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1405         WARN("File comes from a Mac, strings will probably be screwed up\n");
1406     hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1407     if (FAILED(hr))
1408         goto end;
1409     if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1410     {
1411         WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset,
1412          stat.cbSize.u.LowPart);
1413         hr = STG_E_INVALIDHEADER;
1414         goto end;
1415     }
1416     /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1417      * follows not one, but two sections.  The first contains the standard properties
1418      * for the document summary information, and the second consists of user-defined
1419      * properties.  This is the only case in which multiple sections are
1420      * allowed.
1421      * Reading the second stream isn't implemented yet.
1422      */
1423     hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1424     if (FAILED(hr))
1425         goto end;
1426     /* The section size includes the section header, so check it */
1427     if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1428     {
1429         WARN("section header too small, got %d\n", sectionHdr.cbSection);
1430         hr = STG_E_INVALIDHEADER;
1431         goto end;
1432     }
1433     buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1434      sizeof(PROPERTYSECTIONHEADER));
1435     if (!buf)
1436     {
1437         hr = STG_E_INSUFFICIENTMEMORY;
1438         goto end;
1439     }
1440     hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1441      sizeof(PROPERTYSECTIONHEADER), &count);
1442     if (FAILED(hr))
1443         goto end;
1444     TRACE("Reading %d properties:\n", sectionHdr.cProperties);
1445     for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1446     {
1447         PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1448          i * sizeof(PROPERTYIDOFFSET));
1449 
1450         if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1451          idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1452             hr = STG_E_INVALIDPOINTER;
1453         else
1454         {
1455             if (idOffset->propid >= PID_FIRST_USABLE &&
1456              idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1457              This->highestProp)
1458                 This->highestProp = idOffset->propid;
1459             if (idOffset->propid == PID_DICTIONARY)
1460             {
1461                 /* Don't read the dictionary yet, its entries depend on the
1462                  * code page.  Just store the offset so we know to read it
1463                  * later.
1464                  */
1465                 dictOffset = idOffset->dwOffset;
1466                 TRACE("Dictionary offset is %d\n", dictOffset);
1467             }
1468             else
1469             {
1470                 PROPVARIANT prop;
1471 
1472                 PropVariantInit(&prop);
1473                 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
1474                  buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER),
1475                  This->codePage, Allocate_CoTaskMemAlloc, NULL)))
1476                 {
1477                     TRACE("Read property with ID 0x%08x, type %d\n",
1478                      idOffset->propid, prop.vt);
1479                     switch(idOffset->propid)
1480                     {
1481                     case PID_CODEPAGE:
1482                         if (prop.vt == VT_I2)
1483                             This->codePage = (UINT)prop.u.iVal;
1484                         break;
1485                     case PID_LOCALE:
1486                         if (prop.vt == VT_I4)
1487                             This->locale = (LCID)prop.u.lVal;
1488                         break;
1489                     case PID_BEHAVIOR:
1490                         if (prop.vt == VT_I4 && prop.u.lVal)
1491                             This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1492                         /* The format should already be 1, but just in case */
1493                         This->format = 1;
1494                         break;
1495                     default:
1496                         hr = PropertyStorage_StorePropWithId(This,
1497                          idOffset->propid, &prop, This->codePage);
1498                     }
1499                 }
1500                 PropVariantClear(&prop);
1501             }
1502         }
1503     }
1504     if (!This->codePage)
1505     {
1506         /* default to Unicode unless told not to, as specified on msdn */
1507         if (This->grfFlags & PROPSETFLAG_ANSI)
1508             This->codePage = GetACP();
1509         else
1510             This->codePage = CP_UNICODE;
1511     }
1512     if (!This->locale)
1513         This->locale = LOCALE_SYSTEM_DEFAULT;
1514     TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
1515     if (dictOffset)
1516         hr = PropertyStorage_ReadDictionary(This,
1517          buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1518 
1519 end:
1520     HeapFree(GetProcessHeap(), 0, buf);
1521     if (FAILED(hr))
1522     {
1523         dictionary_destroy(This->name_to_propid);
1524         This->name_to_propid = NULL;
1525         dictionary_destroy(This->propid_to_name);
1526         This->propid_to_name = NULL;
1527         dictionary_destroy(This->propid_to_prop);
1528         This->propid_to_prop = NULL;
1529     }
1530     return hr;
1531 }
1532 
1533 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1534  PROPERTYSETHEADER *hdr)
1535 {
1536     assert(hdr);
1537     StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1538      PROPSETHDR_BYTEORDER_MAGIC);
1539     StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1540     StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1541     StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1542     StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1543 }
1544 
1545 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1546  FORMATIDOFFSET *fmtOffset)
1547 {
1548     assert(fmtOffset);
1549     StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1550     StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1551      sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1552 }
1553 
1554 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1555  PROPERTYSECTIONHEADER *hdr)
1556 {
1557     assert(hdr);
1558     StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1559     StorageUtl_WriteDWord((BYTE *)hdr,
1560      offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1561 }
1562 
1563 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1564  PROPERTYIDOFFSET *propIdOffset)
1565 {
1566     assert(propIdOffset);
1567     StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1568     StorageUtl_WriteDWord((BYTE *)propIdOffset,
1569      offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1570 }
1571 
1572 static inline HRESULT PropertStorage_WriteWStringToStream(IStream *stm,
1573  LPCWSTR str, DWORD len, DWORD *written)
1574 {
1575 #ifdef WORDS_BIGENDIAN
1576     WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1577     HRESULT hr;
1578 
1579     if (!leStr)
1580         return E_OUTOFMEMORY;
1581     memcpy(leStr, str, len * sizeof(WCHAR));
1582     PropertyStorage_ByteSwapString(leStr, len);
1583     hr = IStream_Write(stm, leStr, len, written);
1584     HeapFree(GetProcessHeap(), 0, leStr);
1585     return hr;
1586 #else
1587     return IStream_Write(stm, str, len, written);
1588 #endif
1589 }
1590 
1591 struct DictionaryClosure
1592 {
1593     HRESULT hr;
1594     DWORD bytesWritten;
1595 };
1596 
1597 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1598  const void *value, void *extra, void *closure)
1599 {
1600     PropertyStorage_impl *This = extra;
1601     struct DictionaryClosure *c = closure;
1602     DWORD propid;
1603     ULONG count;
1604 
1605     assert(key);
1606     assert(closure);
1607     StorageUtl_WriteDWord((LPBYTE)&propid, 0, PtrToUlong(value));
1608     c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1609     if (FAILED(c->hr))
1610         goto end;
1611     c->bytesWritten += sizeof(DWORD);
1612     if (This->codePage == CP_UNICODE)
1613     {
1614         DWORD keyLen, pad = 0;
1615 
1616         StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1617          (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
1618         c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1619         if (FAILED(c->hr))
1620             goto end;
1621         c->bytesWritten += sizeof(DWORD);
1622         c->hr = PropertStorage_WriteWStringToStream(This->stm, key, keyLen,
1623          &count);
1624         if (FAILED(c->hr))
1625             goto end;
1626         c->bytesWritten += keyLen * sizeof(WCHAR);
1627         if (keyLen % sizeof(DWORD))
1628         {
1629             c->hr = IStream_Write(This->stm, &pad,
1630              sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1631             if (FAILED(c->hr))
1632                 goto end;
1633             c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1634         }
1635     }
1636     else
1637     {
1638         DWORD keyLen;
1639 
1640         StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1641         c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1642         if (FAILED(c->hr))
1643             goto end;
1644         c->bytesWritten += sizeof(DWORD);
1645         c->hr = IStream_Write(This->stm, key, keyLen, &count);
1646         if (FAILED(c->hr))
1647             goto end;
1648         c->bytesWritten += keyLen;
1649     }
1650 end:
1651     return SUCCEEDED(c->hr);
1652 }
1653 
1654 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1655 
1656 /* Writes the dictionary to the stream.  Assumes without checking that the
1657  * dictionary isn't empty.
1658  */
1659 static HRESULT PropertyStorage_WriteDictionaryToStream(
1660  PropertyStorage_impl *This, DWORD *sectionOffset)
1661 {
1662     HRESULT hr;
1663     LARGE_INTEGER seek;
1664     PROPERTYIDOFFSET propIdOffset;
1665     ULONG count;
1666     DWORD dwTemp;
1667     struct DictionaryClosure closure;
1668 
1669     assert(sectionOffset);
1670 
1671     /* The dictionary's always the first property written, so seek to its
1672      * spot.
1673      */
1674     seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1675     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1676     if (FAILED(hr))
1677         goto end;
1678     PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1679      &propIdOffset);
1680     hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1681     if (FAILED(hr))
1682         goto end;
1683 
1684     seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1685     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1686     if (FAILED(hr))
1687         goto end;
1688     StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1689      dictionary_num_entries(This->name_to_propid));
1690     hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1691     if (FAILED(hr))
1692         goto end;
1693     *sectionOffset += sizeof(dwTemp);
1694 
1695     closure.hr = S_OK;
1696     closure.bytesWritten = 0;
1697     dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1698      &closure);
1699     hr = closure.hr;
1700     if (FAILED(hr))
1701         goto end;
1702     *sectionOffset += closure.bytesWritten;
1703     if (closure.bytesWritten % sizeof(DWORD))
1704     {
1705         DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1706         TRACE("adding %d bytes of padding\n", padding);
1707         *sectionOffset += padding;
1708     }
1709 
1710 end:
1711     return hr;
1712 }
1713 
1714 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1715  DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
1716 {
1717     HRESULT hr;
1718     LARGE_INTEGER seek;
1719     PROPERTYIDOFFSET propIdOffset;
1720     ULONG count;
1721     DWORD dwType, bytesWritten;
1722 
1723     assert(var);
1724     assert(sectionOffset);
1725 
1726     TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
1727      *sectionOffset);
1728 
1729     seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1730      propNum * sizeof(PROPERTYIDOFFSET);
1731     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1732     if (FAILED(hr))
1733         goto end;
1734     PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1735     hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1736     if (FAILED(hr))
1737         goto end;
1738 
1739     seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1740     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1741     if (FAILED(hr))
1742         goto end;
1743     StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1744     hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1745     if (FAILED(hr))
1746         goto end;
1747     *sectionOffset += sizeof(dwType);
1748 
1749     switch (var->vt)
1750     {
1751     case VT_EMPTY:
1752     case VT_NULL:
1753         bytesWritten = 0;
1754         break;
1755     case VT_I1:
1756     case VT_UI1:
1757         hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1758          &count);
1759         bytesWritten = count;
1760         break;
1761     case VT_I2:
1762     case VT_UI2:
1763     {
1764         WORD wTemp;
1765 
1766         StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1767         hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1768         bytesWritten = count;
1769         break;
1770     }
1771     case VT_I4:
1772     case VT_UI4:
1773     {
1774         DWORD dwTemp;
1775 
1776         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1777         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1778         bytesWritten = count;
1779         break;
1780     }
1781     case VT_LPSTR:
1782     {
1783         DWORD len, dwTemp;
1784 
1785         if (This->codePage == CP_UNICODE)
1786             len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1787         else
1788             len = lstrlenA(var->u.pszVal) + 1;
1789         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1790         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1791         if (FAILED(hr))
1792             goto end;
1793         hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1794         bytesWritten = count + sizeof(DWORD);
1795         break;
1796     }
1797     case VT_LPWSTR:
1798     {
1799         DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1800 
1801         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1802         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1803         if (FAILED(hr))
1804             goto end;
1805         hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1806          &count);
1807         bytesWritten = count + sizeof(DWORD);
1808         break;
1809     }
1810     case VT_FILETIME:
1811     {
1812         FILETIME temp;
1813 
1814         StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1815          (const ULARGE_INTEGER *)&var->u.filetime);
1816         hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1817         bytesWritten = count;
1818         break;
1819     }
1820     case VT_CF:
1821     {
1822         DWORD cf_hdr[2], len;
1823 
1824         len = var->u.pclipdata->cbSize;
1825         StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
1826         StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
1827         hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
1828         if (FAILED(hr))
1829             goto end;
1830         hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
1831                            len - sizeof(var->u.pclipdata->ulClipFmt), &count);
1832         if (FAILED(hr))
1833             goto end;
1834         bytesWritten = count + sizeof cf_hdr;
1835         break;
1836     }
1837     case VT_CLSID:
1838     {
1839         CLSID temp;
1840 
1841         StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
1842         hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
1843         bytesWritten = count;
1844         break;
1845     }
1846     default:
1847         FIXME("unsupported type: %d\n", var->vt);
1848         return STG_E_INVALIDPARAMETER;
1849     }
1850 
1851     if (SUCCEEDED(hr))
1852     {
1853         *sectionOffset += bytesWritten;
1854         if (bytesWritten % sizeof(DWORD))
1855         {
1856             DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1857             TRACE("adding %d bytes of padding\n", padding);
1858             *sectionOffset += padding;
1859         }
1860     }
1861 
1862 end:
1863     return hr;
1864 }
1865 
1866 struct PropertyClosure
1867 {
1868     HRESULT hr;
1869     DWORD   propNum;
1870     DWORD  *sectionOffset;
1871 };
1872 
1873 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1874  void *extra, void *closure)
1875 {
1876     PropertyStorage_impl *This = extra;
1877     struct PropertyClosure *c = closure;
1878 
1879     assert(key);
1880     assert(value);
1881     assert(extra);
1882     assert(closure);
1883     c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
1884                                                   PtrToUlong(key), value, c->sectionOffset);
1885     return SUCCEEDED(c->hr);
1886 }
1887 
1888 static HRESULT PropertyStorage_WritePropertiesToStream(
1889  PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1890 {
1891     struct PropertyClosure closure;
1892 
1893     assert(sectionOffset);
1894     closure.hr = S_OK;
1895     closure.propNum = startingPropNum;
1896     closure.sectionOffset = sectionOffset;
1897     dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1898      &closure);
1899     return closure.hr;
1900 }
1901 
1902 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1903 {
1904     HRESULT hr;
1905     ULONG count = 0;
1906     LARGE_INTEGER seek = { {0} };
1907     PROPERTYSETHEADER hdr;
1908     FORMATIDOFFSET fmtOffset;
1909 
1910     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1911     if (FAILED(hr))
1912         goto end;
1913     PropertyStorage_MakeHeader(This, &hdr);
1914     hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1915     if (FAILED(hr))
1916         goto end;
1917     if (count != sizeof(hdr))
1918     {
1919         hr = STG_E_WRITEFAULT;
1920         goto end;
1921     }
1922 
1923     PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1924     hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1925     if (FAILED(hr))
1926         goto end;
1927     if (count != sizeof(fmtOffset))
1928     {
1929         hr = STG_E_WRITEFAULT;
1930         goto end;
1931     }
1932     hr = S_OK;
1933 
1934 end:
1935     return hr;
1936 }
1937 
1938 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1939 {
1940     PROPERTYSECTIONHEADER sectionHdr;
1941     HRESULT hr;
1942     ULONG count;
1943     LARGE_INTEGER seek;
1944     DWORD numProps, prop, sectionOffset, dwTemp;
1945     PROPVARIANT var;
1946 
1947     PropertyStorage_WriteHeadersToStream(This);
1948 
1949     /* Count properties.  Always at least one property, the code page */
1950     numProps = 1;
1951     if (dictionary_num_entries(This->name_to_propid))
1952         numProps++;
1953     if (This->locale != LOCALE_SYSTEM_DEFAULT)
1954         numProps++;
1955     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1956         numProps++;
1957     numProps += dictionary_num_entries(This->propid_to_prop);
1958 
1959     /* Write section header with 0 bytes right now, I'll adjust it after
1960      * writing properties.
1961      */
1962     PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1963     seek.QuadPart = SECTIONHEADER_OFFSET;
1964     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1965     if (FAILED(hr))
1966         goto end;
1967     hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1968     if (FAILED(hr))
1969         goto end;
1970 
1971     prop = 0;
1972     sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1973      numProps * sizeof(PROPERTYIDOFFSET);
1974 
1975     if (dictionary_num_entries(This->name_to_propid))
1976     {
1977         prop++;
1978         hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1979         if (FAILED(hr))
1980             goto end;
1981     }
1982 
1983     PropVariantInit(&var);
1984 
1985     var.vt = VT_I2;
1986     var.u.iVal = This->codePage;
1987     hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1988      &var, &sectionOffset);
1989     if (FAILED(hr))
1990         goto end;
1991 
1992     if (This->locale != LOCALE_SYSTEM_DEFAULT)
1993     {
1994         var.vt = VT_I4;
1995         var.u.lVal = This->locale;
1996         hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1997          &var, &sectionOffset);
1998         if (FAILED(hr))
1999             goto end;
2000     }
2001 
2002     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2003     {
2004         var.vt = VT_I4;
2005         var.u.lVal = 1;
2006         hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2007          &var, &sectionOffset);
2008         if (FAILED(hr))
2009             goto end;
2010     }
2011 
2012     hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2013     if (FAILED(hr))
2014         goto end;
2015 
2016     /* Now write the byte count of the section */
2017     seek.QuadPart = SECTIONHEADER_OFFSET;
2018     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2019     if (FAILED(hr))
2020         goto end;
2021     StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
2022     hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2023 
2024 end:
2025     return hr;
2026 }
2027 
2028 /***********************************************************************
2029  * PropertyStorage_Construct
2030  */
2031 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2032 {
2033     dictionary_destroy(This->name_to_propid);
2034     This->name_to_propid = NULL;
2035     dictionary_destroy(This->propid_to_name);
2036     This->propid_to_name = NULL;
2037     dictionary_destroy(This->propid_to_prop);
2038     This->propid_to_prop = NULL;
2039 }
2040 
2041 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2042 {
2043     HRESULT hr = S_OK;
2044 
2045     This->name_to_propid = dictionary_create(
2046      PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2047      This);
2048     if (!This->name_to_propid)
2049     {
2050         hr = STG_E_INSUFFICIENTMEMORY;
2051         goto end;
2052     }
2053     This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2054      NULL, This);
2055     if (!This->propid_to_name)
2056     {
2057         hr = STG_E_INSUFFICIENTMEMORY;
2058         goto end;
2059     }
2060     This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2061      PropertyStorage_PropertyDestroy, This);
2062     if (!This->propid_to_prop)
2063     {
2064         hr = STG_E_INSUFFICIENTMEMORY;
2065         goto end;
2066     }
2067 end:
2068     if (FAILED(hr))
2069         PropertyStorage_DestroyDictionaries(This);
2070     return hr;
2071 }
2072 
2073 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2074  REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2075 {
2076     HRESULT hr = S_OK;
2077 
2078     assert(pps);
2079     assert(rfmtid);
2080     *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2081     if (!*pps)
2082         return E_OUTOFMEMORY;
2083 
2084     (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2085     (*pps)->ref = 1;
2086     InitializeCriticalSection(&(*pps)->cs);
2087     (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2088     (*pps)->stm = stm;
2089     (*pps)->fmtid = *rfmtid;
2090     (*pps)->grfMode = grfMode;
2091 
2092     hr = PropertyStorage_CreateDictionaries(*pps);
2093     if (FAILED(hr))
2094     {
2095         (*pps)->cs.DebugInfo->Spare[0] = 0;
2096         DeleteCriticalSection(&(*pps)->cs);
2097         HeapFree(GetProcessHeap(), 0, *pps);
2098         *pps = NULL;
2099     }
2100     else IStream_AddRef( stm );
2101 
2102     return hr;
2103 }
2104 
2105 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2106  REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2107 {
2108     PropertyStorage_impl *ps;
2109     HRESULT hr;
2110 
2111     assert(pps);
2112     hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2113     if (SUCCEEDED(hr))
2114     {
2115         hr = PropertyStorage_ReadFromStream(ps);
2116         if (SUCCEEDED(hr))
2117         {
2118             *pps = &ps->IPropertyStorage_iface;
2119             TRACE("PropertyStorage %p constructed\n", ps);
2120             hr = S_OK;
2121         }
2122         else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2123     }
2124     return hr;
2125 }
2126 
2127 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2128  REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2129 {
2130     PropertyStorage_impl *ps;
2131     HRESULT hr;
2132 
2133     assert(pps);
2134     hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2135     if (SUCCEEDED(hr))
2136     {
2137         ps->format = 0;
2138         ps->grfFlags = grfFlags;
2139         if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2140             ps->format = 1;
2141         /* default to Unicode unless told not to, as specified on msdn */
2142         if (ps->grfFlags & PROPSETFLAG_ANSI)
2143             ps->codePage = GetACP();
2144         else
2145             ps->codePage = CP_UNICODE;
2146         ps->locale = LOCALE_SYSTEM_DEFAULT;
2147         TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2148         *pps = &ps->IPropertyStorage_iface;
2149         TRACE("PropertyStorage %p constructed\n", ps);
2150         hr = S_OK;
2151     }
2152     return hr;
2153 }
2154 
2155 
2156 /***********************************************************************
2157  * Implementation of IPropertySetStorage
2158  */
2159 
2160 /************************************************************************
2161  * IPropertySetStorage_fnQueryInterface (IUnknown)
2162  *
2163  *  This method forwards to the common QueryInterface implementation
2164  */
2165 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2166     IPropertySetStorage *ppstg,
2167     REFIID riid,
2168     void** ppvObject)
2169 {
2170     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2171     return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2172 }
2173 
2174 /************************************************************************
2175  * IPropertySetStorage_fnAddRef (IUnknown)
2176  *
2177  *  This method forwards to the common AddRef implementation
2178  */
2179 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2180     IPropertySetStorage *ppstg)
2181 {
2182     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2183     return IStorage_AddRef( &This->base.IStorage_iface );
2184 }
2185 
2186 /************************************************************************
2187  * IPropertySetStorage_fnRelease (IUnknown)
2188  *
2189  *  This method forwards to the common Release implementation
2190  */
2191 static ULONG WINAPI IPropertySetStorage_fnRelease(
2192     IPropertySetStorage *ppstg)
2193 {
2194     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2195     return IStorage_Release( &This->base.IStorage_iface );
2196 }
2197 
2198 /************************************************************************
2199  * IPropertySetStorage_fnCreate (IPropertySetStorage)
2200  */
2201 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2202     IPropertySetStorage *ppstg,
2203     REFFMTID rfmtid,
2204     const CLSID* pclsid,
2205     DWORD grfFlags,
2206     DWORD grfMode,
2207     IPropertyStorage** ppprstg)
2208 {
2209     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2210     WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2211     IStream *stm = NULL;
2212     HRESULT r;
2213 
2214     TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2215      grfMode, ppprstg);
2216 
2217     /* be picky */
2218     if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2219     {
2220         r = STG_E_INVALIDFLAG;
2221         goto end;
2222     }
2223 
2224     if (!rfmtid)
2225     {
2226         r = E_INVALIDARG;
2227         goto end;
2228     }
2229 
2230     /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2231      * storage, not a stream.  For now, disallow it.
2232      */
2233     if (grfFlags & PROPSETFLAG_NONSIMPLE)
2234     {
2235         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2236         r = STG_E_INVALIDFLAG;
2237         goto end;
2238     }
2239 
2240     r = FmtIdToPropStgName(rfmtid, name);
2241     if (FAILED(r))
2242         goto end;
2243 
2244     r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2245     if (FAILED(r))
2246         goto end;
2247 
2248     r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2249 
2250     IStream_Release( stm );
2251 
2252 end:
2253     TRACE("returning 0x%08x\n", r);
2254     return r;
2255 }
2256 
2257 /************************************************************************
2258  * IPropertySetStorage_fnOpen (IPropertySetStorage)
2259  */
2260 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2261     IPropertySetStorage *ppstg,
2262     REFFMTID rfmtid,
2263     DWORD grfMode,
2264     IPropertyStorage** ppprstg)
2265 {
2266     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2267     IStream *stm = NULL;
2268     WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2269     HRESULT r;
2270 
2271     TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2272 
2273     /* be picky */
2274     if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2275         grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2276     {
2277         r = STG_E_INVALIDFLAG;
2278         goto end;
2279     }
2280 
2281     if (!rfmtid)
2282     {
2283         r = E_INVALIDARG;
2284         goto end;
2285     }
2286 
2287     r = FmtIdToPropStgName(rfmtid, name);
2288     if (FAILED(r))
2289         goto end;
2290 
2291     r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2292     if (FAILED(r))
2293         goto end;
2294 
2295     r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2296 
2297     IStream_Release( stm );
2298 
2299 end:
2300     TRACE("returning 0x%08x\n", r);
2301     return r;
2302 }
2303 
2304 /************************************************************************
2305  * IPropertySetStorage_fnDelete (IPropertySetStorage)
2306  */
2307 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2308     IPropertySetStorage *ppstg,
2309     REFFMTID rfmtid)
2310 {
2311     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2312     WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2313     HRESULT r;
2314 
2315     TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2316 
2317     if (!rfmtid)
2318         return E_INVALIDARG;
2319 
2320     r = FmtIdToPropStgName(rfmtid, name);
2321     if (FAILED(r))
2322         return r;
2323 
2324     return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2325 }
2326 
2327 /************************************************************************
2328  * IPropertySetStorage_fnEnum (IPropertySetStorage)
2329  */
2330 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2331     IPropertySetStorage *ppstg,
2332     IEnumSTATPROPSETSTG** ppenum)
2333 {
2334     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2335     return create_EnumSTATPROPSETSTG(This, ppenum);
2336 }
2337 
2338 /************************************************************************
2339  * Implement IEnumSTATPROPSETSTG using enumx
2340  */
2341 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2342     IEnumSTATPROPSETSTG *iface,
2343     REFIID riid,
2344     void** ppvObject)
2345 {
2346     return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2347 }
2348 
2349 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2350     IEnumSTATPROPSETSTG *iface)
2351 {
2352     return enumx_AddRef((enumx_impl*)iface);
2353 }
2354 
2355 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2356     IEnumSTATPROPSETSTG *iface)
2357 {
2358     return enumx_Release((enumx_impl*)iface);
2359 }
2360 
2361 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2362     IEnumSTATPROPSETSTG *iface,
2363     ULONG celt,
2364     STATPROPSETSTG *rgelt,
2365     ULONG *pceltFetched)
2366 {
2367     return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2368 }
2369 
2370 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2371     IEnumSTATPROPSETSTG *iface,
2372     ULONG celt)
2373 {
2374     return enumx_Skip((enumx_impl*)iface, celt);
2375 }
2376 
2377 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2378     IEnumSTATPROPSETSTG *iface)
2379 {
2380     return enumx_Reset((enumx_impl*)iface);
2381 }
2382 
2383 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2384     IEnumSTATPROPSETSTG *iface,
2385     IEnumSTATPROPSETSTG **ppenum)
2386 {
2387     return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2388 }
2389 
2390 static HRESULT create_EnumSTATPROPSETSTG(
2391     StorageImpl *This,
2392     IEnumSTATPROPSETSTG** ppenum)
2393 {
2394     IStorage *stg = &This->base.IStorage_iface;
2395     IEnumSTATSTG *penum = NULL;
2396     STATSTG stat;
2397     ULONG count;
2398     HRESULT r;
2399     STATPROPSETSTG statpss;
2400     enumx_impl *enumx;
2401 
2402     TRACE("%p %p\n", This, ppenum);
2403 
2404     enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2405                            &IEnumSTATPROPSETSTG_Vtbl,
2406                            sizeof (STATPROPSETSTG),
2407                            (IUnknown*)&This->base.IStorage_iface,
2408                            NULL);
2409 
2410     /* add all the property set elements into a list */
2411     r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2412     if (FAILED(r))
2413     {
2414         enumx_Release(enumx);
2415         return E_OUTOFMEMORY;
2416     }
2417 
2418     while (1)
2419     {
2420         count = 0;
2421         r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2422         if (FAILED(r))
2423             break;
2424         if (!count)
2425             break;
2426         if (!stat.pwcsName)
2427             continue;
2428         if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2429         {
2430             PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2431             TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2432                   debugstr_guid(&statpss.fmtid));
2433             statpss.mtime = stat.mtime;
2434             statpss.atime = stat.atime;
2435             statpss.ctime = stat.ctime;
2436             statpss.grfFlags = stat.grfMode;
2437             statpss.clsid = stat.clsid;
2438             enumx_add_element(enumx, &statpss);
2439         }
2440         CoTaskMemFree(stat.pwcsName);
2441     }
2442     IEnumSTATSTG_Release(penum);
2443 
2444     *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2445 
2446     return S_OK;
2447 }
2448 
2449 /************************************************************************
2450  * Implement IEnumSTATPROPSTG using enumx
2451  */
2452 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2453     IEnumSTATPROPSTG *iface,
2454     REFIID riid,
2455     void** ppvObject)
2456 {
2457     return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2458 }
2459 
2460 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2461     IEnumSTATPROPSTG *iface)
2462 {
2463     return enumx_AddRef((enumx_impl*)iface);
2464 }
2465 
2466 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2467     IEnumSTATPROPSTG *iface)
2468 {
2469     return enumx_Release((enumx_impl*)iface);
2470 }
2471 
2472 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2473     IEnumSTATPROPSTG *iface,
2474     ULONG celt,
2475     STATPROPSTG *rgelt,
2476     ULONG *pceltFetched)
2477 {
2478     return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2479 }
2480 
2481 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2482     IEnumSTATPROPSTG *iface,
2483     ULONG celt)
2484 {
2485     return enumx_Skip((enumx_impl*)iface, celt);
2486 }
2487 
2488 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2489     IEnumSTATPROPSTG *iface)
2490 {
2491     return enumx_Reset((enumx_impl*)iface);
2492 }
2493 
2494 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2495     IEnumSTATPROPSTG *iface,
2496     IEnumSTATPROPSTG **ppenum)
2497 {
2498     return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2499 }
2500 
2501 static void prop_enum_copy_cb(IUnknown *parent, void *orig, void *dest)
2502 {
2503     PropertyStorage_impl *storage = impl_from_IPropertyStorage((IPropertyStorage*)parent);
2504     STATPROPSTG *src_prop = orig;
2505     STATPROPSTG *dest_prop = dest;
2506     LPWSTR name;
2507 
2508     dest_prop->propid = src_prop->propid;
2509     dest_prop->vt = src_prop->vt;
2510     dest_prop->lpwstrName = NULL;
2511 
2512     if (dictionary_find(storage->propid_to_name, UlongToPtr(src_prop->propid), (void**)&name))
2513     {
2514         DWORD size = (strlenW(name) + 1) * sizeof(WCHAR);
2515 
2516         dest_prop->lpwstrName = CoTaskMemAlloc(size);
2517         if (!dest_prop->lpwstrName) return;
2518         memcpy(dest_prop->lpwstrName, name, size);
2519     }
2520 }
2521 
2522 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2523 {
2524     enumx_impl *enumx = arg;
2525     PROPID propid = PtrToUlong(k);
2526     const PROPVARIANT *prop = v;
2527     STATPROPSTG stat;
2528 
2529     stat.lpwstrName = NULL;
2530     stat.propid = propid;
2531     stat.vt = prop->vt;
2532 
2533     enumx_add_element(enumx, &stat);
2534 
2535     return TRUE;
2536 }
2537 
2538 static HRESULT create_EnumSTATPROPSTG(
2539     PropertyStorage_impl *This,
2540     IEnumSTATPROPSTG** ppenum)
2541 {
2542     enumx_impl *enumx;
2543 
2544     TRACE("%p %p\n", This, ppenum);
2545 
2546     enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2547                            &IEnumSTATPROPSTG_Vtbl,
2548                            sizeof (STATPROPSTG),
2549                            (IUnknown*)&This->IPropertyStorage_iface,
2550                            prop_enum_copy_cb);
2551 
2552     dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2553 
2554     *ppenum = (IEnumSTATPROPSTG*) enumx;
2555 
2556     return S_OK;
2557 }
2558 
2559 /***********************************************************************
2560  * vtables
2561  */
2562 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2563 {
2564     IPropertySetStorage_fnQueryInterface,
2565     IPropertySetStorage_fnAddRef,
2566     IPropertySetStorage_fnRelease,
2567     IPropertySetStorage_fnCreate,
2568     IPropertySetStorage_fnOpen,
2569     IPropertySetStorage_fnDelete,
2570     IPropertySetStorage_fnEnum
2571 };
2572 
2573 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2574 {
2575     IPropertyStorage_fnQueryInterface,
2576     IPropertyStorage_fnAddRef,
2577     IPropertyStorage_fnRelease,
2578     IPropertyStorage_fnReadMultiple,
2579     IPropertyStorage_fnWriteMultiple,
2580     IPropertyStorage_fnDeleteMultiple,
2581     IPropertyStorage_fnReadPropertyNames,
2582     IPropertyStorage_fnWritePropertyNames,
2583     IPropertyStorage_fnDeletePropertyNames,
2584     IPropertyStorage_fnCommit,
2585     IPropertyStorage_fnRevert,
2586     IPropertyStorage_fnEnum,
2587     IPropertyStorage_fnSetTimes,
2588     IPropertyStorage_fnSetClass,
2589     IPropertyStorage_fnStat,
2590 };
2591 
2592 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2593 {
2594     IEnumSTATPROPSETSTG_fnQueryInterface,
2595     IEnumSTATPROPSETSTG_fnAddRef,
2596     IEnumSTATPROPSETSTG_fnRelease,
2597     IEnumSTATPROPSETSTG_fnNext,
2598     IEnumSTATPROPSETSTG_fnSkip,
2599     IEnumSTATPROPSETSTG_fnReset,
2600     IEnumSTATPROPSETSTG_fnClone,
2601 };
2602 
2603 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2604 {
2605     IEnumSTATPROPSTG_fnQueryInterface,
2606     IEnumSTATPROPSTG_fnAddRef,
2607     IEnumSTATPROPSTG_fnRelease,
2608     IEnumSTATPROPSTG_fnNext,
2609     IEnumSTATPROPSTG_fnSkip,
2610     IEnumSTATPROPSTG_fnReset,
2611     IEnumSTATPROPSTG_fnClone,
2612 };
2613 
2614 /***********************************************************************
2615  * Format ID <-> name conversion
2616  */
2617 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2618  'I','n','f','o','r','m','a','t','i','o','n',0 };
2619 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2620  'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2621 
2622 #define BITS_PER_BYTE    8
2623 #define CHARMASK         0x1f
2624 #define BITS_IN_CHARMASK 5
2625 #define NUM_ALPHA_CHARS  26
2626 
2627 /***********************************************************************
2628  * FmtIdToPropStgName					[ole32.@]
2629  * Returns the storage name of the format ID rfmtid.
2630  * PARAMS
2631  *  rfmtid [I] Format ID for which to return a storage name
2632  *  str    [O] Storage name associated with rfmtid.
2633  *
2634  * RETURNS
2635  *  E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2636  *
2637  * NOTES
2638  * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2639  */
2640 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2641 {
2642     static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2643 
2644     TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2645 
2646     if (!rfmtid) return E_INVALIDARG;
2647     if (!str) return E_INVALIDARG;
2648 
2649     if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2650         lstrcpyW(str, szSummaryInfo);
2651     else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2652         lstrcpyW(str, szDocSummaryInfo);
2653     else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2654         lstrcpyW(str, szDocSummaryInfo);
2655     else
2656     {
2657         const BYTE *fmtptr;
2658         WCHAR *pstr = str;
2659         ULONG bitsRemaining = BITS_PER_BYTE;
2660 
2661         *pstr++ = 5;
2662         for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
2663         {
2664             ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2665 
2666             if (bitsRemaining >= BITS_IN_CHARMASK)
2667             {
2668                 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2669                 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2670                  *pstr <= 'z')
2671                     *pstr += 'A' - 'a';
2672                 pstr++;
2673                 bitsRemaining -= BITS_IN_CHARMASK;
2674                 if (bitsRemaining == 0)
2675                 {
2676                     fmtptr++;
2677                     bitsRemaining = BITS_PER_BYTE;
2678                 }
2679             }
2680             else
2681             {
2682                 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2683                     i |= *fmtptr << bitsRemaining;
2684                 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2685                 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2686             }
2687         }
2688         *pstr = 0;
2689     }
2690     TRACE("returning %s\n", debugstr_w(str));
2691     return S_OK;
2692 }
2693 
2694 /***********************************************************************
2695  * PropStgNameToFmtId					[ole32.@]
2696  * Returns the format ID corresponding to the given name.
2697  * PARAMS
2698  *  str    [I] Storage name to convert to a format ID.
2699  *  rfmtid [O] Format ID corresponding to str.
2700  *
2701  * RETURNS
2702  *  E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2703  *  a format ID, S_OK otherwise.
2704  */
2705 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2706 {
2707     HRESULT hr = STG_E_INVALIDNAME;
2708 
2709     TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2710 
2711     if (!rfmtid) return E_INVALIDARG;
2712     if (!str) return STG_E_INVALIDNAME;
2713 
2714     if (!lstrcmpiW(str, szDocSummaryInfo))
2715     {
2716         *rfmtid = FMTID_DocSummaryInformation;
2717         hr = S_OK;
2718     }
2719     else if (!lstrcmpiW(str, szSummaryInfo))
2720     {
2721         *rfmtid = FMTID_SummaryInformation;
2722         hr = S_OK;
2723     }
2724     else
2725     {
2726         ULONG bits;
2727         BYTE *fmtptr = (BYTE *)rfmtid - 1;
2728         const WCHAR *pstr = str;
2729 
2730         memset(rfmtid, 0, sizeof(*rfmtid));
2731         for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2732          bits += BITS_IN_CHARMASK)
2733         {
2734             ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2735             WCHAR wc;
2736 
2737             if (bitsUsed == 0)
2738                 fmtptr++;
2739             wc = *++pstr - 'A';
2740             if (wc > NUM_ALPHA_CHARS)
2741             {
2742                 wc += 'A' - 'a';
2743                 if (wc > NUM_ALPHA_CHARS)
2744                 {
2745                     wc += 'a' - '0' + NUM_ALPHA_CHARS;
2746                     if (wc > CHARMASK)
2747                     {
2748                         WARN("invalid character (%d)\n", *pstr);
2749                         goto end;
2750                     }
2751                 }
2752             }
2753             *fmtptr |= wc << bitsUsed;
2754             bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2755             if (bitsStored < BITS_IN_CHARMASK)
2756             {
2757                 wc >>= BITS_PER_BYTE - bitsUsed;
2758                 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2759                 {
2760                     if (wc != 0)
2761                     {
2762                         WARN("extra bits\n");
2763                         goto end;
2764                     }
2765                     break;
2766                 }
2767                 fmtptr++;
2768                 *fmtptr |= (BYTE)wc;
2769             }
2770         }
2771         hr = S_OK;
2772     }
2773 end:
2774     return hr;
2775 }
2776 
2777 #ifdef __i386__  /* thiscall functions are i386-specific */
2778 
2779 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
2780    __ASM_STDCALL_FUNC(func, args, \
2781                    "popl %eax\n\t" \
2782                    "popl %ecx\n\t" \
2783                    "pushl %eax\n\t" \
2784                    "movl (%ecx), %eax\n\t" \
2785                    "jmp *(4*(" #num "))(%eax)" )
2786 
2787 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
2788 extern void* __stdcall Allocate_PMemoryAllocator(void *this, ULONG cbSize);
2789 
2790 #else
2791 
2792 static void* __cdecl Allocate_PMemoryAllocator(void *this, ULONG cbSize)
2793 {
2794     void* (__cdecl *fn)(void*,ULONG) = **(void***)this;
2795     return fn(this, cbSize);
2796 }
2797 
2798 #endif
2799 
2800 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
2801     USHORT CodePage, PROPVARIANT* pvar, void* pma)
2802 {
2803     HRESULT hr;
2804 
2805     hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma);
2806 
2807     if (FAILED(hr))
2808     {
2809         FIXME("should raise C++ exception on failure\n");
2810         PropVariantInit(pvar);
2811     }
2812 
2813     return FALSE;
2814 }
2815 
2816 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
2817     USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
2818     BOOLEAN fReserved, ULONG *pcIndirect)
2819 {
2820     FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
2821 
2822     return NULL;
2823 }
2824 
2825 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
2826                                 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
2827 {
2828     IStorage *stg;
2829     IStream *stm;
2830     HRESULT r;
2831 
2832     TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
2833 
2834     if (!fmt || reserved)
2835     {
2836         r = E_INVALIDARG;
2837         goto end;
2838     }
2839 
2840     if (flags & PROPSETFLAG_NONSIMPLE)
2841     {
2842         r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2843         if (FAILED(r))
2844             goto end;
2845 
2846         /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
2847          * storage, not a stream.  For now, disallow it.
2848          */
2849         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2850         IStorage_Release(stg);
2851         r = STG_E_INVALIDFLAG;
2852     }
2853     else
2854     {
2855         r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2856         if (FAILED(r))
2857             goto end;
2858 
2859         r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
2860                 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2861 
2862         IStream_Release( stm );
2863     }
2864 
2865 end:
2866     TRACE("returning 0x%08x\n", r);
2867     return r;
2868 }
2869 
2870 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
2871                               DWORD reserved, IPropertyStorage **prop_stg)
2872 {
2873     IStorage *stg;
2874     IStream *stm;
2875     HRESULT r;
2876 
2877     TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
2878 
2879     if (!fmt || reserved)
2880     {
2881         r = E_INVALIDARG;
2882         goto end;
2883     }
2884 
2885     if (flags & PROPSETFLAG_NONSIMPLE)
2886     {
2887         r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2888         if (FAILED(r))
2889             goto end;
2890 
2891         /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
2892          * storage, not a stream.  For now, disallow it.
2893          */
2894         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2895         IStorage_Release(stg);
2896         r = STG_E_INVALIDFLAG;
2897     }
2898     else
2899     {
2900         r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2901         if (FAILED(r))
2902             goto end;
2903 
2904         r = PropertyStorage_ConstructFromStream(stm, fmt,
2905                 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2906 
2907         IStream_Release( stm );
2908     }
2909 
2910 end:
2911     TRACE("returning 0x%08x\n", r);
2912     return r;
2913 }
2914