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