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