msxml3: Fix xml declaration output when it's specified in loaded document (in case...
[wine] / dlls / windowscodecs / metadatahandler.c
1 /*
2  * Copyright 2012 Vincent Povirk for CodeWeavers
3  * Copyright 2012 Dmitry Timoshkov
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include "config.h"
21
22 #include <stdarg.h>
23 #include <stdio.h>
24
25 #define COBJMACROS
26 #define NONAMELESSUNION
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winternl.h"
31 #include "objbase.h"
32 #include "wincodec.h"
33 #include "wincodecsdk.h"
34
35 #include "wincodecs_private.h"
36
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
40
41 typedef struct MetadataHandler {
42     IWICMetadataWriter IWICMetadataWriter_iface;
43     LONG ref;
44     IWICPersistStream IWICPersistStream_iface;
45     const MetadataHandlerVtbl *vtable;
46     MetadataItem *items;
47     DWORD item_count;
48     CRITICAL_SECTION lock;
49 } MetadataHandler;
50
51 static inline MetadataHandler *impl_from_IWICMetadataWriter(IWICMetadataWriter *iface)
52 {
53     return CONTAINING_RECORD(iface, MetadataHandler, IWICMetadataWriter_iface);
54 }
55
56 static inline MetadataHandler *impl_from_IWICPersistStream(IWICPersistStream *iface)
57 {
58     return CONTAINING_RECORD(iface, MetadataHandler, IWICPersistStream_iface);
59 }
60
61 static void MetadataHandler_FreeItems(MetadataHandler *This)
62 {
63     int i;
64
65     for (i=0; i<This->item_count; i++)
66     {
67         PropVariantClear(&This->items[i].schema);
68         PropVariantClear(&This->items[i].id);
69         PropVariantClear(&This->items[i].value);
70     }
71
72     HeapFree(GetProcessHeap(), 0, This->items);
73 }
74
75 static HRESULT MetadataHandlerEnum_Create(MetadataHandler *parent, DWORD index,
76     IWICEnumMetadataItem **ppIEnumMetadataItem);
77
78 static HRESULT WINAPI MetadataHandler_QueryInterface(IWICMetadataWriter *iface, REFIID iid,
79     void **ppv)
80 {
81     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
82     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
83
84     if (!ppv) return E_INVALIDARG;
85
86     if (IsEqualIID(&IID_IUnknown, iid) ||
87         IsEqualIID(&IID_IWICMetadataReader, iid) ||
88         (IsEqualIID(&IID_IWICMetadataWriter, iid) && This->vtable->is_writer))
89     {
90         *ppv = &This->IWICMetadataWriter_iface;
91     }
92     else if (IsEqualIID(&IID_IPersist, iid) ||
93              IsEqualIID(&IID_IPersistStream, iid) ||
94              IsEqualIID(&IID_IWICPersistStream, iid))
95     {
96         *ppv = &This->IWICPersistStream_iface;
97     }
98     else
99     {
100         *ppv = NULL;
101         return E_NOINTERFACE;
102     }
103
104     IUnknown_AddRef((IUnknown*)*ppv);
105     return S_OK;
106 }
107
108 static ULONG WINAPI MetadataHandler_AddRef(IWICMetadataWriter *iface)
109 {
110     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
111     ULONG ref = InterlockedIncrement(&This->ref);
112
113     TRACE("(%p) refcount=%u\n", iface, ref);
114
115     return ref;
116 }
117
118 static ULONG WINAPI MetadataHandler_Release(IWICMetadataWriter *iface)
119 {
120     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
121     ULONG ref = InterlockedDecrement(&This->ref);
122
123     TRACE("(%p) refcount=%u\n", iface, ref);
124
125     if (ref == 0)
126     {
127         MetadataHandler_FreeItems(This);
128         This->lock.DebugInfo->Spare[0] = 0;
129         DeleteCriticalSection(&This->lock);
130         HeapFree(GetProcessHeap(), 0, This);
131     }
132
133     return ref;
134 }
135
136 static HRESULT WINAPI MetadataHandler_GetMetadataHandlerInfo(IWICMetadataWriter *iface,
137     IWICMetadataHandlerInfo **ppIHandler)
138 {
139     HRESULT hr;
140     IWICComponentInfo *component_info;
141     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
142
143     TRACE("%p,%p\n", iface, ppIHandler);
144
145     hr = CreateComponentInfo(This->vtable->clsid, &component_info);
146     if (FAILED(hr)) return hr;
147
148     hr = IWICComponentInfo_QueryInterface(component_info, &IID_IWICMetadataHandlerInfo,
149         (void **)ppIHandler);
150
151     IWICComponentInfo_Release(component_info);
152     return hr;
153 }
154
155 static HRESULT WINAPI MetadataHandler_GetMetadataFormat(IWICMetadataWriter *iface,
156     GUID *pguidMetadataFormat)
157 {
158     HRESULT hr;
159     IWICMetadataHandlerInfo *metadata_info;
160
161     TRACE("%p,%p\n", iface, pguidMetadataFormat);
162
163     if (!pguidMetadataFormat) return E_INVALIDARG;
164
165     hr = MetadataHandler_GetMetadataHandlerInfo(iface, &metadata_info);
166     if (FAILED(hr)) return hr;
167
168     hr = IWICMetadataHandlerInfo_GetMetadataFormat(metadata_info, pguidMetadataFormat);
169     IWICMetadataHandlerInfo_Release(metadata_info);
170
171     return hr;
172 }
173
174 static HRESULT WINAPI MetadataHandler_GetCount(IWICMetadataWriter *iface,
175     UINT *pcCount)
176 {
177     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
178
179     TRACE("%p,%p\n", iface, pcCount);
180
181     if (!pcCount) return E_INVALIDARG;
182
183     *pcCount = This->item_count;
184     return S_OK;
185 }
186
187 static HRESULT WINAPI MetadataHandler_GetValueByIndex(IWICMetadataWriter *iface,
188     UINT index, PROPVARIANT *schema, PROPVARIANT *id, PROPVARIANT *value)
189 {
190     HRESULT hr = S_OK;
191     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
192
193     TRACE("%p,%u,%p,%p,%p\n", iface, index, schema, id, value);
194
195     EnterCriticalSection(&This->lock);
196
197     if (index >= This->item_count)
198     {
199         LeaveCriticalSection(&This->lock);
200         return E_INVALIDARG;
201     }
202
203     if (schema)
204         hr = PropVariantCopy(schema, &This->items[index].schema);
205
206     if (SUCCEEDED(hr) && id)
207         hr = PropVariantCopy(id, &This->items[index].id);
208
209     if (SUCCEEDED(hr) && value)
210         hr = PropVariantCopy(value, &This->items[index].value);
211
212     LeaveCriticalSection(&This->lock);
213     return hr;
214 }
215
216 static BOOL get_int_value(const PROPVARIANT *pv, LONGLONG *value)
217 {
218     switch (pv->vt)
219     {
220     case VT_NULL:
221     case VT_EMPTY:
222         *value = 0;
223         break;
224     case VT_I1:
225         *value = pv->u.cVal;
226         break;
227     case VT_UI1:
228         *value = pv->u.bVal;
229         break;
230     case VT_I2:
231         *value = pv->u.iVal;
232         break;
233     case VT_UI2:
234         *value = pv->u.uiVal;
235         break;
236     case VT_I4:
237         *value = pv->u.lVal;
238         break;
239     case VT_UI4:
240         *value = pv->u.ulVal;
241         break;
242     case VT_I8:
243     case VT_UI8:
244         *value = pv->u.hVal.QuadPart;
245         break;
246     default:
247         FIXME("not supported variant type %d\n", pv->vt);
248         return FALSE;
249     }
250     return TRUE;
251 }
252
253 /* FiXME: Use propsys.PropVariantCompareEx once it's implemented */
254 static int propvar_cmp(const PROPVARIANT *v1, const PROPVARIANT *v2)
255 {
256     LONGLONG value1, value2;
257
258     if (!get_int_value(v1, &value1)) return -1;
259     if (!get_int_value(v2, &value2)) return -1;
260
261     value1 -= value2;
262     if (value1) return value1 < 0 ? -1 : 1;
263     return 0;
264 }
265
266 static HRESULT WINAPI MetadataHandler_GetValue(IWICMetadataWriter *iface,
267     const PROPVARIANT *schema, const PROPVARIANT *id, PROPVARIANT *value)
268 {
269     UINT i;
270     HRESULT hr = WINCODEC_ERR_PROPERTYNOTFOUND;
271     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
272
273     TRACE("(%p,%p,%p,%p)\n", iface, schema, id, value);
274
275     if (!id) return E_INVALIDARG;
276
277     EnterCriticalSection(&This->lock);
278
279     for (i = 0; i < This->item_count; i++)
280     {
281         if (schema && This->items[i].schema.vt != VT_EMPTY)
282         {
283             if (propvar_cmp(schema, &This->items[i].schema) != 0) continue;
284         }
285
286         if (propvar_cmp(id, &This->items[i].id) != 0) continue;
287
288         hr = value ? PropVariantCopy(value, &This->items[i].value) : S_OK;
289         break;
290     }
291
292     LeaveCriticalSection(&This->lock);
293     return hr;
294 }
295
296 static HRESULT WINAPI MetadataHandler_GetEnumerator(IWICMetadataWriter *iface,
297     IWICEnumMetadataItem **ppIEnumMetadata)
298 {
299     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
300     TRACE("(%p,%p)\n", iface, ppIEnumMetadata);
301     return MetadataHandlerEnum_Create(This, 0, ppIEnumMetadata);
302 }
303
304 static HRESULT WINAPI MetadataHandler_SetValue(IWICMetadataWriter *iface,
305     const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, const PROPVARIANT *pvarValue)
306 {
307     FIXME("(%p,%p,%p,%p): stub\n", iface, pvarSchema, pvarId, pvarValue);
308     return E_NOTIMPL;
309 }
310
311 static HRESULT WINAPI MetadataHandler_SetValueByIndex(IWICMetadataWriter *iface,
312     UINT nIndex, const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, const PROPVARIANT *pvarValue)
313 {
314     FIXME("(%p,%u,%p,%p,%p): stub\n", iface, nIndex, pvarSchema, pvarId, pvarValue);
315     return E_NOTIMPL;
316 }
317
318 static HRESULT WINAPI MetadataHandler_RemoveValue(IWICMetadataWriter *iface,
319     const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId)
320 {
321     FIXME("(%p,%p,%p): stub\n", iface, pvarSchema, pvarId);
322     return E_NOTIMPL;
323 }
324
325 static HRESULT WINAPI MetadataHandler_RemoveValueByIndex(IWICMetadataWriter *iface,
326     UINT nIndex)
327 {
328     FIXME("(%p,%u): stub\n", iface, nIndex);
329     return E_NOTIMPL;
330 }
331
332 static const IWICMetadataWriterVtbl MetadataHandler_Vtbl = {
333     MetadataHandler_QueryInterface,
334     MetadataHandler_AddRef,
335     MetadataHandler_Release,
336     MetadataHandler_GetMetadataFormat,
337     MetadataHandler_GetMetadataHandlerInfo,
338     MetadataHandler_GetCount,
339     MetadataHandler_GetValueByIndex,
340     MetadataHandler_GetValue,
341     MetadataHandler_GetEnumerator,
342     MetadataHandler_SetValue,
343     MetadataHandler_SetValueByIndex,
344     MetadataHandler_RemoveValue,
345     MetadataHandler_RemoveValueByIndex
346 };
347
348 static HRESULT WINAPI MetadataHandler_PersistStream_QueryInterface(IWICPersistStream *iface,
349     REFIID iid, void **ppv)
350 {
351     MetadataHandler *This = impl_from_IWICPersistStream(iface);
352     return IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv);
353 }
354
355 static ULONG WINAPI MetadataHandler_PersistStream_AddRef(IWICPersistStream *iface)
356 {
357     MetadataHandler *This = impl_from_IWICPersistStream(iface);
358     return IWICMetadataWriter_AddRef(&This->IWICMetadataWriter_iface);
359 }
360
361 static ULONG WINAPI MetadataHandler_PersistStream_Release(IWICPersistStream *iface)
362 {
363     MetadataHandler *This = impl_from_IWICPersistStream(iface);
364     return IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface);
365 }
366
367 static HRESULT WINAPI MetadataHandler_GetClassID(IWICPersistStream *iface,
368     CLSID *pClassID)
369 {
370     FIXME("(%p,%p): stub\n", iface, pClassID);
371     return E_NOTIMPL;
372 }
373
374 static HRESULT WINAPI MetadataHandler_IsDirty(IWICPersistStream *iface)
375 {
376     FIXME("(%p): stub\n", iface);
377     return E_NOTIMPL;
378 }
379
380 static HRESULT WINAPI MetadataHandler_Load(IWICPersistStream *iface,
381     IStream *pStm)
382 {
383     FIXME("(%p,%p): stub\n", iface, pStm);
384     return E_NOTIMPL;
385 }
386
387 static HRESULT WINAPI MetadataHandler_Save(IWICPersistStream *iface,
388     IStream *pStm, BOOL fClearDirty)
389 {
390     FIXME("(%p,%p,%i): stub\n", iface, pStm, fClearDirty);
391     return E_NOTIMPL;
392 }
393
394 static HRESULT WINAPI MetadataHandler_GetSizeMax(IWICPersistStream *iface,
395     ULARGE_INTEGER *pcbSize)
396 {
397     FIXME("(%p,%p): stub\n", iface, pcbSize);
398     return E_NOTIMPL;
399 }
400
401 static HRESULT WINAPI MetadataHandler_LoadEx(IWICPersistStream *iface,
402     IStream *pIStream, const GUID *pguidPreferredVendor, DWORD dwPersistOptions)
403 {
404     MetadataHandler *This = impl_from_IWICPersistStream(iface);
405     HRESULT hr;
406     MetadataItem *new_items=NULL;
407     DWORD item_count=0;
408
409     TRACE("(%p,%p,%s,%x)\n", iface, pIStream, debugstr_guid(pguidPreferredVendor), dwPersistOptions);
410
411     EnterCriticalSection(&This->lock);
412
413     hr = This->vtable->fnLoad(pIStream, pguidPreferredVendor, dwPersistOptions,
414         &new_items, &item_count);
415
416     if (SUCCEEDED(hr))
417     {
418         MetadataHandler_FreeItems(This);
419         This->items = new_items;
420         This->item_count = item_count;
421     }
422
423     LeaveCriticalSection(&This->lock);
424
425     return hr;
426 }
427
428 static HRESULT WINAPI MetadataHandler_SaveEx(IWICPersistStream *iface,
429     IStream *pIStream, DWORD dwPersistOptions, BOOL fClearDirty)
430 {
431     FIXME("(%p,%p,%x,%i): stub\n", iface, pIStream, dwPersistOptions, fClearDirty);
432     return E_NOTIMPL;
433 }
434
435 static const IWICPersistStreamVtbl MetadataHandler_PersistStream_Vtbl = {
436     MetadataHandler_PersistStream_QueryInterface,
437     MetadataHandler_PersistStream_AddRef,
438     MetadataHandler_PersistStream_Release,
439     MetadataHandler_GetClassID,
440     MetadataHandler_IsDirty,
441     MetadataHandler_Load,
442     MetadataHandler_Save,
443     MetadataHandler_GetSizeMax,
444     MetadataHandler_LoadEx,
445     MetadataHandler_SaveEx
446 };
447
448 static HRESULT MetadataReader_Create(const MetadataHandlerVtbl *vtable, IUnknown *pUnkOuter, REFIID iid, void** ppv)
449 {
450     MetadataHandler *This;
451     HRESULT hr;
452
453     TRACE("%s\n", debugstr_guid(vtable->clsid));
454
455     *ppv = NULL;
456
457     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
458
459     This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandler));
460     if (!This) return E_OUTOFMEMORY;
461
462     This->IWICMetadataWriter_iface.lpVtbl = &MetadataHandler_Vtbl;
463     This->IWICPersistStream_iface.lpVtbl = &MetadataHandler_PersistStream_Vtbl;
464     This->ref = 1;
465     This->vtable = vtable;
466     This->items = NULL;
467     This->item_count = 0;
468
469     InitializeCriticalSection(&This->lock);
470     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MetadataHandler.lock");
471
472     hr = IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv);
473
474     IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface);
475
476     return hr;
477 }
478
479 typedef struct MetadataHandlerEnum {
480     IWICEnumMetadataItem IWICEnumMetadataItem_iface;
481     LONG ref;
482     MetadataHandler *parent;
483     DWORD index;
484 } MetadataHandlerEnum;
485
486 static inline MetadataHandlerEnum *impl_from_IWICEnumMetadataItem(IWICEnumMetadataItem *iface)
487 {
488     return CONTAINING_RECORD(iface, MetadataHandlerEnum, IWICEnumMetadataItem_iface);
489 }
490
491 static HRESULT WINAPI MetadataHandlerEnum_QueryInterface(IWICEnumMetadataItem *iface, REFIID iid,
492     void **ppv)
493 {
494     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
495     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
496
497     if (!ppv) return E_INVALIDARG;
498
499     if (IsEqualIID(&IID_IUnknown, iid) ||
500         IsEqualIID(&IID_IWICEnumMetadataItem, iid))
501     {
502         *ppv = &This->IWICEnumMetadataItem_iface;
503     }
504     else
505     {
506         *ppv = NULL;
507         return E_NOINTERFACE;
508     }
509
510     IUnknown_AddRef((IUnknown*)*ppv);
511     return S_OK;
512 }
513
514 static ULONG WINAPI MetadataHandlerEnum_AddRef(IWICEnumMetadataItem *iface)
515 {
516     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
517     ULONG ref = InterlockedIncrement(&This->ref);
518
519     TRACE("(%p) refcount=%u\n", iface, ref);
520
521     return ref;
522 }
523
524 static ULONG WINAPI MetadataHandlerEnum_Release(IWICEnumMetadataItem *iface)
525 {
526     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
527     ULONG ref = InterlockedDecrement(&This->ref);
528
529     TRACE("(%p) refcount=%u\n", iface, ref);
530
531     if (ref == 0)
532     {
533         IWICMetadataWriter_Release(&This->parent->IWICMetadataWriter_iface);
534         HeapFree(GetProcessHeap(), 0, This);
535     }
536
537     return ref;
538 }
539
540 static HRESULT WINAPI MetadataHandlerEnum_Next(IWICEnumMetadataItem *iface,
541     ULONG celt, PROPVARIANT *rgeltSchema, PROPVARIANT *rgeltId,
542     PROPVARIANT *rgeltValue, ULONG *pceltFetched)
543 {
544     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
545     ULONG new_index;
546     HRESULT hr=S_FALSE;
547     int i;
548
549     TRACE("(%p,%i)\n", iface, celt);
550
551     EnterCriticalSection(&This->parent->lock);
552
553     if (This->index >= This->parent->item_count)
554     {
555         *pceltFetched = 0;
556         LeaveCriticalSection(&This->parent->lock);
557         return S_FALSE;
558     }
559
560     new_index = min(This->parent->item_count, This->index + celt);
561     *pceltFetched = new_index - This->index;
562
563     if (rgeltSchema)
564     {
565         for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
566             hr = PropVariantCopy(&rgeltSchema[i], &This->parent->items[i+This->index].schema);
567     }
568
569     for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
570         hr = PropVariantCopy(&rgeltId[i], &This->parent->items[i+This->index].id);
571
572     if (rgeltValue)
573     {
574         for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
575             hr = PropVariantCopy(&rgeltValue[i], &This->parent->items[i+This->index].value);
576     }
577
578     if (SUCCEEDED(hr))
579     {
580         This->index = new_index;
581     }
582
583     LeaveCriticalSection(&This->parent->lock);
584
585     return hr;
586 }
587
588 static HRESULT WINAPI MetadataHandlerEnum_Skip(IWICEnumMetadataItem *iface,
589     ULONG celt)
590 {
591     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
592
593     EnterCriticalSection(&This->parent->lock);
594
595     This->index += celt;
596
597     LeaveCriticalSection(&This->parent->lock);
598
599     return S_OK;
600 }
601
602 static HRESULT WINAPI MetadataHandlerEnum_Reset(IWICEnumMetadataItem *iface)
603 {
604     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
605
606     EnterCriticalSection(&This->parent->lock);
607
608     This->index = 0;
609
610     LeaveCriticalSection(&This->parent->lock);
611
612     return S_OK;
613 }
614
615 static HRESULT WINAPI MetadataHandlerEnum_Clone(IWICEnumMetadataItem *iface,
616     IWICEnumMetadataItem **ppIEnumMetadataItem)
617 {
618     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
619     HRESULT hr;
620
621     EnterCriticalSection(&This->parent->lock);
622
623     hr = MetadataHandlerEnum_Create(This->parent, This->index, ppIEnumMetadataItem);
624
625     LeaveCriticalSection(&This->parent->lock);
626
627     return hr;
628 }
629
630 static const IWICEnumMetadataItemVtbl MetadataHandlerEnum_Vtbl = {
631     MetadataHandlerEnum_QueryInterface,
632     MetadataHandlerEnum_AddRef,
633     MetadataHandlerEnum_Release,
634     MetadataHandlerEnum_Next,
635     MetadataHandlerEnum_Skip,
636     MetadataHandlerEnum_Reset,
637     MetadataHandlerEnum_Clone
638 };
639
640 static HRESULT MetadataHandlerEnum_Create(MetadataHandler *parent, DWORD index,
641     IWICEnumMetadataItem **ppIEnumMetadataItem)
642 {
643     MetadataHandlerEnum *This;
644
645     if (!ppIEnumMetadataItem) return E_INVALIDARG;
646
647     *ppIEnumMetadataItem = NULL;
648
649     This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandlerEnum));
650     if (!This) return E_OUTOFMEMORY;
651
652     IWICMetadataWriter_AddRef(&parent->IWICMetadataWriter_iface);
653
654     This->IWICEnumMetadataItem_iface.lpVtbl = &MetadataHandlerEnum_Vtbl;
655     This->ref = 1;
656     This->parent = parent;
657     This->index = index;
658
659     *ppIEnumMetadataItem = &This->IWICEnumMetadataItem_iface;
660
661     return S_OK;
662 }
663
664 static HRESULT LoadUnknownMetadata(IStream *input, const GUID *preferred_vendor,
665     DWORD persist_options, MetadataItem **items, DWORD *item_count)
666 {
667     HRESULT hr;
668     MetadataItem *result;
669     STATSTG stat;
670     BYTE *data;
671     ULONG bytesread;
672
673     TRACE("\n");
674
675     hr = IStream_Stat(input, &stat, STATFLAG_NONAME);
676     if (FAILED(hr))
677         return hr;
678
679     data = HeapAlloc(GetProcessHeap(), 0, stat.cbSize.QuadPart);
680     if (!data) return E_OUTOFMEMORY;
681
682     hr = IStream_Read(input, data, stat.cbSize.QuadPart, &bytesread);
683     if (FAILED(hr))
684     {
685         HeapFree(GetProcessHeap(), 0, data);
686         return hr;
687     }
688
689     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem));
690     if (!result)
691     {
692         HeapFree(GetProcessHeap(), 0, data);
693         return E_OUTOFMEMORY;
694     }
695
696     PropVariantInit(&result[0].schema);
697     PropVariantInit(&result[0].id);
698     PropVariantInit(&result[0].value);
699
700     result[0].value.vt = VT_BLOB;
701     result[0].value.u.blob.cbSize = bytesread;
702     result[0].value.u.blob.pBlobData = data;
703
704     *items = result;
705     *item_count = 1;
706
707     return S_OK;
708 }
709
710 static const MetadataHandlerVtbl UnknownMetadataReader_Vtbl = {
711     0,
712     &CLSID_WICUnknownMetadataReader,
713     LoadUnknownMetadata
714 };
715
716 HRESULT UnknownMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
717 {
718     return MetadataReader_Create(&UnknownMetadataReader_Vtbl, pUnkOuter, iid, ppv);
719 }
720
721 #define SWAP_USHORT(x) do { if (!native_byte_order) (x) = RtlUshortByteSwap(x); } while(0)
722 #define SWAP_ULONG(x) do { if (!native_byte_order) (x) = RtlUlongByteSwap(x); } while(0)
723 #define SWAP_ULONGLONG(x) do { if (!native_byte_order) (x) = RtlUlonglongByteSwap(x); } while(0)
724
725 struct IFD_entry
726 {
727     SHORT id;
728     SHORT type;
729     ULONG count;
730     LONG  value;
731 };
732
733 #define IFD_BYTE 1
734 #define IFD_ASCII 2
735 #define IFD_SHORT 3
736 #define IFD_LONG 4
737 #define IFD_RATIONAL 5
738 #define IFD_SBYTE 6
739 #define IFD_UNDEFINED 7
740 #define IFD_SSHORT 8
741 #define IFD_SLONG 9
742 #define IFD_SRATIONAL 10
743 #define IFD_FLOAT 11
744 #define IFD_DOUBLE 12
745 #define IFD_IFD 13
746
747 static int tag_to_vt(SHORT tag)
748 {
749     static const int tag2vt[] =
750     {
751         VT_EMPTY, /* 0 */
752         VT_UI1,   /* IFD_BYTE 1 */
753         VT_LPSTR, /* IFD_ASCII 2 */
754         VT_UI2,   /* IFD_SHORT 3 */
755         VT_UI4,   /* IFD_LONG 4 */
756         VT_UI8,   /* IFD_RATIONAL 5 */
757         VT_I1,    /* IFD_SBYTE 6 */
758         VT_BLOB,  /* IFD_UNDEFINED 7 */
759         VT_I2,    /* IFD_SSHORT 8 */
760         VT_I4,    /* IFD_SLONG 9 */
761         VT_I8,    /* IFD_SRATIONAL 10 */
762         VT_R4,    /* IFD_FLOAT 11 */
763         VT_R8,    /* IFD_DOUBLE 12 */
764         VT_BLOB,  /* IFD_IFD 13 */
765     };
766     return (tag > 0 && tag <= 13) ? tag2vt[tag] : VT_BLOB;
767 }
768
769 static HRESULT load_IFD_entry(IStream *input, const struct IFD_entry *entry,
770                               MetadataItem *item, BOOL native_byte_order)
771 {
772     ULONG count, value, i;
773     SHORT type;
774     LARGE_INTEGER pos;
775     HRESULT hr;
776
777     item->schema.vt = VT_EMPTY;
778     item->id.vt = VT_UI2;
779     item->id.u.uiVal = entry->id;
780     SWAP_USHORT(item->id.u.uiVal);
781
782     count = entry->count;
783     SWAP_ULONG(count);
784     type = entry->type;
785     SWAP_USHORT(type);
786     item->value.vt = tag_to_vt(type);
787     value = entry->value;
788     SWAP_ULONG(value);
789
790     switch (type)
791     {
792      case IFD_BYTE:
793      case IFD_SBYTE:
794         if (!count) count = 1;
795
796         if (count <= 4)
797         {
798             const BYTE *data = (const BYTE *)&entry->value;
799
800             if (count == 1)
801                 item->value.u.bVal = data[0];
802             else
803             {
804                 item->value.vt |= VT_VECTOR;
805                 item->value.u.caub.cElems = count;
806                 item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count);
807                 memcpy(item->value.u.caub.pElems, data, count);
808             }
809             break;
810         }
811
812         item->value.vt |= VT_VECTOR;
813         item->value.u.caub.cElems = count;
814         item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count);
815         if (!item->value.u.caub.pElems) return E_OUTOFMEMORY;
816
817         pos.QuadPart = value;
818         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
819         if (FAILED(hr))
820         {
821             HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems);
822             return hr;
823         }
824         hr = IStream_Read(input, item->value.u.caub.pElems, count, NULL);
825         if (FAILED(hr))
826         {
827             HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems);
828             return hr;
829         }
830         break;
831     case IFD_SHORT:
832     case IFD_SSHORT:
833         if (!count) count = 1;
834
835         if (count <= 2)
836         {
837             const SHORT *data = (const SHORT *)&entry->value;
838
839             if (count == 1)
840             {
841                 item->value.u.uiVal = data[0];
842                 SWAP_USHORT(item->value.u.uiVal);
843             }
844             else
845             {
846                 item->value.vt |= VT_VECTOR;
847                 item->value.u.caui.cElems = count;
848                 item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2);
849                 memcpy(item->value.u.caui.pElems, data, count * 2);
850                 for (i = 0; i < count; i++)
851                     SWAP_USHORT(item->value.u.caui.pElems[i]);
852             }
853             break;
854         }
855
856         item->value.vt |= VT_VECTOR;
857         item->value.u.caui.cElems = count;
858         item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2);
859         if (!item->value.u.caui.pElems) return E_OUTOFMEMORY;
860
861         pos.QuadPart = value;
862         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
863         if (FAILED(hr))
864         {
865             HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems);
866             return hr;
867         }
868         hr = IStream_Read(input, item->value.u.caui.pElems, count * 2, NULL);
869         if (FAILED(hr))
870         {
871             HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems);
872             return hr;
873         }
874         for (i = 0; i < count; i++)
875             SWAP_USHORT(item->value.u.caui.pElems[i]);
876         break;
877     case IFD_LONG:
878     case IFD_SLONG:
879     case IFD_FLOAT:
880         if (!count) count = 1;
881
882         if (count == 1)
883         {
884             item->value.u.ulVal = value;
885             break;
886         }
887
888         item->value.vt |= VT_VECTOR;
889         item->value.u.caul.cElems = count;
890         item->value.u.caul.pElems = HeapAlloc(GetProcessHeap(), 0, count * 4);
891         if (!item->value.u.caul.pElems) return E_OUTOFMEMORY;
892
893         pos.QuadPart = value;
894         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
895         if (FAILED(hr))
896         {
897             HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems);
898             return hr;
899         }
900         hr = IStream_Read(input, item->value.u.caul.pElems, count * 4, NULL);
901         if (FAILED(hr))
902         {
903             HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems);
904             return hr;
905         }
906         for (i = 0; i < count; i++)
907             SWAP_ULONG(item->value.u.caul.pElems[i]);
908         break;
909     case IFD_RATIONAL:
910     case IFD_SRATIONAL:
911     case IFD_DOUBLE:
912         if (!count)
913         {
914             FIXME("IFD field type %d, count 0\n", type);
915             item->value.vt = VT_EMPTY;
916             break;
917         }
918
919         if (count == 1)
920         {
921             ULONGLONG ull;
922
923             pos.QuadPart = value;
924             hr = IStream_Seek(input, pos, SEEK_SET, NULL);
925             if (FAILED(hr)) return hr;
926
927             hr = IStream_Read(input, &ull, sizeof(ull), NULL);
928             if (FAILED(hr)) return hr;
929
930             item->value.u.uhVal.QuadPart = ull;
931
932             if (type == IFD_DOUBLE)
933                 SWAP_ULONGLONG(item->value.u.uhVal.QuadPart);
934             else
935             {
936                 SWAP_ULONG(item->value.u.uhVal.u.LowPart);
937                 SWAP_ULONG(item->value.u.uhVal.u.HighPart);
938             }
939             break;
940         }
941         else
942         {
943             item->value.vt |= VT_VECTOR;
944             item->value.u.cauh.cElems = count;
945             item->value.u.cauh.pElems = HeapAlloc(GetProcessHeap(), 0, count * 8);
946             if (!item->value.u.cauh.pElems) return E_OUTOFMEMORY;
947
948             pos.QuadPart = value;
949             hr = IStream_Seek(input, pos, SEEK_SET, NULL);
950             if (FAILED(hr))
951             {
952                 HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems);
953                 return hr;
954             }
955             hr = IStream_Read(input, item->value.u.cauh.pElems, count * 8, NULL);
956             if (FAILED(hr))
957             {
958                 HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems);
959                 return hr;
960             }
961             for (i = 0; i < count; i++)
962             {
963                 if (type == IFD_DOUBLE)
964                     SWAP_ULONGLONG(item->value.u.cauh.pElems[i].QuadPart);
965                 else
966                 {
967                     SWAP_ULONG(item->value.u.cauh.pElems[i].u.LowPart);
968                     SWAP_ULONG(item->value.u.cauh.pElems[i].u.HighPart);
969                 }
970             }
971         }
972         break;
973     case IFD_ASCII:
974         item->value.u.pszVal = HeapAlloc(GetProcessHeap(), 0, count + 1);
975         if (!item->value.u.pszVal) return E_OUTOFMEMORY;
976
977         if (count <= 4)
978         {
979             const char *data = (const char *)&entry->value;
980             memcpy(item->value.u.pszVal, data, count);
981             item->value.u.pszVal[count] = 0;
982             break;
983         }
984
985         pos.QuadPart = value;
986         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
987         if (FAILED(hr))
988         {
989             HeapFree(GetProcessHeap(), 0, item->value.u.pszVal);
990             return hr;
991         }
992         hr = IStream_Read(input, item->value.u.pszVal, count, NULL);
993         if (FAILED(hr))
994         {
995             HeapFree(GetProcessHeap(), 0, item->value.u.pszVal);
996             return hr;
997         }
998         item->value.u.pszVal[count] = 0;
999         break;
1000     case IFD_UNDEFINED:
1001         if (!count)
1002         {
1003             FIXME("IFD field type %d, count 0\n", type);
1004             item->value.vt = VT_EMPTY;
1005             break;
1006         }
1007
1008         item->value.u.blob.pBlobData = HeapAlloc(GetProcessHeap(), 0, count);
1009         if (!item->value.u.blob.pBlobData) return E_OUTOFMEMORY;
1010
1011         item->value.u.blob.cbSize = count;
1012
1013         if (count <= 4)
1014         {
1015             const char *data = (const char *)&entry->value;
1016             memcpy(item->value.u.blob.pBlobData, data, count);
1017             break;
1018         }
1019
1020         pos.QuadPart = value;
1021         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
1022         if (FAILED(hr))
1023         {
1024             HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData);
1025             return hr;
1026         }
1027         hr = IStream_Read(input, item->value.u.blob.pBlobData, count, NULL);
1028         if (FAILED(hr))
1029         {
1030             HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData);
1031             return hr;
1032         }
1033         break;
1034     default:
1035         FIXME("loading field of type %d, count %u is not implemented\n", type, count);
1036         break;
1037     }
1038     return S_OK;
1039 }
1040
1041 static HRESULT LoadIfdMetadata(IStream *input, const GUID *preferred_vendor,
1042     DWORD persist_options, MetadataItem **items, DWORD *item_count)
1043 {
1044     HRESULT hr;
1045     MetadataItem *result;
1046     USHORT count, i;
1047     struct IFD_entry *entry;
1048     BOOL native_byte_order = TRUE;
1049
1050     TRACE("\n");
1051
1052 #ifdef WORDS_BIGENDIAN
1053     if (persist_options & WICPersistOptionsLittleEndian)
1054 #else
1055     if (persist_options & WICPersistOptionsBigEndian)
1056 #endif
1057         native_byte_order = FALSE;
1058
1059     hr = IStream_Read(input, &count, sizeof(count), NULL);
1060     if (FAILED(hr)) return hr;
1061
1062     SWAP_USHORT(count);
1063
1064     entry = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*entry));
1065     if (!entry) return E_OUTOFMEMORY;
1066
1067     hr = IStream_Read(input, entry, count * sizeof(*entry), NULL);
1068     if (FAILED(hr))
1069     {
1070         HeapFree(GetProcessHeap(), 0, entry);
1071         return hr;
1072     }
1073
1074     /* limit number of IFDs to 4096 to avoid infinite loop */
1075     for (i = 0; i < 4096; i++)
1076     {
1077         ULONG next_ifd_offset;
1078         LARGE_INTEGER pos;
1079         USHORT next_ifd_count;
1080
1081         hr = IStream_Read(input, &next_ifd_offset, sizeof(next_ifd_offset), NULL);
1082         if (FAILED(hr)) break;
1083
1084         SWAP_ULONG(next_ifd_offset);
1085         if (!next_ifd_offset) break;
1086
1087         pos.QuadPart = next_ifd_offset;
1088         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
1089         if (FAILED(hr)) break;
1090
1091         hr = IStream_Read(input, &next_ifd_count, sizeof(next_ifd_count), NULL);
1092         if (FAILED(hr)) break;
1093
1094         SWAP_USHORT(next_ifd_count);
1095
1096         pos.QuadPart = next_ifd_count * sizeof(*entry);
1097         hr = IStream_Seek(input, pos, SEEK_CUR, NULL);
1098         if (FAILED(hr)) break;
1099     }
1100
1101     if (FAILED(hr) || i == 4096)
1102     {
1103         HeapFree(GetProcessHeap(), 0, entry);
1104         return WINCODEC_ERR_BADMETADATAHEADER;
1105     }
1106
1107     result = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*result));
1108     if (!result)
1109     {
1110         HeapFree(GetProcessHeap(), 0, entry);
1111         return E_OUTOFMEMORY;
1112     }
1113
1114     for (i = 0; i < count; i++)
1115     {
1116         hr = load_IFD_entry(input, &entry[i], &result[i], native_byte_order);
1117         if (FAILED(hr))
1118         {
1119             HeapFree(GetProcessHeap(), 0, entry);
1120             HeapFree(GetProcessHeap(), 0, result);
1121             return hr;
1122         }
1123     }
1124
1125     HeapFree(GetProcessHeap(), 0, entry);
1126
1127     *items = result;
1128     *item_count = count;
1129
1130     return S_OK;
1131 }
1132
1133 static const MetadataHandlerVtbl IfdMetadataReader_Vtbl = {
1134     0,
1135     &CLSID_WICIfdMetadataReader,
1136     LoadIfdMetadata
1137 };
1138
1139 HRESULT IfdMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
1140 {
1141     return MetadataReader_Create(&IfdMetadataReader_Vtbl, pUnkOuter, iid, ppv);
1142 }