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