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