dinput: Stub IDirectInputJoyConfig8 interface.
[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     case VT_CLSID:
1750     {
1751         CLSID temp;
1752
1753         StorageUtl_WriteGUID((BYTE *)&temp, 0, var->u.puuid);
1754         hr = IStream_Write(This->stm, &temp, sizeof(temp), &count);
1755         bytesWritten = count;
1756         break;
1757     }
1758     default:
1759         FIXME("unsupported type: %d\n", var->vt);
1760         return STG_E_INVALIDPARAMETER;
1761     }
1762
1763     if (SUCCEEDED(hr))
1764     {
1765         *sectionOffset += bytesWritten;
1766         if (bytesWritten % sizeof(DWORD))
1767         {
1768             DWORD padding = sizeof(DWORD) - bytesWritten % sizeof(DWORD);
1769             TRACE("adding %d bytes of padding\n", padding);
1770             *sectionOffset += padding;
1771         }
1772     }
1773
1774 end:
1775     return hr;
1776 }
1777
1778 struct PropertyClosure
1779 {
1780     HRESULT hr;
1781     DWORD   propNum;
1782     DWORD  *sectionOffset;
1783 };
1784
1785 static BOOL PropertyStorage_PropertiesWriter(const void *key, const void *value,
1786  void *extra, void *closure)
1787 {
1788     PropertyStorage_impl *This = extra;
1789     struct PropertyClosure *c = closure;
1790
1791     assert(key);
1792     assert(value);
1793     assert(extra);
1794     assert(closure);
1795     c->hr = PropertyStorage_WritePropertyToStream(This, c->propNum++,
1796                                                   PtrToUlong(key), value, c->sectionOffset);
1797     return SUCCEEDED(c->hr);
1798 }
1799
1800 static HRESULT PropertyStorage_WritePropertiesToStream(
1801  PropertyStorage_impl *This, DWORD startingPropNum, DWORD *sectionOffset)
1802 {
1803     struct PropertyClosure closure;
1804
1805     assert(sectionOffset);
1806     closure.hr = S_OK;
1807     closure.propNum = startingPropNum;
1808     closure.sectionOffset = sectionOffset;
1809     dictionary_enumerate(This->propid_to_prop, PropertyStorage_PropertiesWriter,
1810      &closure);
1811     return closure.hr;
1812 }
1813
1814 static HRESULT PropertyStorage_WriteHeadersToStream(PropertyStorage_impl *This)
1815 {
1816     HRESULT hr;
1817     ULONG count = 0;
1818     LARGE_INTEGER seek = { {0} };
1819     PROPERTYSETHEADER hdr;
1820     FORMATIDOFFSET fmtOffset;
1821
1822     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1823     if (FAILED(hr))
1824         goto end;
1825     PropertyStorage_MakeHeader(This, &hdr);
1826     hr = IStream_Write(This->stm, &hdr, sizeof(hdr), &count);
1827     if (FAILED(hr))
1828         goto end;
1829     if (count != sizeof(hdr))
1830     {
1831         hr = STG_E_WRITEFAULT;
1832         goto end;
1833     }
1834
1835     PropertyStorage_MakeFmtIdOffset(This, &fmtOffset);
1836     hr = IStream_Write(This->stm, &fmtOffset, sizeof(fmtOffset), &count);
1837     if (FAILED(hr))
1838         goto end;
1839     if (count != sizeof(fmtOffset))
1840     {
1841         hr = STG_E_WRITEFAULT;
1842         goto end;
1843     }
1844     hr = S_OK;
1845
1846 end:
1847     return hr;
1848 }
1849
1850 static HRESULT PropertyStorage_WriteToStream(PropertyStorage_impl *This)
1851 {
1852     PROPERTYSECTIONHEADER sectionHdr;
1853     HRESULT hr;
1854     ULONG count;
1855     LARGE_INTEGER seek;
1856     DWORD numProps, prop, sectionOffset, dwTemp;
1857     PROPVARIANT var;
1858
1859     PropertyStorage_WriteHeadersToStream(This);
1860
1861     /* Count properties.  Always at least one property, the code page */
1862     numProps = 1;
1863     if (dictionary_num_entries(This->name_to_propid))
1864         numProps++;
1865     if (This->locale != LOCALE_SYSTEM_DEFAULT)
1866         numProps++;
1867     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1868         numProps++;
1869     numProps += dictionary_num_entries(This->propid_to_prop);
1870
1871     /* Write section header with 0 bytes right now, I'll adjust it after
1872      * writing properties.
1873      */
1874     PropertyStorage_MakeSectionHdr(0, numProps, &sectionHdr);
1875     seek.QuadPart = SECTIONHEADER_OFFSET;
1876     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1877     if (FAILED(hr))
1878         goto end;
1879     hr = IStream_Write(This->stm, &sectionHdr, sizeof(sectionHdr), &count);
1880     if (FAILED(hr))
1881         goto end;
1882
1883     prop = 0;
1884     sectionOffset = sizeof(PROPERTYSECTIONHEADER) +
1885      numProps * sizeof(PROPERTYIDOFFSET);
1886
1887     if (dictionary_num_entries(This->name_to_propid))
1888     {
1889         prop++;
1890         hr = PropertyStorage_WriteDictionaryToStream(This, &sectionOffset);
1891         if (FAILED(hr))
1892             goto end;
1893     }
1894
1895     PropVariantInit(&var);
1896
1897     var.vt = VT_I2;
1898     var.u.iVal = This->codePage;
1899     hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_CODEPAGE,
1900      &var, &sectionOffset);
1901     if (FAILED(hr))
1902         goto end;
1903
1904     if (This->locale != LOCALE_SYSTEM_DEFAULT)
1905     {
1906         var.vt = VT_I4;
1907         var.u.lVal = This->locale;
1908         hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_LOCALE,
1909          &var, &sectionOffset);
1910         if (FAILED(hr))
1911             goto end;
1912     }
1913
1914     if (This->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
1915     {
1916         var.vt = VT_I4;
1917         var.u.lVal = 1;
1918         hr = PropertyStorage_WritePropertyToStream(This, prop++, PID_BEHAVIOR,
1919          &var, &sectionOffset);
1920         if (FAILED(hr))
1921             goto end;
1922     }
1923
1924     hr = PropertyStorage_WritePropertiesToStream(This, prop, &sectionOffset);
1925     if (FAILED(hr))
1926         goto end;
1927
1928     /* Now write the byte count of the section */
1929     seek.QuadPart = SECTIONHEADER_OFFSET;
1930     hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
1931     if (FAILED(hr))
1932         goto end;
1933     StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0, sectionOffset);
1934     hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
1935
1936 end:
1937     return hr;
1938 }
1939
1940 /***********************************************************************
1941  * PropertyStorage_Construct
1942  */
1943 static void PropertyStorage_DestroyDictionaries(PropertyStorage_impl *This)
1944 {
1945     dictionary_destroy(This->name_to_propid);
1946     This->name_to_propid = NULL;
1947     dictionary_destroy(This->propid_to_name);
1948     This->propid_to_name = NULL;
1949     dictionary_destroy(This->propid_to_prop);
1950     This->propid_to_prop = NULL;
1951 }
1952
1953 static HRESULT PropertyStorage_CreateDictionaries(PropertyStorage_impl *This)
1954 {
1955     HRESULT hr = S_OK;
1956
1957     This->name_to_propid = dictionary_create(
1958      PropertyStorage_PropNameCompare, PropertyStorage_PropNameDestroy,
1959      This);
1960     if (!This->name_to_propid)
1961     {
1962         hr = STG_E_INSUFFICIENTMEMORY;
1963         goto end;
1964     }
1965     This->propid_to_name = dictionary_create(PropertyStorage_PropCompare,
1966      NULL, This);
1967     if (!This->propid_to_name)
1968     {
1969         hr = STG_E_INSUFFICIENTMEMORY;
1970         goto end;
1971     }
1972     This->propid_to_prop = dictionary_create(PropertyStorage_PropCompare,
1973      PropertyStorage_PropertyDestroy, This);
1974     if (!This->propid_to_prop)
1975     {
1976         hr = STG_E_INSUFFICIENTMEMORY;
1977         goto end;
1978     }
1979 end:
1980     if (FAILED(hr))
1981         PropertyStorage_DestroyDictionaries(This);
1982     return hr;
1983 }
1984
1985 static HRESULT PropertyStorage_BaseConstruct(IStream *stm,
1986  REFFMTID rfmtid, DWORD grfMode, PropertyStorage_impl **pps)
1987 {
1988     HRESULT hr = S_OK;
1989
1990     assert(pps);
1991     assert(rfmtid);
1992     *pps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof **pps);
1993     if (!*pps)
1994         return E_OUTOFMEMORY;
1995
1996     (*pps)->IPropertyStorage_iface.lpVtbl = &IPropertyStorage_Vtbl;
1997     (*pps)->ref = 1;
1998     InitializeCriticalSection(&(*pps)->cs);
1999     (*pps)->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PropertyStorage_impl.cs");
2000     (*pps)->stm = stm;
2001     (*pps)->fmtid = *rfmtid;
2002     (*pps)->grfMode = grfMode;
2003
2004     hr = PropertyStorage_CreateDictionaries(*pps);
2005     if (FAILED(hr))
2006     {
2007         IStream_Release(stm);
2008         (*pps)->cs.DebugInfo->Spare[0] = 0;
2009         DeleteCriticalSection(&(*pps)->cs);
2010         HeapFree(GetProcessHeap(), 0, *pps);
2011         *pps = NULL;
2012     }
2013
2014     return hr;
2015 }
2016
2017 static HRESULT PropertyStorage_ConstructFromStream(IStream *stm,
2018  REFFMTID rfmtid, DWORD grfMode, IPropertyStorage** pps)
2019 {
2020     PropertyStorage_impl *ps;
2021     HRESULT hr;
2022
2023     assert(pps);
2024     hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2025     if (SUCCEEDED(hr))
2026     {
2027         hr = PropertyStorage_ReadFromStream(ps);
2028         if (SUCCEEDED(hr))
2029         {
2030             *pps = &ps->IPropertyStorage_iface;
2031             TRACE("PropertyStorage %p constructed\n", ps);
2032             hr = S_OK;
2033         }
2034         else
2035         {
2036             PropertyStorage_DestroyDictionaries(ps);
2037             HeapFree(GetProcessHeap(), 0, ps);
2038         }
2039     }
2040     return hr;
2041 }
2042
2043 static HRESULT PropertyStorage_ConstructEmpty(IStream *stm,
2044  REFFMTID rfmtid, DWORD grfFlags, DWORD grfMode, IPropertyStorage** pps)
2045 {
2046     PropertyStorage_impl *ps;
2047     HRESULT hr;
2048
2049     assert(pps);
2050     hr = PropertyStorage_BaseConstruct(stm, rfmtid, grfMode, &ps);
2051     if (SUCCEEDED(hr))
2052     {
2053         ps->format = 0;
2054         ps->grfFlags = grfFlags;
2055         if (ps->grfFlags & PROPSETFLAG_CASE_SENSITIVE)
2056             ps->format = 1;
2057         /* default to Unicode unless told not to, as specified on msdn */
2058         if (ps->grfFlags & PROPSETFLAG_ANSI)
2059             ps->codePage = GetACP();
2060         else
2061             ps->codePage = CP_UNICODE;
2062         ps->locale = LOCALE_SYSTEM_DEFAULT;
2063         TRACE("Code page is %d, locale is %d\n", ps->codePage, ps->locale);
2064         *pps = &ps->IPropertyStorage_iface;
2065         TRACE("PropertyStorage %p constructed\n", ps);
2066         hr = S_OK;
2067     }
2068     return hr;
2069 }
2070
2071
2072 /***********************************************************************
2073  * Implementation of IPropertySetStorage
2074  */
2075
2076 /************************************************************************
2077  * IPropertySetStorage_fnQueryInterface (IUnknown)
2078  *
2079  *  This method forwards to the common QueryInterface implementation
2080  */
2081 static HRESULT WINAPI IPropertySetStorage_fnQueryInterface(
2082     IPropertySetStorage *ppstg,
2083     REFIID riid,
2084     void** ppvObject)
2085 {
2086     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2087     return IStorage_QueryInterface( (IStorage*)This, riid, ppvObject );
2088 }
2089
2090 /************************************************************************
2091  * IPropertySetStorage_fnAddRef (IUnknown)
2092  *
2093  *  This method forwards to the common AddRef implementation
2094  */
2095 static ULONG WINAPI IPropertySetStorage_fnAddRef(
2096     IPropertySetStorage *ppstg)
2097 {
2098     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2099     return IStorage_AddRef( (IStorage*)This );
2100 }
2101
2102 /************************************************************************
2103  * IPropertySetStorage_fnRelease (IUnknown)
2104  *
2105  *  This method forwards to the common Release implementation
2106  */
2107 static ULONG WINAPI IPropertySetStorage_fnRelease(
2108     IPropertySetStorage *ppstg)
2109 {
2110     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2111     return IStorage_Release( (IStorage*)This );
2112 }
2113
2114 /************************************************************************
2115  * IPropertySetStorage_fnCreate (IPropertySetStorage)
2116  */
2117 static HRESULT WINAPI IPropertySetStorage_fnCreate(
2118     IPropertySetStorage *ppstg,
2119     REFFMTID rfmtid,
2120     const CLSID* pclsid,
2121     DWORD grfFlags,
2122     DWORD grfMode,
2123     IPropertyStorage** ppprstg)
2124 {
2125     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2126     WCHAR name[CCH_MAX_PROPSTG_NAME];
2127     IStream *stm = NULL;
2128     HRESULT r;
2129
2130     TRACE("%p %s %08x %08x %p\n", This, debugstr_guid(rfmtid), grfFlags,
2131      grfMode, ppprstg);
2132
2133     /* be picky */
2134     if (grfMode != (STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE))
2135     {
2136         r = STG_E_INVALIDFLAG;
2137         goto end;
2138     }
2139
2140     if (!rfmtid)
2141     {
2142         r = E_INVALIDARG;
2143         goto end;
2144     }
2145
2146     /* FIXME: if (grfFlags & PROPSETFLAG_NONSIMPLE), we need to create a
2147      * storage, not a stream.  For now, disallow it.
2148      */
2149     if (grfFlags & PROPSETFLAG_NONSIMPLE)
2150     {
2151         FIXME("PROPSETFLAG_NONSIMPLE not supported\n");
2152         r = STG_E_INVALIDFLAG;
2153         goto end;
2154     }
2155
2156     r = FmtIdToPropStgName(rfmtid, name);
2157     if (FAILED(r))
2158         goto end;
2159
2160     r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm );
2161     if (FAILED(r))
2162         goto end;
2163
2164     r = PropertyStorage_ConstructEmpty(stm, rfmtid, grfFlags, grfMode, ppprstg);
2165
2166 end:
2167     TRACE("returning 0x%08x\n", r);
2168     return r;
2169 }
2170
2171 /************************************************************************
2172  * IPropertySetStorage_fnOpen (IPropertySetStorage)
2173  */
2174 static HRESULT WINAPI IPropertySetStorage_fnOpen(
2175     IPropertySetStorage *ppstg,
2176     REFFMTID rfmtid,
2177     DWORD grfMode,
2178     IPropertyStorage** ppprstg)
2179 {
2180     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2181     IStream *stm = NULL;
2182     WCHAR name[CCH_MAX_PROPSTG_NAME];
2183     HRESULT r;
2184
2185     TRACE("%p %s %08x %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg);
2186
2187     /* be picky */
2188     if (grfMode != (STGM_READWRITE|STGM_SHARE_EXCLUSIVE) &&
2189         grfMode != (STGM_READ|STGM_SHARE_EXCLUSIVE))
2190     {
2191         r = STG_E_INVALIDFLAG;
2192         goto end;
2193     }
2194
2195     if (!rfmtid)
2196     {
2197         r = E_INVALIDARG;
2198         goto end;
2199     }
2200
2201     r = FmtIdToPropStgName(rfmtid, name);
2202     if (FAILED(r))
2203         goto end;
2204
2205     r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm );
2206     if (FAILED(r))
2207         goto end;
2208
2209     r = PropertyStorage_ConstructFromStream(stm, rfmtid, grfMode, ppprstg);
2210
2211 end:
2212     TRACE("returning 0x%08x\n", r);
2213     return r;
2214 }
2215
2216 /************************************************************************
2217  * IPropertySetStorage_fnDelete (IPropertySetStorage)
2218  */
2219 static HRESULT WINAPI IPropertySetStorage_fnDelete(
2220     IPropertySetStorage *ppstg,
2221     REFFMTID rfmtid)
2222 {
2223     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2224     IStorage *stg = NULL;
2225     WCHAR name[CCH_MAX_PROPSTG_NAME];
2226     HRESULT r;
2227
2228     TRACE("%p %s\n", This, debugstr_guid(rfmtid));
2229
2230     if (!rfmtid)
2231         return E_INVALIDARG;
2232
2233     r = FmtIdToPropStgName(rfmtid, name);
2234     if (FAILED(r))
2235         return r;
2236
2237     stg = (IStorage*) This;
2238     return IStorage_DestroyElement(stg, name);
2239 }
2240
2241 /************************************************************************
2242  * IPropertySetStorage_fnEnum (IPropertySetStorage)
2243  */
2244 static HRESULT WINAPI IPropertySetStorage_fnEnum(
2245     IPropertySetStorage *ppstg,
2246     IEnumSTATPROPSETSTG** ppenum)
2247 {
2248     StorageImpl *This = impl_from_IPropertySetStorage(ppstg);
2249     return create_EnumSTATPROPSETSTG(This, ppenum);
2250 }
2251
2252 /************************************************************************
2253  * Implement IEnumSTATPROPSETSTG using enumx
2254  */
2255 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnQueryInterface(
2256     IEnumSTATPROPSETSTG *iface,
2257     REFIID riid,
2258     void** ppvObject)
2259 {
2260     return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2261 }
2262
2263 static ULONG WINAPI IEnumSTATPROPSETSTG_fnAddRef(
2264     IEnumSTATPROPSETSTG *iface)
2265 {
2266     return enumx_AddRef((enumx_impl*)iface);
2267 }
2268
2269 static ULONG WINAPI IEnumSTATPROPSETSTG_fnRelease(
2270     IEnumSTATPROPSETSTG *iface)
2271 {
2272     return enumx_Release((enumx_impl*)iface);
2273 }
2274
2275 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnNext(
2276     IEnumSTATPROPSETSTG *iface,
2277     ULONG celt,
2278     STATPROPSETSTG *rgelt,
2279     ULONG *pceltFetched)
2280 {
2281     return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2282 }
2283
2284 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnSkip(
2285     IEnumSTATPROPSETSTG *iface,
2286     ULONG celt)
2287 {
2288     return enumx_Skip((enumx_impl*)iface, celt);
2289 }
2290
2291 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnReset(
2292     IEnumSTATPROPSETSTG *iface)
2293 {
2294     return enumx_Reset((enumx_impl*)iface);
2295 }
2296
2297 static HRESULT WINAPI IEnumSTATPROPSETSTG_fnClone(
2298     IEnumSTATPROPSETSTG *iface,
2299     IEnumSTATPROPSETSTG **ppenum)
2300 {
2301     return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2302 }
2303
2304 static HRESULT create_EnumSTATPROPSETSTG(
2305     StorageImpl *This,
2306     IEnumSTATPROPSETSTG** ppenum)
2307 {
2308     IStorage *stg = (IStorage*) &This->base.lpVtbl;
2309     IEnumSTATSTG *penum = NULL;
2310     STATSTG stat;
2311     ULONG count;
2312     HRESULT r;
2313     STATPROPSETSTG statpss;
2314     enumx_impl *enumx;
2315
2316     TRACE("%p %p\n", This, ppenum);
2317
2318     enumx = enumx_allocate(&IID_IEnumSTATPROPSETSTG,
2319                            &IEnumSTATPROPSETSTG_Vtbl,
2320                            sizeof (STATPROPSETSTG));
2321
2322     /* add all the property set elements into a list */
2323     r = IStorage_EnumElements(stg, 0, NULL, 0, &penum);
2324     if (FAILED(r))
2325         return E_OUTOFMEMORY;
2326
2327     while (1)
2328     {
2329         count = 0;
2330         r = IEnumSTATSTG_Next(penum, 1, &stat, &count);
2331         if (FAILED(r))
2332             break;
2333         if (!count)
2334             break;
2335         if (!stat.pwcsName)
2336             continue;
2337         if (stat.pwcsName[0] == 5 && stat.type == STGTY_STREAM)
2338         {
2339             PropStgNameToFmtId(stat.pwcsName, &statpss.fmtid);
2340             TRACE("adding %s (%s)\n", debugstr_w(stat.pwcsName),
2341                   debugstr_guid(&statpss.fmtid));
2342             statpss.mtime = stat.mtime;
2343             statpss.atime = stat.atime;
2344             statpss.ctime = stat.ctime;
2345             statpss.grfFlags = stat.grfMode;
2346             statpss.clsid = stat.clsid;
2347             enumx_add_element(enumx, &statpss);
2348         }
2349         CoTaskMemFree(stat.pwcsName);
2350     }
2351     IEnumSTATSTG_Release(penum);
2352
2353     *ppenum = (IEnumSTATPROPSETSTG*) enumx;
2354
2355     return S_OK;
2356 }
2357
2358 /************************************************************************
2359  * Implement IEnumSTATPROPSTG using enumx
2360  */
2361 static HRESULT WINAPI IEnumSTATPROPSTG_fnQueryInterface(
2362     IEnumSTATPROPSTG *iface,
2363     REFIID riid,
2364     void** ppvObject)
2365 {
2366     return enumx_QueryInterface((enumx_impl*)iface, riid, ppvObject);
2367 }
2368
2369 static ULONG WINAPI IEnumSTATPROPSTG_fnAddRef(
2370     IEnumSTATPROPSTG *iface)
2371 {
2372     return enumx_AddRef((enumx_impl*)iface);
2373 }
2374
2375 static ULONG WINAPI IEnumSTATPROPSTG_fnRelease(
2376     IEnumSTATPROPSTG *iface)
2377 {
2378     return enumx_Release((enumx_impl*)iface);
2379 }
2380
2381 static HRESULT WINAPI IEnumSTATPROPSTG_fnNext(
2382     IEnumSTATPROPSTG *iface,
2383     ULONG celt,
2384     STATPROPSTG *rgelt,
2385     ULONG *pceltFetched)
2386 {
2387     return enumx_Next((enumx_impl*)iface, celt, rgelt, pceltFetched);
2388 }
2389
2390 static HRESULT WINAPI IEnumSTATPROPSTG_fnSkip(
2391     IEnumSTATPROPSTG *iface,
2392     ULONG celt)
2393 {
2394     return enumx_Skip((enumx_impl*)iface, celt);
2395 }
2396
2397 static HRESULT WINAPI IEnumSTATPROPSTG_fnReset(
2398     IEnumSTATPROPSTG *iface)
2399 {
2400     return enumx_Reset((enumx_impl*)iface);
2401 }
2402
2403 static HRESULT WINAPI IEnumSTATPROPSTG_fnClone(
2404     IEnumSTATPROPSTG *iface,
2405     IEnumSTATPROPSTG **ppenum)
2406 {
2407     return enumx_Clone((enumx_impl*)iface, (enumx_impl**)ppenum);
2408 }
2409
2410 static BOOL prop_enum_stat(const void *k, const void *v, void *extra, void *arg)
2411 {
2412     enumx_impl *enumx = arg;
2413     PROPID propid = PtrToUlong(k);
2414     const PROPVARIANT *prop = v;
2415     STATPROPSTG stat;
2416
2417     stat.lpwstrName = NULL;
2418     stat.propid = propid;
2419     stat.vt = prop->vt;
2420
2421     enumx_add_element(enumx, &stat);
2422
2423     return TRUE;
2424 }
2425
2426 static HRESULT create_EnumSTATPROPSTG(
2427     PropertyStorage_impl *This,
2428     IEnumSTATPROPSTG** ppenum)
2429 {
2430     enumx_impl *enumx;
2431
2432     TRACE("%p %p\n", This, ppenum);
2433
2434     enumx = enumx_allocate(&IID_IEnumSTATPROPSTG,
2435                            &IEnumSTATPROPSTG_Vtbl,
2436                            sizeof (STATPROPSTG));
2437
2438     dictionary_enumerate(This->propid_to_prop, prop_enum_stat, enumx);
2439
2440     *ppenum = (IEnumSTATPROPSTG*) enumx;
2441
2442     return S_OK;
2443 }
2444
2445 /***********************************************************************
2446  * vtables
2447  */
2448 const IPropertySetStorageVtbl IPropertySetStorage_Vtbl =
2449 {
2450     IPropertySetStorage_fnQueryInterface,
2451     IPropertySetStorage_fnAddRef,
2452     IPropertySetStorage_fnRelease,
2453     IPropertySetStorage_fnCreate,
2454     IPropertySetStorage_fnOpen,
2455     IPropertySetStorage_fnDelete,
2456     IPropertySetStorage_fnEnum
2457 };
2458
2459 static const IPropertyStorageVtbl IPropertyStorage_Vtbl =
2460 {
2461     IPropertyStorage_fnQueryInterface,
2462     IPropertyStorage_fnAddRef,
2463     IPropertyStorage_fnRelease,
2464     IPropertyStorage_fnReadMultiple,
2465     IPropertyStorage_fnWriteMultiple,
2466     IPropertyStorage_fnDeleteMultiple,
2467     IPropertyStorage_fnReadPropertyNames,
2468     IPropertyStorage_fnWritePropertyNames,
2469     IPropertyStorage_fnDeletePropertyNames,
2470     IPropertyStorage_fnCommit,
2471     IPropertyStorage_fnRevert,
2472     IPropertyStorage_fnEnum,
2473     IPropertyStorage_fnSetTimes,
2474     IPropertyStorage_fnSetClass,
2475     IPropertyStorage_fnStat,
2476 };
2477
2478 static const IEnumSTATPROPSETSTGVtbl IEnumSTATPROPSETSTG_Vtbl =
2479 {
2480     IEnumSTATPROPSETSTG_fnQueryInterface,
2481     IEnumSTATPROPSETSTG_fnAddRef,
2482     IEnumSTATPROPSETSTG_fnRelease,
2483     IEnumSTATPROPSETSTG_fnNext,
2484     IEnumSTATPROPSETSTG_fnSkip,
2485     IEnumSTATPROPSETSTG_fnReset,
2486     IEnumSTATPROPSETSTG_fnClone,
2487 };
2488
2489 static const IEnumSTATPROPSTGVtbl IEnumSTATPROPSTG_Vtbl =
2490 {
2491     IEnumSTATPROPSTG_fnQueryInterface,
2492     IEnumSTATPROPSTG_fnAddRef,
2493     IEnumSTATPROPSTG_fnRelease,
2494     IEnumSTATPROPSTG_fnNext,
2495     IEnumSTATPROPSTG_fnSkip,
2496     IEnumSTATPROPSTG_fnReset,
2497     IEnumSTATPROPSTG_fnClone,
2498 };
2499
2500 /***********************************************************************
2501  * Format ID <-> name conversion
2502  */
2503 static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
2504  'I','n','f','o','r','m','a','t','i','o','n',0 };
2505 static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
2506  'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 };
2507
2508 #define BITS_PER_BYTE    8
2509 #define CHARMASK         0x1f
2510 #define BITS_IN_CHARMASK 5
2511 #define NUM_ALPHA_CHARS  26
2512
2513 /***********************************************************************
2514  * FmtIdToPropStgName                                   [ole32.@]
2515  * Returns the storage name of the format ID rfmtid.
2516  * PARAMS
2517  *  rfmtid [I] Format ID for which to return a storage name
2518  *  str    [O] Storage name associated with rfmtid.
2519  *
2520  * RETURNS
2521  *  E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise.
2522  *
2523  * NOTES
2524  * str must be at least CCH_MAX_PROPSTG_NAME characters in length.
2525  */
2526 HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str)
2527 {
2528     static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345";
2529
2530     TRACE("%s, %p\n", debugstr_guid(rfmtid), str);
2531
2532     if (!rfmtid) return E_INVALIDARG;
2533     if (!str) return E_INVALIDARG;
2534
2535     if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid))
2536         lstrcpyW(str, szSummaryInfo);
2537     else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid))
2538         lstrcpyW(str, szDocSummaryInfo);
2539     else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid))
2540         lstrcpyW(str, szDocSummaryInfo);
2541     else
2542     {
2543         const BYTE *fmtptr;
2544         WCHAR *pstr = str;
2545         ULONG bitsRemaining = BITS_PER_BYTE;
2546
2547         *pstr++ = 5;
2548         for (fmtptr = (const BYTE *)rfmtid; fmtptr < (const BYTE *)rfmtid + sizeof(FMTID); )
2549         {
2550             ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining);
2551
2552             if (bitsRemaining >= BITS_IN_CHARMASK)
2553             {
2554                 *pstr = (WCHAR)(fmtMap[i & CHARMASK]);
2555                 if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' &&
2556                  *pstr <= 'z')
2557                     *pstr += 'A' - 'a';
2558                 pstr++;
2559                 bitsRemaining -= BITS_IN_CHARMASK;
2560                 if (bitsRemaining == 0)
2561                 {
2562                     fmtptr++;
2563                     bitsRemaining = BITS_PER_BYTE;
2564                 }
2565             }
2566             else
2567             {
2568                 if (++fmtptr < (const BYTE *)rfmtid + sizeof(FMTID))
2569                     i |= *fmtptr << bitsRemaining;
2570                 *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]);
2571                 bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK;
2572             }
2573         }
2574         *pstr = 0;
2575     }
2576     TRACE("returning %s\n", debugstr_w(str));
2577     return S_OK;
2578 }
2579
2580 /***********************************************************************
2581  * PropStgNameToFmtId                                   [ole32.@]
2582  * Returns the format ID corresponding to the given name.
2583  * PARAMS
2584  *  str    [I] Storage name to convert to a format ID.
2585  *  rfmtid [O] Format ID corresponding to str.
2586  *
2587  * RETURNS
2588  *  E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to
2589  *  a format ID, S_OK otherwise.
2590  */
2591 HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid)
2592 {
2593     HRESULT hr = STG_E_INVALIDNAME;
2594
2595     TRACE("%s, %p\n", debugstr_w(str), rfmtid);
2596
2597     if (!rfmtid) return E_INVALIDARG;
2598     if (!str) return STG_E_INVALIDNAME;
2599
2600     if (!lstrcmpiW(str, szDocSummaryInfo))
2601     {
2602         *rfmtid = FMTID_DocSummaryInformation;
2603         hr = S_OK;
2604     }
2605     else if (!lstrcmpiW(str, szSummaryInfo))
2606     {
2607         *rfmtid = FMTID_SummaryInformation;
2608         hr = S_OK;
2609     }
2610     else
2611     {
2612         ULONG bits;
2613         BYTE *fmtptr = (BYTE *)rfmtid - 1;
2614         const WCHAR *pstr = str;
2615
2616         memset(rfmtid, 0, sizeof(*rfmtid));
2617         for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE;
2618          bits += BITS_IN_CHARMASK)
2619         {
2620             ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored;
2621             WCHAR wc;
2622
2623             if (bitsUsed == 0)
2624                 fmtptr++;
2625             wc = *++pstr - 'A';
2626             if (wc > NUM_ALPHA_CHARS)
2627             {
2628                 wc += 'A' - 'a';
2629                 if (wc > NUM_ALPHA_CHARS)
2630                 {
2631                     wc += 'a' - '0' + NUM_ALPHA_CHARS;
2632                     if (wc > CHARMASK)
2633                     {
2634                         WARN("invalid character (%d)\n", *pstr);
2635                         goto end;
2636                     }
2637                 }
2638             }
2639             *fmtptr |= wc << bitsUsed;
2640             bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK);
2641             if (bitsStored < BITS_IN_CHARMASK)
2642             {
2643                 wc >>= BITS_PER_BYTE - bitsUsed;
2644                 if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE)
2645                 {
2646                     if (wc != 0)
2647                     {
2648                         WARN("extra bits\n");
2649                         goto end;
2650                     }
2651                     break;
2652                 }
2653                 fmtptr++;
2654                 *fmtptr |= (BYTE)wc;
2655             }
2656         }
2657         hr = S_OK;
2658     }
2659 end:
2660     return hr;
2661 }