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