xref: /reactos/dll/win32/ole32/stg_prop.c (revision 62919904)
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     DWORD vt;
1062 
1063     assert(prop);
1064     assert(data);
1065     StorageUtl_ReadDWord(data, 0, &vt);
1066     data += sizeof(DWORD);
1067     prop->vt = vt;
1068     switch (prop->vt)
1069     {
1070     case VT_EMPTY:
1071     case VT_NULL:
1072         break;
1073     case VT_I1:
1074         prop->u.cVal = *(const char *)data;
1075         TRACE("Read char 0x%x ('%c')\n", prop->u.cVal, prop->u.cVal);
1076         break;
1077     case VT_UI1:
1078         prop->u.bVal = *data;
1079         TRACE("Read byte 0x%x\n", prop->u.bVal);
1080         break;
1081     case VT_BOOL:
1082         StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.boolVal);
1083         TRACE("Read bool %d\n", prop->u.boolVal);
1084         break;
1085     case VT_I2:
1086         StorageUtl_ReadWord(data, 0, (WORD*)&prop->u.iVal);
1087         TRACE("Read short %d\n", prop->u.iVal);
1088         break;
1089     case VT_UI2:
1090         StorageUtl_ReadWord(data, 0, &prop->u.uiVal);
1091         TRACE("Read ushort %d\n", prop->u.uiVal);
1092         break;
1093     case VT_INT:
1094     case VT_I4:
1095         StorageUtl_ReadDWord(data, 0, (DWORD*)&prop->u.lVal);
1096         TRACE("Read long %d\n", prop->u.lVal);
1097         break;
1098     case VT_UINT:
1099     case VT_UI4:
1100         StorageUtl_ReadDWord(data, 0, &prop->u.ulVal);
1101         TRACE("Read ulong %d\n", prop->u.ulVal);
1102         break;
1103     case VT_I8:
1104         StorageUtl_ReadULargeInteger(data, 0, (ULARGE_INTEGER *)&prop->u.hVal);
1105         TRACE("Read long long %s\n", wine_dbgstr_longlong(prop->u.hVal.QuadPart));
1106         break;
1107     case VT_UI8:
1108         StorageUtl_ReadULargeInteger(data, 0, &prop->u.uhVal);
1109         TRACE("Read ulong long %s\n", wine_dbgstr_longlong(prop->u.uhVal.QuadPart));
1110         break;
1111     case VT_R8:
1112         memcpy(&prop->u.dblVal, data, sizeof(double));
1113         TRACE("Read double %f\n", prop->u.dblVal);
1114         break;
1115     case VT_LPSTR:
1116     {
1117         DWORD count;
1118 
1119         StorageUtl_ReadDWord(data, 0, &count);
1120         if (codepage == CP_UNICODE && count % 2)
1121         {
1122             WARN("Unicode string has odd number of bytes\n");
1123             hr = STG_E_INVALIDHEADER;
1124         }
1125         else
1126         {
1127             prop->u.pszVal = allocate(allocate_data, count);
1128             if (prop->u.pszVal)
1129             {
1130                 memcpy(prop->u.pszVal, data + sizeof(DWORD), count);
1131                 /* This is stored in the code page specified in codepage.
1132                  * Don't convert it, the caller will just store it as-is.
1133                  */
1134                 if (codepage == CP_UNICODE)
1135                 {
1136                     /* Make sure it's NULL-terminated */
1137                     prop->u.pszVal[count / sizeof(WCHAR) - 1] = '\0';
1138                     TRACE("Read string value %s\n",
1139                      debugstr_w(prop->u.pwszVal));
1140                 }
1141                 else
1142                 {
1143                     /* Make sure it's NULL-terminated */
1144                     prop->u.pszVal[count - 1] = '\0';
1145                     TRACE("Read string value %s\n", debugstr_a(prop->u.pszVal));
1146                 }
1147             }
1148             else
1149                 hr = STG_E_INSUFFICIENTMEMORY;
1150         }
1151         break;
1152     }
1153     case VT_BSTR:
1154     {
1155         DWORD count, wcount;
1156 
1157         StorageUtl_ReadDWord(data, 0, &count);
1158         if (codepage == CP_UNICODE && count % 2)
1159         {
1160             WARN("Unicode string has odd number of bytes\n");
1161             hr = STG_E_INVALIDHEADER;
1162         }
1163         else
1164         {
1165             if (codepage == CP_UNICODE)
1166                 wcount = count / 2;
1167             else
1168                 wcount = MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, NULL, 0);
1169 
1170             prop->u.bstrVal = SysAllocStringLen(NULL, wcount); /* FIXME: use allocator? */
1171 
1172             if (prop->u.bstrVal)
1173             {
1174                 if (codepage == CP_UNICODE)
1175                     memcpy(prop->u.bstrVal, data + sizeof(DWORD), count);
1176                 else
1177                     MultiByteToWideChar(codepage, 0, (LPCSTR)(data + sizeof(DWORD)), count, prop->u.bstrVal, wcount);
1178 
1179                 prop->u.bstrVal[wcount - 1] = '\0';
1180                 TRACE("Read string value %s\n", debugstr_w(prop->u.bstrVal));
1181             }
1182             else
1183                 hr = STG_E_INSUFFICIENTMEMORY;
1184         }
1185         break;
1186     }
1187     case VT_BLOB:
1188     {
1189         DWORD count;
1190 
1191         StorageUtl_ReadDWord(data, 0, &count);
1192         prop->u.blob.cbSize = count;
1193         prop->u.blob.pBlobData = allocate(allocate_data, count);
1194         if (prop->u.blob.pBlobData)
1195         {
1196             memcpy(prop->u.blob.pBlobData, data + sizeof(DWORD), count);
1197             TRACE("Read blob value of size %d\n", count);
1198         }
1199         else
1200             hr = STG_E_INSUFFICIENTMEMORY;
1201         break;
1202     }
1203     case VT_LPWSTR:
1204     {
1205         DWORD count;
1206 
1207         StorageUtl_ReadDWord(data, 0, &count);
1208         prop->u.pwszVal = allocate(allocate_data, count * sizeof(WCHAR));
1209         if (prop->u.pwszVal)
1210         {
1211             memcpy(prop->u.pwszVal, data + sizeof(DWORD),
1212              count * sizeof(WCHAR));
1213             /* make sure string is NULL-terminated */
1214             prop->u.pwszVal[count - 1] = '\0';
1215             PropertyStorage_ByteSwapString(prop->u.pwszVal, count);
1216             TRACE("Read string value %s\n", debugstr_w(prop->u.pwszVal));
1217         }
1218         else
1219             hr = STG_E_INSUFFICIENTMEMORY;
1220         break;
1221     }
1222     case VT_FILETIME:
1223         StorageUtl_ReadULargeInteger(data, 0,
1224          (ULARGE_INTEGER *)&prop->u.filetime);
1225         break;
1226     case VT_CF:
1227         {
1228             DWORD len = 0, tag = 0;
1229 
1230             StorageUtl_ReadDWord(data, 0, &len);
1231             StorageUtl_ReadDWord(data, 4, &tag);
1232             if (len > 8)
1233             {
1234                 len -= 8;
1235                 prop->u.pclipdata = allocate(allocate_data, sizeof (CLIPDATA));
1236                 prop->u.pclipdata->cbSize = len;
1237                 prop->u.pclipdata->ulClipFmt = tag;
1238                 prop->u.pclipdata->pClipData = allocate(allocate_data, len - sizeof(prop->u.pclipdata->ulClipFmt));
1239                 memcpy(prop->u.pclipdata->pClipData, data+8, len - sizeof(prop->u.pclipdata->ulClipFmt));
1240             }
1241             else
1242                 hr = STG_E_INVALIDPARAMETER;
1243         }
1244         break;
1245     default:
1246         FIXME("unsupported type %d\n", prop->vt);
1247         hr = STG_E_INVALIDPARAMETER;
1248     }
1249     return hr;
1250 }
1251 
1252 static HRESULT PropertyStorage_ReadHeaderFromStream(IStream *stm,
1253  PROPERTYSETHEADER *hdr)
1254 {
1255     BYTE buf[sizeof(PROPERTYSETHEADER)];
1256     ULONG count = 0;
1257     HRESULT hr;
1258 
1259     assert(stm);
1260     assert(hdr);
1261     hr = IStream_Read(stm, buf, sizeof(buf), &count);
1262     if (SUCCEEDED(hr))
1263     {
1264         if (count != sizeof(buf))
1265         {
1266             WARN("read only %d\n", count);
1267             hr = STG_E_INVALIDHEADER;
1268         }
1269         else
1270         {
1271             StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wByteOrder),
1272              &hdr->wByteOrder);
1273             StorageUtl_ReadWord(buf, offsetof(PROPERTYSETHEADER, wFormat),
1274              &hdr->wFormat);
1275             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, dwOSVer),
1276              &hdr->dwOSVer);
1277             StorageUtl_ReadGUID(buf, offsetof(PROPERTYSETHEADER, clsid),
1278              &hdr->clsid);
1279             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSETHEADER, reserved),
1280              &hdr->reserved);
1281         }
1282     }
1283     TRACE("returning 0x%08x\n", hr);
1284     return hr;
1285 }
1286 
1287 static HRESULT PropertyStorage_ReadFmtIdOffsetFromStream(IStream *stm,
1288  FORMATIDOFFSET *fmt)
1289 {
1290     BYTE buf[sizeof(FORMATIDOFFSET)];
1291     ULONG count = 0;
1292     HRESULT hr;
1293 
1294     assert(stm);
1295     assert(fmt);
1296     hr = IStream_Read(stm, buf, sizeof(buf), &count);
1297     if (SUCCEEDED(hr))
1298     {
1299         if (count != sizeof(buf))
1300         {
1301             WARN("read only %d\n", count);
1302             hr = STG_E_INVALIDHEADER;
1303         }
1304         else
1305         {
1306             StorageUtl_ReadGUID(buf, offsetof(FORMATIDOFFSET, fmtid),
1307              &fmt->fmtid);
1308             StorageUtl_ReadDWord(buf, offsetof(FORMATIDOFFSET, dwOffset),
1309              &fmt->dwOffset);
1310         }
1311     }
1312     TRACE("returning 0x%08x\n", hr);
1313     return hr;
1314 }
1315 
1316 static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
1317  PROPERTYSECTIONHEADER *hdr)
1318 {
1319     BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
1320     ULONG count = 0;
1321     HRESULT hr;
1322 
1323     assert(stm);
1324     assert(hdr);
1325     hr = IStream_Read(stm, buf, sizeof(buf), &count);
1326     if (SUCCEEDED(hr))
1327     {
1328         if (count != sizeof(buf))
1329         {
1330             WARN("read only %d\n", count);
1331             hr = STG_E_INVALIDHEADER;
1332         }
1333         else
1334         {
1335             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1336              cbSection), &hdr->cbSection);
1337             StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
1338              cProperties), &hdr->cProperties);
1339         }
1340     }
1341     TRACE("returning 0x%08x\n", hr);
1342     return hr;
1343 }
1344 
1345 static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
1346 {
1347     PROPERTYSETHEADER hdr;
1348     FORMATIDOFFSET fmtOffset;
1349     PROPERTYSECTIONHEADER sectionHdr;
1350     LARGE_INTEGER seek;
1351     ULONG i;
1352     STATSTG stat;
1353     HRESULT hr;
1354     BYTE *buf = NULL;
1355     ULONG count = 0;
1356     DWORD dictOffset = 0;
1357 
1358     This->dirty = FALSE;
1359     This->highestProp = 0;
1360     hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
1361     if (FAILED(hr))
1362         goto end;
1363     if (stat.cbSize.u.HighPart)
1364     {
1365         WARN("stream too big\n");
1366         /* maximum size varies, but it can't be this big */
1367         hr = STG_E_INVALIDHEADER;
1368         goto end;
1369     }
1370     if (stat.cbSize.u.LowPart == 0)
1371     {
1372         /* empty stream is okay */
1373         hr = S_OK;
1374         goto end;
1375     }
1376     else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
1377      sizeof(FORMATIDOFFSET))
1378     {
1379         WARN("stream too small\n");
1380         hr = STG_E_INVALIDHEADER;
1381         goto end;
1382     }
1383     seek.QuadPart = 0;
1384     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1385     if (FAILED(hr))
1386         goto end;
1387     hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
1388     /* I've only seen reserved == 1, but the article says I shouldn't disallow
1389      * higher values.
1390      */
1391     if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
1392     {
1393         WARN("bad magic in prop set header\n");
1394         hr = STG_E_INVALIDHEADER;
1395         goto end;
1396     }
1397     if (hdr.wFormat != 0 && hdr.wFormat != 1)
1398     {
1399         WARN("bad format version %d\n", hdr.wFormat);
1400         hr = STG_E_INVALIDHEADER;
1401         goto end;
1402     }
1403     This->format = hdr.wFormat;
1404     This->clsid = hdr.clsid;
1405     This->originatorOS = hdr.dwOSVer;
1406     if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
1407         WARN("File comes from a Mac, strings will probably be screwed up\n");
1408     hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
1409     if (FAILED(hr))
1410         goto end;
1411     if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
1412     {
1413         WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset,
1414          stat.cbSize.u.LowPart);
1415         hr = STG_E_INVALIDHEADER;
1416         goto end;
1417     }
1418     /* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
1419      * follows not one, but two sections.  The first contains the standard properties
1420      * for the document summary information, and the second consists of user-defined
1421      * properties.  This is the only case in which multiple sections are
1422      * allowed.
1423      * Reading the second stream isn't implemented yet.
1424      */
1425     hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, &sectionHdr);
1426     if (FAILED(hr))
1427         goto end;
1428     /* The section size includes the section header, so check it */
1429     if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
1430     {
1431         WARN("section header too small, got %d\n", sectionHdr.cbSection);
1432         hr = STG_E_INVALIDHEADER;
1433         goto end;
1434     }
1435     buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
1436      sizeof(PROPERTYSECTIONHEADER));
1437     if (!buf)
1438     {
1439         hr = STG_E_INSUFFICIENTMEMORY;
1440         goto end;
1441     }
1442     hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
1443      sizeof(PROPERTYSECTIONHEADER), &count);
1444     if (FAILED(hr))
1445         goto end;
1446     TRACE("Reading %d properties:\n", sectionHdr.cProperties);
1447     for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
1448     {
1449         PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
1450          i * sizeof(PROPERTYIDOFFSET));
1451 
1452         if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
1453          idOffset->dwOffset > sectionHdr.cbSection - sizeof(DWORD))
1454             hr = STG_E_INVALIDPOINTER;
1455         else
1456         {
1457             if (idOffset->propid >= PID_FIRST_USABLE &&
1458              idOffset->propid < PID_MIN_READONLY && idOffset->propid >
1459              This->highestProp)
1460                 This->highestProp = idOffset->propid;
1461             if (idOffset->propid == PID_DICTIONARY)
1462             {
1463                 /* Don't read the dictionary yet, its entries depend on the
1464                  * code page.  Just store the offset so we know to read it
1465                  * later.
1466                  */
1467                 dictOffset = idOffset->dwOffset;
1468                 TRACE("Dictionary offset is %d\n", dictOffset);
1469             }
1470             else
1471             {
1472                 PROPVARIANT prop;
1473 
1474                 PropVariantInit(&prop);
1475                 if (SUCCEEDED(PropertyStorage_ReadProperty(&prop,
1476                  buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER),
1477                  This->codePage, Allocate_CoTaskMemAlloc, NULL)))
1478                 {
1479                     TRACE("Read property with ID 0x%08x, type %d\n",
1480                      idOffset->propid, prop.vt);
1481                     switch(idOffset->propid)
1482                     {
1483                     case PID_CODEPAGE:
1484                         if (prop.vt == VT_I2)
1485                             This->codePage = (UINT)prop.u.iVal;
1486                         break;
1487                     case PID_LOCALE:
1488                         if (prop.vt == VT_I4)
1489                             This->locale = (LCID)prop.u.lVal;
1490                         break;
1491                     case PID_BEHAVIOR:
1492                         if (prop.vt == VT_I4 && prop.u.lVal)
1493                             This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
1494                         /* The format should already be 1, but just in case */
1495                         This->format = 1;
1496                         break;
1497                     default:
1498                         hr = PropertyStorage_StorePropWithId(This,
1499                          idOffset->propid, &prop, This->codePage);
1500                     }
1501                 }
1502                 PropVariantClear(&prop);
1503             }
1504         }
1505     }
1506     if (!This->codePage)
1507     {
1508         /* default to Unicode unless told not to, as specified on msdn */
1509         if (This->grfFlags & PROPSETFLAG_ANSI)
1510             This->codePage = GetACP();
1511         else
1512             This->codePage = CP_UNICODE;
1513     }
1514     if (!This->locale)
1515         This->locale = LOCALE_SYSTEM_DEFAULT;
1516     TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
1517     if (dictOffset)
1518         hr = PropertyStorage_ReadDictionary(This,
1519          buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
1520 
1521 end:
1522     HeapFree(GetProcessHeap(), 0, buf);
1523     if (FAILED(hr))
1524     {
1525         dictionary_destroy(This->name_to_propid);
1526         This->name_to_propid = NULL;
1527         dictionary_destroy(This->propid_to_name);
1528         This->propid_to_name = NULL;
1529         dictionary_destroy(This->propid_to_prop);
1530         This->propid_to_prop = NULL;
1531     }
1532     return hr;
1533 }
1534 
1535 static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
1536  PROPERTYSETHEADER *hdr)
1537 {
1538     assert(hdr);
1539     StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
1540      PROPSETHDR_BYTEORDER_MAGIC);
1541     StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
1542     StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
1543     StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
1544     StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
1545 }
1546 
1547 static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
1548  FORMATIDOFFSET *fmtOffset)
1549 {
1550     assert(fmtOffset);
1551     StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
1552     StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
1553      sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
1554 }
1555 
1556 static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
1557  PROPERTYSECTIONHEADER *hdr)
1558 {
1559     assert(hdr);
1560     StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
1561     StorageUtl_WriteDWord((BYTE *)hdr,
1562      offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
1563 }
1564 
1565 static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
1566  PROPERTYIDOFFSET *propIdOffset)
1567 {
1568     assert(propIdOffset);
1569     StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
1570     StorageUtl_WriteDWord((BYTE *)propIdOffset,
1571      offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
1572 }
1573 
1574 static inline HRESULT PropertStorage_WriteWStringToStream(IStream *stm,
1575  LPCWSTR str, DWORD len, DWORD *written)
1576 {
1577 #ifdef WORDS_BIGENDIAN
1578     WCHAR *leStr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1579     HRESULT hr;
1580 
1581     if (!leStr)
1582         return E_OUTOFMEMORY;
1583     memcpy(leStr, str, len * sizeof(WCHAR));
1584     PropertyStorage_ByteSwapString(leStr, len);
1585     hr = IStream_Write(stm, leStr, len, written);
1586     HeapFree(GetProcessHeap(), 0, leStr);
1587     return hr;
1588 #else
1589     return IStream_Write(stm, str, len, written);
1590 #endif
1591 }
1592 
1593 struct DictionaryClosure
1594 {
1595     HRESULT hr;
1596     DWORD bytesWritten;
1597 };
1598 
1599 static BOOL PropertyStorage_DictionaryWriter(const void *key,
1600  const void *value, void *extra, void *closure)
1601 {
1602     PropertyStorage_impl *This = extra;
1603     struct DictionaryClosure *c = closure;
1604     DWORD propid;
1605     ULONG count;
1606 
1607     assert(key);
1608     assert(closure);
1609     StorageUtl_WriteDWord((LPBYTE)&propid, 0, PtrToUlong(value));
1610     c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
1611     if (FAILED(c->hr))
1612         goto end;
1613     c->bytesWritten += sizeof(DWORD);
1614     if (This->codePage == CP_UNICODE)
1615     {
1616         DWORD keyLen, pad = 0;
1617 
1618         StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
1619          (lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
1620         c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1621         if (FAILED(c->hr))
1622             goto end;
1623         c->bytesWritten += sizeof(DWORD);
1624         c->hr = PropertStorage_WriteWStringToStream(This->stm, key, keyLen,
1625          &count);
1626         if (FAILED(c->hr))
1627             goto end;
1628         c->bytesWritten += keyLen * sizeof(WCHAR);
1629         if (keyLen % sizeof(DWORD))
1630         {
1631             c->hr = IStream_Write(This->stm, &pad,
1632              sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
1633             if (FAILED(c->hr))
1634                 goto end;
1635             c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
1636         }
1637     }
1638     else
1639     {
1640         DWORD keyLen;
1641 
1642         StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
1643         c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
1644         if (FAILED(c->hr))
1645             goto end;
1646         c->bytesWritten += sizeof(DWORD);
1647         c->hr = IStream_Write(This->stm, key, keyLen, &count);
1648         if (FAILED(c->hr))
1649             goto end;
1650         c->bytesWritten += keyLen;
1651     }
1652 end:
1653     return SUCCEEDED(c->hr);
1654 }
1655 
1656 #define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
1657 
1658 /* Writes the dictionary to the stream.  Assumes without checking that the
1659  * dictionary isn't empty.
1660  */
1661 static HRESULT PropertyStorage_WriteDictionaryToStream(
1662  PropertyStorage_impl *This, DWORD *sectionOffset)
1663 {
1664     HRESULT hr;
1665     LARGE_INTEGER seek;
1666     PROPERTYIDOFFSET propIdOffset;
1667     ULONG count;
1668     DWORD dwTemp;
1669     struct DictionaryClosure closure;
1670 
1671     assert(sectionOffset);
1672 
1673     /* The dictionary's always the first property written, so seek to its
1674      * spot.
1675      */
1676     seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
1677     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1678     if (FAILED(hr))
1679         goto end;
1680     PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
1681      &propIdOffset);
1682     hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1683     if (FAILED(hr))
1684         goto end;
1685 
1686     seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1687     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1688     if (FAILED(hr))
1689         goto end;
1690     StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
1691      dictionary_num_entries(This->name_to_propid));
1692     hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1693     if (FAILED(hr))
1694         goto end;
1695     *sectionOffset += sizeof(dwTemp);
1696 
1697     closure.hr = S_OK;
1698     closure.bytesWritten = 0;
1699     dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
1700      &closure);
1701     hr = closure.hr;
1702     if (FAILED(hr))
1703         goto end;
1704     *sectionOffset += closure.bytesWritten;
1705     if (closure.bytesWritten % sizeof(DWORD))
1706     {
1707         DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
1708         TRACE("adding %d bytes of padding\n", padding);
1709         *sectionOffset += padding;
1710     }
1711 
1712 end:
1713     return hr;
1714 }
1715 
1716 static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
1717  DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
1718 {
1719     HRESULT hr;
1720     LARGE_INTEGER seek;
1721     PROPERTYIDOFFSET propIdOffset;
1722     ULONG count;
1723     DWORD dwType, bytesWritten;
1724 
1725     assert(var);
1726     assert(sectionOffset);
1727 
1728     TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
1729      *sectionOffset);
1730 
1731     seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
1732      propNum * sizeof(PROPERTYIDOFFSET);
1733     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1734     if (FAILED(hr))
1735         goto end;
1736     PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
1737     hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
1738     if (FAILED(hr))
1739         goto end;
1740 
1741     seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
1742     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1743     if (FAILED(hr))
1744         goto end;
1745     StorageUtl_WriteDWord((LPBYTE)&dwType, 0, var->vt);
1746     hr = IStream_Write(This->stm, &dwType, sizeof(dwType), &count);
1747     if (FAILED(hr))
1748         goto end;
1749     *sectionOffset += sizeof(dwType);
1750 
1751     switch (var->vt)
1752     {
1753     case VT_EMPTY:
1754     case VT_NULL:
1755         bytesWritten = 0;
1756         break;
1757     case VT_I1:
1758     case VT_UI1:
1759         hr = IStream_Write(This->stm, &var->u.cVal, sizeof(var->u.cVal),
1760          &count);
1761         bytesWritten = count;
1762         break;
1763     case VT_I2:
1764     case VT_UI2:
1765     {
1766         WORD wTemp;
1767 
1768         StorageUtl_WriteWord((LPBYTE)&wTemp, 0, var->u.iVal);
1769         hr = IStream_Write(This->stm, &wTemp, sizeof(wTemp), &count);
1770         bytesWritten = count;
1771         break;
1772     }
1773     case VT_I4:
1774     case VT_UI4:
1775     {
1776         DWORD dwTemp;
1777 
1778         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, var->u.lVal);
1779         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1780         bytesWritten = count;
1781         break;
1782     }
1783     case VT_LPSTR:
1784     {
1785         DWORD len, dwTemp;
1786 
1787         if (This->codePage == CP_UNICODE)
1788             len = (lstrlenW(var->u.pwszVal) + 1) * sizeof(WCHAR);
1789         else
1790             len = lstrlenA(var->u.pszVal) + 1;
1791         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1792         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1793         if (FAILED(hr))
1794             goto end;
1795         hr = IStream_Write(This->stm, var->u.pszVal, len, &count);
1796         bytesWritten = count + sizeof(DWORD);
1797         break;
1798     }
1799     case VT_LPWSTR:
1800     {
1801         DWORD len = lstrlenW(var->u.pwszVal) + 1, dwTemp;
1802 
1803         StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, len);
1804         hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1805         if (FAILED(hr))
1806             goto end;
1807         hr = IStream_Write(This->stm, var->u.pwszVal, len * sizeof(WCHAR),
1808          &count);
1809         bytesWritten = count + sizeof(DWORD);
1810         break;
1811     }
1812     case VT_FILETIME:
1813     {
1814         FILETIME temp;
1815 
1816         StorageUtl_WriteULargeInteger((BYTE *)&temp, 0,
1817          (const ULARGE_INTEGER *)&var->u.filetime);
1818         hr = IStream_Write(This->stm, &temp, sizeof(FILETIME), &count);
1819         bytesWritten = count;
1820         break;
1821     }
1822     case VT_CF:
1823     {
1824         DWORD cf_hdr[2], len;
1825 
1826         len = var->u.pclipdata->cbSize;
1827         StorageUtl_WriteDWord((LPBYTE)&cf_hdr[0], 0, len + 8);
1828         StorageUtl_WriteDWord((LPBYTE)&cf_hdr[1], 0, var->u.pclipdata->ulClipFmt);
1829         hr = IStream_Write(This->stm, cf_hdr, sizeof(cf_hdr), &count);
1830         if (FAILED(hr))
1831             goto end;
1832         hr = IStream_Write(This->stm, var->u.pclipdata->pClipData,
1833                            len - sizeof(var->u.pclipdata->ulClipFmt), &count);
1834         if (FAILED(hr))
1835             goto end;
1836         bytesWritten = count + sizeof cf_hdr;
1837         break;
1838     }
1839     case VT_CLSID:
1840     {
1841         CLSID temp;
1842 
1843         StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
1844         hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
1845         bytesWritten = count;
1846         break;
1847     }
1848     default:
1849         FIXME("unsupported type: %d\n", var->vt);
1850         return STG_E_INVALIDPARAMETER;
1851     }
1852 
1853     if (SUCCEEDED(hr))
1854     {
1855         *sectionOffset += bytesWritten;
1856         if (bytesWritten % sizeof(DWORD))
1857         {
1858             DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1859             TRACE("adding %d bytes of padding\n", padding);
1860             *sectionOffset += padding;
1861         }
1862     }
1863 
1864 end:
1865     return hr;
1866 }
1867 
1868 struct PropertyClosure
1869 {
1870     HRESULT hr;
1871     DWORD   propNum;
1872     DWORD  *sectionOffset;
1873 };
1874 
1875 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1876  void *extra, void *closure)
1877 {
1878     PropertyStorage_impl *This = extra;
1879     struct PropertyClosure *c = closure;
1880 
1881     assert(key);
1882     assert(value);
1883     assert(extra);
1884     assert(closure);
1885     c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
1886                                                   PtrToUlong(key), value, c->sectionOffset);
1887     return SUCCEEDED(c->hr);
1888 }
1889 
1890 static HRESULT PropertyStorage_WritePropertiesToStream(
1891  PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1892 {
1893     struct PropertyClosure closure;
1894 
1895     assert(sectionOffset);
1896     closure.hr = S_OK;
1897     closure.propNum = startingPropNum;
1898     closure.sectionOffset = sectionOffset;
1899     dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1900      &closure);
1901     return closure.hr;
1902 }
1903 
1904 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1905 {
1906     HRESULT hr;
1907     ULONG count = 0;
1908     LARGE_INTEGER seek = { {0} };
1909     PROPERTYSETHEADER hdr;
1910     FORMATIDOFFSET fmtOffset;
1911 
1912     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1913     if (FAILED(hr))
1914         goto end;
1915     PropertyStorage_MakeHeader(This, &hdr);
1916     hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1917     if (FAILED(hr))
1918         goto end;
1919     if (count != sizeof(hdr))
1920     {
1921         hr = STG_E_WRITEFAULT;
1922         goto end;
1923     }
1924 
1925     PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1926     hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1927     if (FAILED(hr))
1928         goto end;
1929     if (count != sizeof(fmtOffset))
1930     {
1931         hr = STG_E_WRITEFAULT;
1932         goto end;
1933     }
1934     hr = S_OK;
1935 
1936 end:
1937     return hr;
1938 }
1939 
1940 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1941 {
1942     PROPERTYSECTIONHEADER sectionHdr;
1943     HRESULT hr;
1944     ULONG count;
1945     LARGE_INTEGER seek;
1946     DWORD numProps, prop, sectionOffset, dwTemp;
1947     PROPVARIANT var;
1948 
1949     PropertyStorage_WriteHeadersToStream(This);
1950 
1951     /* Count properties.  Always at least one property, the code page */
1952     numProps = 1;
1953     if (dictionary_num_entries(This->name_to_propid))
1954         numProps++;
1955     if (This->locale != LOCALE_SYSTEM_DEFAULT)
1956         numProps++;
1957     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1958         numProps++;
1959     numProps += dictionary_num_entries(This->propid_to_prop);
1960 
1961     /* Write section header with 0 bytes right now, I'll adjust it after
1962      * writing properties.
1963      */
1964     PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1965     seek.QuadPart = SECTIONHEADER_OFFSET;
1966     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1967     if (FAILED(hr))
1968         goto end;
1969     hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1970     if (FAILED(hr))
1971         goto end;
1972 
1973     prop = 0;
1974     sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1975      numProps * sizeof(PROPERTYIDOFFSET);
1976 
1977     if (dictionary_num_entries(This->name_to_propid))
1978     {
1979         prop++;
1980         hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1981         if (FAILED(hr))
1982             goto end;
1983     }
1984 
1985     PropVariantInit(&var);
1986 
1987     var.vt = VT_I2;
1988     var.u.iVal = This->codePage;
1989     hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1990      &var, &sectionOffset);
1991     if (FAILED(hr))
1992         goto end;
1993 
1994     if (This->locale != LOCALE_SYSTEM_DEFAULT)
1995     {
1996         var.vt = VT_I4;
1997         var.u.lVal = This->locale;
1998         hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1999          &var, &sectionOffset);
2000         if (FAILED(hr))
2001             goto end;
2002     }
2003 
2004     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2005     {
2006         var.vt = VT_I4;
2007         var.u.lVal = 1;
2008         hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
2009          &var, &sectionOffset);
2010         if (FAILED(hr))
2011             goto end;
2012     }
2013 
2014     hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
2015     if (FAILED(hr))
2016         goto end;
2017 
2018     /* Now write the byte count of the section */
2019     seek.QuadPart = SECTIONHEADER_OFFSET;
2020     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
2021     if (FAILED(hr))
2022         goto end;
2023     StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
2024     hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
2025 
2026 end:
2027     return hr;
2028 }
2029 
2030 /***********************************************************************
2031  * PropertyStorage_Construct
2032  */
2033 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
2034 {
2035     dictionary_destroy(This->name_to_propid);
2036     This->name_to_propid = NULL;
2037     dictionary_destroy(This->propid_to_name);
2038     This->propid_to_name = NULL;
2039     dictionary_destroy(This->propid_to_prop);
2040     This->propid_to_prop = NULL;
2041 }
2042 
2043 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
2044 {
2045     HRESULT hr = S_OK;
2046 
2047     This->name_to_propid = dictionary_create(
2048      PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
2049      This);
2050     if (!This->name_to_propid)
2051     {
2052         hr = STG_E_INSUFFICIENTMEMORY;
2053         goto end;
2054     }
2055     This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
2056      NULL, This);
2057     if (!This->propid_to_name)
2058     {
2059         hr = STG_E_INSUFFICIENTMEMORY;
2060         goto end;
2061     }
2062     This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
2063      PropertyStorage_PropertyDestroy, This);
2064     if (!This->propid_to_prop)
2065     {
2066         hr = STG_E_INSUFFICIENTMEMORY;
2067         goto end;
2068     }
2069 end:
2070     if (FAILED(hr))
2071         PropertyStorage_DestroyDictionaries(This);
2072     return hr;
2073 }
2074 
2075 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
2076  REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
2077 {
2078     HRESULT hr = S_OK;
2079 
2080     assert(pps);
2081     assert(rfmtid);
2082     *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
2083     if (!*pps)
2084         return E_OUTOFMEMORY;
2085 
2086     (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
2087     (*pps)->ref = 1;
2088     InitializeCriticalSection(&(*pps)->cs);
2089     (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2090     (*pps)->stm = stm;
2091     (*pps)->fmtid = *rfmtid;
2092     (*pps)->grfMode = grfMode;
2093 
2094     hr = PropertyStorage_CreateDictionaries(*pps);
2095     if (FAILED(hr))
2096     {
2097         (*pps)->cs.DebugInfo->Spare[0] = 0;
2098         DeleteCriticalSection(&(*pps)->cs);
2099         HeapFree(GetProcessHeap(), 0, *pps);
2100         *pps = NULL;
2101     }
2102     else IStream_AddRef( stm );
2103 
2104     return hr;
2105 }
2106 
2107 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2108  REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2109 {
2110     PropertyStorage_impl *ps;
2111     HRESULT hr;
2112 
2113     assert(pps);
2114     hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2115     if (SUCCEEDED(hr))
2116     {
2117         hr = PropertyStorage_ReadFromStream(ps);
2118         if (SUCCEEDED(hr))
2119         {
2120             *pps = &ps->IPropertyStorage_iface;
2121             TRACE("PropertyStorage %p constructed\n", ps);
2122             hr = S_OK;
2123         }
2124         else IPropertyStorage_Release( &ps->IPropertyStorage_iface );
2125     }
2126     return hr;
2127 }
2128 
2129 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2130  REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2131 {
2132     PropertyStorage_impl *ps;
2133     HRESULT hr;
2134 
2135     assert(pps);
2136     hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2137     if (SUCCEEDED(hr))
2138     {
2139         ps->format = 0;
2140         ps->grfFlags = grfFlags;
2141         if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2142             ps->format = 1;
2143         /* default to Unicode unless told not to, as specified on msdn */
2144         if (ps->grfFlags & PROPSETFLAG_ANSI)
2145             ps->codePage = GetACP();
2146         else
2147             ps->codePage = CP_UNICODE;
2148         ps->locale = LOCALE_SYSTEM_DEFAULT;
2149         TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2150         *pps = &ps->IPropertyStorage_iface;
2151         TRACE("PropertyStorage %p constructed\n", ps);
2152         hr = S_OK;
2153     }
2154     return hr;
2155 }
2156 
2157 
2158 /***********************************************************************
2159  * Implementation of IPropertySetStorage
2160  */
2161 
2162 /************************************************************************
2163  * IPropertySetStorage_fnQueryInterface (IUnknown)
2164  *
2165  *  This method forwards to the common QueryInterface implementation
2166  */
2167 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2168     IPropertySetStorage *ppstg,
2169     REFIID riid,
2170     void** ppvObject)
2171 {
2172     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2173     return IStorage_QueryInterface( &This->base.IStorage_iface, riid, ppvObject );
2174 }
2175 
2176 /************************************************************************
2177  * IPropertySetStorage_fnAddRef (IUnknown)
2178  *
2179  *  This method forwards to the common AddRef implementation
2180  */
2181 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2182     IPropertySetStorage *ppstg)
2183 {
2184     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2185     return IStorage_AddRef( &This->base.IStorage_iface );
2186 }
2187 
2188 /************************************************************************
2189  * IPropertySetStorage_fnRelease (IUnknown)
2190  *
2191  *  This method forwards to the common Release implementation
2192  */
2193 static ULONG WINAPI IPropertySetStorage_fnRelease(
2194     IPropertySetStorage *ppstg)
2195 {
2196     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2197     return IStorage_Release( &This->base.IStorage_iface );
2198 }
2199 
2200 /************************************************************************
2201  * IPropertySetStorage_fnCreate (IPropertySetStorage)
2202  */
2203 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2204     IPropertySetStorage *ppstg,
2205     REFFMTID rfmtid,
2206     const CLSID* pclsid,
2207     DWORD grfFlags,
2208     DWORD grfMode,
2209     IPropertyStorage** ppprstg)
2210 {
2211     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2212     WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2213     IStream *stm = NULL;
2214     HRESULT r;
2215 
2216     TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2217      grfMode, ppprstg);
2218 
2219     /* be picky */
2220     if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2221     {
2222         r = STG_E_INVALIDFLAG;
2223         goto end;
2224     }
2225 
2226     if (!rfmtid)
2227     {
2228         r = E_INVALIDARG;
2229         goto end;
2230     }
2231 
2232     /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2233      * storage, not a stream.  For now, disallow it.
2234      */
2235     if (grfFlags & PROPSETFLAG_NONSIMPLE)
2236     {
2237         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2238         r = STG_E_INVALIDFLAG;
2239         goto end;
2240     }
2241 
2242     r = FmtIdToPropStgName(rfmtid, name);
2243     if (FAILED(r))
2244         goto end;
2245 
2246     r = IStorage_CreateStream( &This->base.IStorage_iface, name, grfMode, 0, 0, &stm );
2247     if (FAILED(r))
2248         goto end;
2249 
2250     r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2251 
2252     IStream_Release( stm );
2253 
2254 end:
2255     TRACE("returning 0x%08x\n", r);
2256     return r;
2257 }
2258 
2259 /************************************************************************
2260  * IPropertySetStorage_fnOpen (IPropertySetStorage)
2261  */
2262 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2263     IPropertySetStorage *ppstg,
2264     REFFMTID rfmtid,
2265     DWORD grfMode,
2266     IPropertyStorage** ppprstg)
2267 {
2268     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2269     IStream *stm = NULL;
2270     WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2271     HRESULT r;
2272 
2273     TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2274 
2275     /* be picky */
2276     if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2277         grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2278     {
2279         r = STG_E_INVALIDFLAG;
2280         goto end;
2281     }
2282 
2283     if (!rfmtid)
2284     {
2285         r = E_INVALIDARG;
2286         goto end;
2287     }
2288 
2289     r = FmtIdToPropStgName(rfmtid, name);
2290     if (FAILED(r))
2291         goto end;
2292 
2293     r = IStorage_OpenStream( &This->base.IStorage_iface, name, 0, grfMode, 0, &stm );
2294     if (FAILED(r))
2295         goto end;
2296 
2297     r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2298 
2299     IStream_Release( stm );
2300 
2301 end:
2302     TRACE("returning 0x%08x\n", r);
2303     return r;
2304 }
2305 
2306 /************************************************************************
2307  * IPropertySetStorage_fnDelete (IPropertySetStorage)
2308  */
2309 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2310     IPropertySetStorage *ppstg,
2311     REFFMTID rfmtid)
2312 {
2313     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2314     WCHAR name[CCH_MAX_PROPSTG_NAME + 1];
2315     HRESULT r;
2316 
2317     TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2318 
2319     if (!rfmtid)
2320         return E_INVALIDARG;
2321 
2322     r = FmtIdToPropStgName(rfmtid, name);
2323     if (FAILED(r))
2324         return r;
2325 
2326     return IStorage_DestroyElement(&This->base.IStorage_iface, name);
2327 }
2328 
2329 /************************************************************************
2330  * IPropertySetStorage_fnEnum (IPropertySetStorage)
2331  */
2332 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2333     IPropertySetStorage *ppstg,
2334     IEnumSTATPROPSETSTG** ppenum)
2335 {
2336     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2337     return create_EnumSTATPROPSETSTG(This, ppenum);
2338 }
2339 
2340 /************************************************************************
2341  * Implement IEnumSTATPROPSETSTG using enumx
2342  */
2343 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2344     IEnumSTATPROPSETSTG *iface,
2345     REFIID riid,
2346     void** ppvObject)
2347 {
2348     return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2349 }
2350 
2351 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2352     IEnumSTATPROPSETSTG *iface)
2353 {
2354     return enumx_AddRef((enumx_impl*)iface);
2355 }
2356 
2357 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2358     IEnumSTATPROPSETSTG *iface)
2359 {
2360     return enumx_Release((enumx_impl*)iface);
2361 }
2362 
2363 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2364     IEnumSTATPROPSETSTG *iface,
2365     ULONG celt,
2366     STATPROPSETSTG *rgelt,
2367     ULONG *pceltFetched)
2368 {
2369     return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2370 }
2371 
2372 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2373     IEnumSTATPROPSETSTG *iface,
2374     ULONG celt)
2375 {
2376     return enumx_Skip((enumx_impl*)iface, celt);
2377 }
2378 
2379 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2380     IEnumSTATPROPSETSTG *iface)
2381 {
2382     return enumx_Reset((enumx_impl*)iface);
2383 }
2384 
2385 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2386     IEnumSTATPROPSETSTG *iface,
2387     IEnumSTATPROPSETSTG **ppenum)
2388 {
2389     return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2390 }
2391 
2392 static HRESULT create_EnumSTATPROPSETSTG(
2393     StorageImpl *This,
2394     IEnumSTATPROPSETSTG** ppenum)
2395 {
2396     IStorage *stg = &This->base.IStorage_iface;
2397     IEnumSTATSTG *penum = NULL;
2398     STATSTG stat;
2399     ULONG count;
2400     HRESULT r;
2401     STATPROPSETSTG statpss;
2402     enumx_impl *enumx;
2403 
2404     TRACE("%p %p\n", This, ppenum);
2405 
2406     enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2407                            &IEnumSTATPROPSETSTG_Vtbl,
2408                            sizeof (STATPROPSETSTG),
2409                            (IUnknown*)&This->base.IStorage_iface,
2410                            NULL);
2411 
2412     /* add all the property set elements into a list */
2413     r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2414     if (FAILED(r))
2415     {
2416         enumx_Release(enumx);
2417         return E_OUTOFMEMORY;
2418     }
2419 
2420     while (1)
2421     {
2422         count = 0;
2423         r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2424         if (FAILED(r))
2425             break;
2426         if (!count)
2427             break;
2428         if (!stat.pwcsName)
2429             continue;
2430         if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2431         {
2432             PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2433             TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2434                   debugstr_guid(&statpss.fmtid));
2435             statpss.mtime = stat.mtime;
2436             statpss.atime = stat.atime;
2437             statpss.ctime = stat.ctime;
2438             statpss.grfFlags = stat.grfMode;
2439             statpss.clsid = stat.clsid;
2440             enumx_add_element(enumx, &statpss);
2441         }
2442         CoTaskMemFree(stat.pwcsName);
2443     }
2444     IEnumSTATSTG_Release(penum);
2445 
2446     *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2447 
2448     return S_OK;
2449 }
2450 
2451 /************************************************************************
2452  * Implement IEnumSTATPROPSTG using enumx
2453  */
2454 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2455     IEnumSTATPROPSTG *iface,
2456     REFIID riid,
2457     void** ppvObject)
2458 {
2459     return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2460 }
2461 
2462 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2463     IEnumSTATPROPSTG *iface)
2464 {
2465     return enumx_AddRef((enumx_impl*)iface);
2466 }
2467 
2468 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2469     IEnumSTATPROPSTG *iface)
2470 {
2471     return enumx_Release((enumx_impl*)iface);
2472 }
2473 
2474 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2475     IEnumSTATPROPSTG *iface,
2476     ULONG celt,
2477     STATPROPSTG *rgelt,
2478     ULONG *pceltFetched)
2479 {
2480     return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2481 }
2482 
2483 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2484     IEnumSTATPROPSTG *iface,
2485     ULONG celt)
2486 {
2487     return enumx_Skip((enumx_impl*)iface, celt);
2488 }
2489 
2490 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2491     IEnumSTATPROPSTG *iface)
2492 {
2493     return enumx_Reset((enumx_impl*)iface);
2494 }
2495 
2496 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2497     IEnumSTATPROPSTG *iface,
2498     IEnumSTATPROPSTG **ppenum)
2499 {
2500     return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2501 }
2502 
2503 static void prop_enum_copy_cb(IUnknown *parent, void *orig, void *dest)
2504 {
2505     PropertyStorage_impl *storage = impl_from_IPropertyStorage((IPropertyStorage*)parent);
2506     STATPROPSTG *src_prop = orig;
2507     STATPROPSTG *dest_prop = dest;
2508     LPWSTR name;
2509 
2510     dest_prop->propid = src_prop->propid;
2511     dest_prop->vt = src_prop->vt;
2512     dest_prop->lpwstrName = NULL;
2513 
2514     if (dictionary_find(storage->propid_to_name, UlongToPtr(src_prop->propid), (void**)&name))
2515     {
2516         DWORD size = (strlenW(name) + 1) * sizeof(WCHAR);
2517 
2518         dest_prop->lpwstrName = CoTaskMemAlloc(size);
2519         if (!dest_prop->lpwstrName) return;
2520         memcpy(dest_prop->lpwstrName, name, size);
2521     }
2522 }
2523 
2524 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2525 {
2526     enumx_impl *enumx = arg;
2527     PROPID propid = PtrToUlong(k);
2528     const PROPVARIANT *prop = v;
2529     STATPROPSTG stat;
2530 
2531     stat.lpwstrName = NULL;
2532     stat.propid = propid;
2533     stat.vt = prop->vt;
2534 
2535     enumx_add_element(enumx, &stat);
2536 
2537     return TRUE;
2538 }
2539 
2540 static HRESULT create_EnumSTATPROPSTG(
2541     PropertyStorage_impl *This,
2542     IEnumSTATPROPSTG** ppenum)
2543 {
2544     enumx_impl *enumx;
2545 
2546     TRACE("%p %p\n", This, ppenum);
2547 
2548     enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2549                            &IEnumSTATPROPSTG_Vtbl,
2550                            sizeof (STATPROPSTG),
2551                            (IUnknown*)&This->IPropertyStorage_iface,
2552                            prop_enum_copy_cb);
2553 
2554     dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2555 
2556     *ppenum = (IEnumSTATPROPSTG*) enumx;
2557 
2558     return S_OK;
2559 }
2560 
2561 /***********************************************************************
2562  * vtables
2563  */
2564 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2565 {
2566     IPropertySetStorage_fnQueryInterface,
2567     IPropertySetStorage_fnAddRef,
2568     IPropertySetStorage_fnRelease,
2569     IPropertySetStorage_fnCreate,
2570     IPropertySetStorage_fnOpen,
2571     IPropertySetStorage_fnDelete,
2572     IPropertySetStorage_fnEnum
2573 };
2574 
2575 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2576 {
2577     IPropertyStorage_fnQueryInterface,
2578     IPropertyStorage_fnAddRef,
2579     IPropertyStorage_fnRelease,
2580     IPropertyStorage_fnReadMultiple,
2581     IPropertyStorage_fnWriteMultiple,
2582     IPropertyStorage_fnDeleteMultiple,
2583     IPropertyStorage_fnReadPropertyNames,
2584     IPropertyStorage_fnWritePropertyNames,
2585     IPropertyStorage_fnDeletePropertyNames,
2586     IPropertyStorage_fnCommit,
2587     IPropertyStorage_fnRevert,
2588     IPropertyStorage_fnEnum,
2589     IPropertyStorage_fnSetTimes,
2590     IPropertyStorage_fnSetClass,
2591     IPropertyStorage_fnStat,
2592 };
2593 
2594 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2595 {
2596     IEnumSTATPROPSETSTG_fnQueryInterface,
2597     IEnumSTATPROPSETSTG_fnAddRef,
2598     IEnumSTATPROPSETSTG_fnRelease,
2599     IEnumSTATPROPSETSTG_fnNext,
2600     IEnumSTATPROPSETSTG_fnSkip,
2601     IEnumSTATPROPSETSTG_fnReset,
2602     IEnumSTATPROPSETSTG_fnClone,
2603 };
2604 
2605 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2606 {
2607     IEnumSTATPROPSTG_fnQueryInterface,
2608     IEnumSTATPROPSTG_fnAddRef,
2609     IEnumSTATPROPSTG_fnRelease,
2610     IEnumSTATPROPSTG_fnNext,
2611     IEnumSTATPROPSTG_fnSkip,
2612     IEnumSTATPROPSTG_fnReset,
2613     IEnumSTATPROPSTG_fnClone,
2614 };
2615 
2616 /***********************************************************************
2617  * Format ID <-> name conversion
2618  */
2619 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2620  'I','n','f','o','r','m','a','t','i','o','n',0 };
2621 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2622  'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2623 
2624 #define BITS_PER_BYTE    8
2625 #define CHARMASK         0x1f
2626 #define BITS_IN_CHARMASK 5
2627 #define NUM_ALPHA_CHARS  26
2628 
2629 /***********************************************************************
2630  * FmtIdToPropStgName					[ole32.@]
2631  * Returns the storage name of the format ID rfmtid.
2632  * PARAMS
2633  *  rfmtid [I] Format ID for which to return a storage name
2634  *  str    [O] Storage name associated with rfmtid.
2635  *
2636  * RETURNS
2637  *  E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2638  *
2639  * NOTES
2640  * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2641  */
2642 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2643 {
2644     static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2645 
2646     TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2647 
2648     if (!rfmtid) return E_INVALIDARG;
2649     if (!str) return E_INVALIDARG;
2650 
2651     if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2652         lstrcpyW(str, szSummaryInfo);
2653     else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2654         lstrcpyW(str, szDocSummaryInfo);
2655     else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2656         lstrcpyW(str, szDocSummaryInfo);
2657     else
2658     {
2659         const BYTE *fmtptr;
2660         WCHAR *pstr = str;
2661         ULONG bitsRemaining = BITS_PER_BYTE;
2662 
2663         *pstr++ = 5;
2664         for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
2665         {
2666             ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2667 
2668             if (bitsRemaining >= BITS_IN_CHARMASK)
2669             {
2670                 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2671                 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2672                  *pstr <= 'z')
2673                     *pstr += 'A' - 'a';
2674                 pstr++;
2675                 bitsRemaining -= BITS_IN_CHARMASK;
2676                 if (bitsRemaining == 0)
2677                 {
2678                     fmtptr++;
2679                     bitsRemaining = BITS_PER_BYTE;
2680                 }
2681             }
2682             else
2683             {
2684                 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2685                     i |= *fmtptr << bitsRemaining;
2686                 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2687                 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2688             }
2689         }
2690         *pstr = 0;
2691     }
2692     TRACE("returning %s\n", debugstr_w(str));
2693     return S_OK;
2694 }
2695 
2696 /***********************************************************************
2697  * PropStgNameToFmtId					[ole32.@]
2698  * Returns the format ID corresponding to the given name.
2699  * PARAMS
2700  *  str    [I] Storage name to convert to a format ID.
2701  *  rfmtid [O] Format ID corresponding to str.
2702  *
2703  * RETURNS
2704  *  E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2705  *  a format ID, S_OK otherwise.
2706  */
2707 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2708 {
2709     HRESULT hr = STG_E_INVALIDNAME;
2710 
2711     TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2712 
2713     if (!rfmtid) return E_INVALIDARG;
2714     if (!str) return STG_E_INVALIDNAME;
2715 
2716     if (!lstrcmpiW(str, szDocSummaryInfo))
2717     {
2718         *rfmtid = FMTID_DocSummaryInformation;
2719         hr = S_OK;
2720     }
2721     else if (!lstrcmpiW(str, szSummaryInfo))
2722     {
2723         *rfmtid = FMTID_SummaryInformation;
2724         hr = S_OK;
2725     }
2726     else
2727     {
2728         ULONG bits;
2729         BYTE *fmtptr = (BYTE *)rfmtid - 1;
2730         const WCHAR *pstr = str;
2731 
2732         memset(rfmtid, 0, sizeof(*rfmtid));
2733         for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2734          bits += BITS_IN_CHARMASK)
2735         {
2736             ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2737             WCHAR wc;
2738 
2739             if (bitsUsed == 0)
2740                 fmtptr++;
2741             wc = *++pstr - 'A';
2742             if (wc > NUM_ALPHA_CHARS)
2743             {
2744                 wc += 'A' - 'a';
2745                 if (wc > NUM_ALPHA_CHARS)
2746                 {
2747                     wc += 'a' - '0' + NUM_ALPHA_CHARS;
2748                     if (wc > CHARMASK)
2749                     {
2750                         WARN("invalid character (%d)\n", *pstr);
2751                         goto end;
2752                     }
2753                 }
2754             }
2755             *fmtptr |= wc << bitsUsed;
2756             bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2757             if (bitsStored < BITS_IN_CHARMASK)
2758             {
2759                 wc >>= BITS_PER_BYTE - bitsUsed;
2760                 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2761                 {
2762                     if (wc != 0)
2763                     {
2764                         WARN("extra bits\n");
2765                         goto end;
2766                     }
2767                     break;
2768                 }
2769                 fmtptr++;
2770                 *fmtptr |= (BYTE)wc;
2771             }
2772         }
2773         hr = S_OK;
2774     }
2775 end:
2776     return hr;
2777 }
2778 
2779 #ifdef __i386__  /* thiscall functions are i386-specific */
2780 
2781 #define DEFINE_STDCALL_WRAPPER(num,func,args) \
2782    __ASM_STDCALL_FUNC(func, args, \
2783                    "popl %eax\n\t" \
2784                    "popl %ecx\n\t" \
2785                    "pushl %eax\n\t" \
2786                    "movl (%ecx), %eax\n\t" \
2787                    "jmp *(4*(" #num "))(%eax)" )
2788 
2789 DEFINE_STDCALL_WRAPPER(0,Allocate_PMemoryAllocator,8)
2790 extern void* __stdcall Allocate_PMemoryAllocator(void *this, ULONG cbSize);
2791 
2792 #else
2793 
2794 static void* __cdecl Allocate_PMemoryAllocator(void *this, ULONG cbSize)
2795 {
2796     void* (__cdecl *fn)(void*,ULONG) = **(void***)this;
2797     return fn(this, cbSize);
2798 }
2799 
2800 #endif
2801 
2802 BOOLEAN WINAPI StgConvertPropertyToVariant(const SERIALIZEDPROPERTYVALUE* prop,
2803     USHORT CodePage, PROPVARIANT* pvar, void* pma)
2804 {
2805     HRESULT hr;
2806 
2807     hr = PropertyStorage_ReadProperty(pvar, (const BYTE*)prop, CodePage, Allocate_PMemoryAllocator, pma);
2808 
2809     if (FAILED(hr))
2810     {
2811         FIXME("should raise C++ exception on failure\n");
2812         PropVariantInit(pvar);
2813     }
2814 
2815     return FALSE;
2816 }
2817 
2818 SERIALIZEDPROPERTYVALUE* WINAPI StgConvertVariantToProperty(const PROPVARIANT *pvar,
2819     USHORT CodePage, SERIALIZEDPROPERTYVALUE *pprop, ULONG *pcb, PROPID pid,
2820     BOOLEAN fReserved, ULONG *pcIndirect)
2821 {
2822     FIXME("%p,%d,%p,%p,%d,%d,%p\n", pvar, CodePage, pprop, pcb, pid, fReserved, pcIndirect);
2823 
2824     return NULL;
2825 }
2826 
2827 HRESULT WINAPI StgCreatePropStg(IUnknown *unk, REFFMTID fmt, const CLSID *clsid,
2828                                 DWORD flags, DWORD reserved, IPropertyStorage **prop_stg)
2829 {
2830     IStorage *stg;
2831     IStream *stm;
2832     HRESULT r;
2833 
2834     TRACE("%p %s %s %08x %d %p\n", unk, debugstr_guid(fmt), debugstr_guid(clsid), flags, reserved, prop_stg);
2835 
2836     if (!fmt || reserved)
2837     {
2838         r = E_INVALIDARG;
2839         goto end;
2840     }
2841 
2842     if (flags & PROPSETFLAG_NONSIMPLE)
2843     {
2844         r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2845         if (FAILED(r))
2846             goto end;
2847 
2848         /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to create a
2849          * storage, not a stream.  For now, disallow it.
2850          */
2851         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2852         IStorage_Release(stg);
2853         r = STG_E_INVALIDFLAG;
2854     }
2855     else
2856     {
2857         r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2858         if (FAILED(r))
2859             goto end;
2860 
2861         r = PropertyStorage_ConstructEmpty(stm, fmt, flags,
2862                 STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2863 
2864         IStream_Release( stm );
2865     }
2866 
2867 end:
2868     TRACE("returning 0x%08x\n", r);
2869     return r;
2870 }
2871 
2872 HRESULT WINAPI StgOpenPropStg(IUnknown *unk, REFFMTID fmt, DWORD flags,
2873                               DWORD reserved, IPropertyStorage **prop_stg)
2874 {
2875     IStorage *stg;
2876     IStream *stm;
2877     HRESULT r;
2878 
2879     TRACE("%p %s %08x %d %p\n", unk, debugstr_guid(fmt), flags, reserved, prop_stg);
2880 
2881     if (!fmt || reserved)
2882     {
2883         r = E_INVALIDARG;
2884         goto end;
2885     }
2886 
2887     if (flags & PROPSETFLAG_NONSIMPLE)
2888     {
2889         r = IUnknown_QueryInterface(unk, &IID_IStorage, (void **)&stg);
2890         if (FAILED(r))
2891             goto end;
2892 
2893         /* FIXME: if (flags & PROPSETFLAG_NONSIMPLE), we need to open a
2894          * storage, not a stream.  For now, disallow it.
2895          */
2896         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2897         IStorage_Release(stg);
2898         r = STG_E_INVALIDFLAG;
2899     }
2900     else
2901     {
2902         r = IUnknown_QueryInterface(unk, &IID_IStream, (void **)&stm);
2903         if (FAILED(r))
2904             goto end;
2905 
2906         r = PropertyStorage_ConstructFromStream(stm, fmt,
2907                 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, prop_stg);
2908 
2909         IStream_Release( stm );
2910     }
2911 
2912 end:
2913     TRACE("returning 0x%08x\n", r);
2914     return r;
2915 }
2916