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