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