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