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