windowscodecs: Do not assume that vtable is the first element of the object, avoid...
[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     MetadataHandler *This = impl_from_IWICPersistStream(iface);
384     TRACE("(%p,%p)\n", iface, pStm);
385     return IWICPersistStream_LoadEx(&This->IWICPersistStream_iface, pStm, NULL, WICPersistOptionsDefault);
386 }
387
388 static HRESULT WINAPI MetadataHandler_Save(IWICPersistStream *iface,
389     IStream *pStm, BOOL fClearDirty)
390 {
391     FIXME("(%p,%p,%i): stub\n", iface, pStm, fClearDirty);
392     return E_NOTIMPL;
393 }
394
395 static HRESULT WINAPI MetadataHandler_GetSizeMax(IWICPersistStream *iface,
396     ULARGE_INTEGER *pcbSize)
397 {
398     FIXME("(%p,%p): stub\n", iface, pcbSize);
399     return E_NOTIMPL;
400 }
401
402 static HRESULT WINAPI MetadataHandler_LoadEx(IWICPersistStream *iface,
403     IStream *pIStream, const GUID *pguidPreferredVendor, DWORD dwPersistOptions)
404 {
405     MetadataHandler *This = impl_from_IWICPersistStream(iface);
406     HRESULT hr;
407     MetadataItem *new_items=NULL;
408     DWORD item_count=0;
409
410     TRACE("(%p,%p,%s,%x)\n", iface, pIStream, debugstr_guid(pguidPreferredVendor), dwPersistOptions);
411
412     EnterCriticalSection(&This->lock);
413
414     hr = This->vtable->fnLoad(pIStream, pguidPreferredVendor, dwPersistOptions,
415         &new_items, &item_count);
416
417     if (SUCCEEDED(hr))
418     {
419         MetadataHandler_FreeItems(This);
420         This->items = new_items;
421         This->item_count = item_count;
422     }
423
424     LeaveCriticalSection(&This->lock);
425
426     return hr;
427 }
428
429 static HRESULT WINAPI MetadataHandler_SaveEx(IWICPersistStream *iface,
430     IStream *pIStream, DWORD dwPersistOptions, BOOL fClearDirty)
431 {
432     FIXME("(%p,%p,%x,%i): stub\n", iface, pIStream, dwPersistOptions, fClearDirty);
433     return E_NOTIMPL;
434 }
435
436 static const IWICPersistStreamVtbl MetadataHandler_PersistStream_Vtbl = {
437     MetadataHandler_PersistStream_QueryInterface,
438     MetadataHandler_PersistStream_AddRef,
439     MetadataHandler_PersistStream_Release,
440     MetadataHandler_GetClassID,
441     MetadataHandler_IsDirty,
442     MetadataHandler_Load,
443     MetadataHandler_Save,
444     MetadataHandler_GetSizeMax,
445     MetadataHandler_LoadEx,
446     MetadataHandler_SaveEx
447 };
448
449 static HRESULT MetadataReader_Create(const MetadataHandlerVtbl *vtable, IUnknown *pUnkOuter, REFIID iid, void** ppv)
450 {
451     MetadataHandler *This;
452     HRESULT hr;
453
454     TRACE("%s\n", debugstr_guid(vtable->clsid));
455
456     *ppv = NULL;
457
458     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
459
460     This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandler));
461     if (!This) return E_OUTOFMEMORY;
462
463     This->IWICMetadataWriter_iface.lpVtbl = &MetadataHandler_Vtbl;
464     This->IWICPersistStream_iface.lpVtbl = &MetadataHandler_PersistStream_Vtbl;
465     This->ref = 1;
466     This->vtable = vtable;
467     This->items = NULL;
468     This->item_count = 0;
469
470     InitializeCriticalSection(&This->lock);
471     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MetadataHandler.lock");
472
473     hr = IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv);
474
475     IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface);
476
477     return hr;
478 }
479
480 typedef struct MetadataHandlerEnum {
481     IWICEnumMetadataItem IWICEnumMetadataItem_iface;
482     LONG ref;
483     MetadataHandler *parent;
484     DWORD index;
485 } MetadataHandlerEnum;
486
487 static inline MetadataHandlerEnum *impl_from_IWICEnumMetadataItem(IWICEnumMetadataItem *iface)
488 {
489     return CONTAINING_RECORD(iface, MetadataHandlerEnum, IWICEnumMetadataItem_iface);
490 }
491
492 static HRESULT WINAPI MetadataHandlerEnum_QueryInterface(IWICEnumMetadataItem *iface, REFIID iid,
493     void **ppv)
494 {
495     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
496     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
497
498     if (!ppv) return E_INVALIDARG;
499
500     if (IsEqualIID(&IID_IUnknown, iid) ||
501         IsEqualIID(&IID_IWICEnumMetadataItem, iid))
502     {
503         *ppv = &This->IWICEnumMetadataItem_iface;
504     }
505     else
506     {
507         *ppv = NULL;
508         return E_NOINTERFACE;
509     }
510
511     IUnknown_AddRef((IUnknown*)*ppv);
512     return S_OK;
513 }
514
515 static ULONG WINAPI MetadataHandlerEnum_AddRef(IWICEnumMetadataItem *iface)
516 {
517     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
518     ULONG ref = InterlockedIncrement(&This->ref);
519
520     TRACE("(%p) refcount=%u\n", iface, ref);
521
522     return ref;
523 }
524
525 static ULONG WINAPI MetadataHandlerEnum_Release(IWICEnumMetadataItem *iface)
526 {
527     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
528     ULONG ref = InterlockedDecrement(&This->ref);
529
530     TRACE("(%p) refcount=%u\n", iface, ref);
531
532     if (ref == 0)
533     {
534         IWICMetadataWriter_Release(&This->parent->IWICMetadataWriter_iface);
535         HeapFree(GetProcessHeap(), 0, This);
536     }
537
538     return ref;
539 }
540
541 static HRESULT WINAPI MetadataHandlerEnum_Next(IWICEnumMetadataItem *iface,
542     ULONG celt, PROPVARIANT *rgeltSchema, PROPVARIANT *rgeltId,
543     PROPVARIANT *rgeltValue, ULONG *pceltFetched)
544 {
545     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
546     ULONG new_index;
547     HRESULT hr=S_FALSE;
548     int i;
549
550     TRACE("(%p,%i)\n", iface, celt);
551
552     EnterCriticalSection(&This->parent->lock);
553
554     if (This->index >= This->parent->item_count)
555     {
556         *pceltFetched = 0;
557         LeaveCriticalSection(&This->parent->lock);
558         return S_FALSE;
559     }
560
561     new_index = min(This->parent->item_count, This->index + celt);
562     *pceltFetched = new_index - This->index;
563
564     if (rgeltSchema)
565     {
566         for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
567             hr = PropVariantCopy(&rgeltSchema[i], &This->parent->items[i+This->index].schema);
568     }
569
570     for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
571         hr = PropVariantCopy(&rgeltId[i], &This->parent->items[i+This->index].id);
572
573     if (rgeltValue)
574     {
575         for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
576             hr = PropVariantCopy(&rgeltValue[i], &This->parent->items[i+This->index].value);
577     }
578
579     if (SUCCEEDED(hr))
580     {
581         This->index = new_index;
582     }
583
584     LeaveCriticalSection(&This->parent->lock);
585
586     return hr;
587 }
588
589 static HRESULT WINAPI MetadataHandlerEnum_Skip(IWICEnumMetadataItem *iface,
590     ULONG celt)
591 {
592     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
593
594     EnterCriticalSection(&This->parent->lock);
595
596     This->index += celt;
597
598     LeaveCriticalSection(&This->parent->lock);
599
600     return S_OK;
601 }
602
603 static HRESULT WINAPI MetadataHandlerEnum_Reset(IWICEnumMetadataItem *iface)
604 {
605     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
606
607     EnterCriticalSection(&This->parent->lock);
608
609     This->index = 0;
610
611     LeaveCriticalSection(&This->parent->lock);
612
613     return S_OK;
614 }
615
616 static HRESULT WINAPI MetadataHandlerEnum_Clone(IWICEnumMetadataItem *iface,
617     IWICEnumMetadataItem **ppIEnumMetadataItem)
618 {
619     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
620     HRESULT hr;
621
622     EnterCriticalSection(&This->parent->lock);
623
624     hr = MetadataHandlerEnum_Create(This->parent, This->index, ppIEnumMetadataItem);
625
626     LeaveCriticalSection(&This->parent->lock);
627
628     return hr;
629 }
630
631 static const IWICEnumMetadataItemVtbl MetadataHandlerEnum_Vtbl = {
632     MetadataHandlerEnum_QueryInterface,
633     MetadataHandlerEnum_AddRef,
634     MetadataHandlerEnum_Release,
635     MetadataHandlerEnum_Next,
636     MetadataHandlerEnum_Skip,
637     MetadataHandlerEnum_Reset,
638     MetadataHandlerEnum_Clone
639 };
640
641 static HRESULT MetadataHandlerEnum_Create(MetadataHandler *parent, DWORD index,
642     IWICEnumMetadataItem **ppIEnumMetadataItem)
643 {
644     MetadataHandlerEnum *This;
645
646     if (!ppIEnumMetadataItem) return E_INVALIDARG;
647
648     *ppIEnumMetadataItem = NULL;
649
650     This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandlerEnum));
651     if (!This) return E_OUTOFMEMORY;
652
653     IWICMetadataWriter_AddRef(&parent->IWICMetadataWriter_iface);
654
655     This->IWICEnumMetadataItem_iface.lpVtbl = &MetadataHandlerEnum_Vtbl;
656     This->ref = 1;
657     This->parent = parent;
658     This->index = index;
659
660     *ppIEnumMetadataItem = &This->IWICEnumMetadataItem_iface;
661
662     return S_OK;
663 }
664
665 static HRESULT LoadUnknownMetadata(IStream *input, const GUID *preferred_vendor,
666     DWORD persist_options, MetadataItem **items, DWORD *item_count)
667 {
668     HRESULT hr;
669     MetadataItem *result;
670     STATSTG stat;
671     BYTE *data;
672     ULONG bytesread;
673
674     TRACE("\n");
675
676     hr = IStream_Stat(input, &stat, STATFLAG_NONAME);
677     if (FAILED(hr))
678         return hr;
679
680     data = HeapAlloc(GetProcessHeap(), 0, stat.cbSize.QuadPart);
681     if (!data) return E_OUTOFMEMORY;
682
683     hr = IStream_Read(input, data, stat.cbSize.QuadPart, &bytesread);
684     if (FAILED(hr))
685     {
686         HeapFree(GetProcessHeap(), 0, data);
687         return hr;
688     }
689
690     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem));
691     if (!result)
692     {
693         HeapFree(GetProcessHeap(), 0, data);
694         return E_OUTOFMEMORY;
695     }
696
697     PropVariantInit(&result[0].schema);
698     PropVariantInit(&result[0].id);
699     PropVariantInit(&result[0].value);
700
701     result[0].value.vt = VT_BLOB;
702     result[0].value.u.blob.cbSize = bytesread;
703     result[0].value.u.blob.pBlobData = data;
704
705     *items = result;
706     *item_count = 1;
707
708     return S_OK;
709 }
710
711 static const MetadataHandlerVtbl UnknownMetadataReader_Vtbl = {
712     0,
713     &CLSID_WICUnknownMetadataReader,
714     LoadUnknownMetadata
715 };
716
717 HRESULT UnknownMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
718 {
719     return MetadataReader_Create(&UnknownMetadataReader_Vtbl, pUnkOuter, iid, ppv);
720 }
721
722 #define SWAP_USHORT(x) do { if (!native_byte_order) (x) = RtlUshortByteSwap(x); } while(0)
723 #define SWAP_ULONG(x) do { if (!native_byte_order) (x) = RtlUlongByteSwap(x); } while(0)
724 #define SWAP_ULONGLONG(x) do { if (!native_byte_order) (x) = RtlUlonglongByteSwap(x); } while(0)
725
726 struct IFD_entry
727 {
728     SHORT id;
729     SHORT type;
730     ULONG count;
731     LONG  value;
732 };
733
734 #define IFD_BYTE 1
735 #define IFD_ASCII 2
736 #define IFD_SHORT 3
737 #define IFD_LONG 4
738 #define IFD_RATIONAL 5
739 #define IFD_SBYTE 6
740 #define IFD_UNDEFINED 7
741 #define IFD_SSHORT 8
742 #define IFD_SLONG 9
743 #define IFD_SRATIONAL 10
744 #define IFD_FLOAT 11
745 #define IFD_DOUBLE 12
746 #define IFD_IFD 13
747
748 static int tag_to_vt(SHORT tag)
749 {
750     static const int tag2vt[] =
751     {
752         VT_EMPTY, /* 0 */
753         VT_UI1,   /* IFD_BYTE 1 */
754         VT_LPSTR, /* IFD_ASCII 2 */
755         VT_UI2,   /* IFD_SHORT 3 */
756         VT_UI4,   /* IFD_LONG 4 */
757         VT_UI8,   /* IFD_RATIONAL 5 */
758         VT_I1,    /* IFD_SBYTE 6 */
759         VT_BLOB,  /* IFD_UNDEFINED 7 */
760         VT_I2,    /* IFD_SSHORT 8 */
761         VT_I4,    /* IFD_SLONG 9 */
762         VT_I8,    /* IFD_SRATIONAL 10 */
763         VT_R4,    /* IFD_FLOAT 11 */
764         VT_R8,    /* IFD_DOUBLE 12 */
765         VT_BLOB,  /* IFD_IFD 13 */
766     };
767     return (tag > 0 && tag <= 13) ? tag2vt[tag] : VT_BLOB;
768 }
769
770 static HRESULT load_IFD_entry(IStream *input, const struct IFD_entry *entry,
771                               MetadataItem *item, BOOL native_byte_order)
772 {
773     ULONG count, value, i;
774     SHORT type;
775     LARGE_INTEGER pos;
776     HRESULT hr;
777
778     item->schema.vt = VT_EMPTY;
779     item->id.vt = VT_UI2;
780     item->id.u.uiVal = entry->id;
781     SWAP_USHORT(item->id.u.uiVal);
782
783     count = entry->count;
784     SWAP_ULONG(count);
785     type = entry->type;
786     SWAP_USHORT(type);
787     item->value.vt = tag_to_vt(type);
788     value = entry->value;
789     SWAP_ULONG(value);
790
791     switch (type)
792     {
793      case IFD_BYTE:
794      case IFD_SBYTE:
795         if (!count) count = 1;
796
797         if (count <= 4)
798         {
799             const BYTE *data = (const BYTE *)&entry->value;
800
801             if (count == 1)
802                 item->value.u.bVal = data[0];
803             else
804             {
805                 item->value.vt |= VT_VECTOR;
806                 item->value.u.caub.cElems = count;
807                 item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count);
808                 memcpy(item->value.u.caub.pElems, data, count);
809             }
810             break;
811         }
812
813         item->value.vt |= VT_VECTOR;
814         item->value.u.caub.cElems = count;
815         item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count);
816         if (!item->value.u.caub.pElems) return E_OUTOFMEMORY;
817
818         pos.QuadPart = value;
819         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
820         if (FAILED(hr))
821         {
822             HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems);
823             return hr;
824         }
825         hr = IStream_Read(input, item->value.u.caub.pElems, count, NULL);
826         if (FAILED(hr))
827         {
828             HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems);
829             return hr;
830         }
831         break;
832     case IFD_SHORT:
833     case IFD_SSHORT:
834         if (!count) count = 1;
835
836         if (count <= 2)
837         {
838             const SHORT *data = (const SHORT *)&entry->value;
839
840             if (count == 1)
841             {
842                 item->value.u.uiVal = data[0];
843                 SWAP_USHORT(item->value.u.uiVal);
844             }
845             else
846             {
847                 item->value.vt |= VT_VECTOR;
848                 item->value.u.caui.cElems = count;
849                 item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2);
850                 memcpy(item->value.u.caui.pElems, data, count * 2);
851                 for (i = 0; i < count; i++)
852                     SWAP_USHORT(item->value.u.caui.pElems[i]);
853             }
854             break;
855         }
856
857         item->value.vt |= VT_VECTOR;
858         item->value.u.caui.cElems = count;
859         item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2);
860         if (!item->value.u.caui.pElems) return E_OUTOFMEMORY;
861
862         pos.QuadPart = value;
863         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
864         if (FAILED(hr))
865         {
866             HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems);
867             return hr;
868         }
869         hr = IStream_Read(input, item->value.u.caui.pElems, count * 2, NULL);
870         if (FAILED(hr))
871         {
872             HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems);
873             return hr;
874         }
875         for (i = 0; i < count; i++)
876             SWAP_USHORT(item->value.u.caui.pElems[i]);
877         break;
878     case IFD_LONG:
879     case IFD_SLONG:
880     case IFD_FLOAT:
881         if (!count) count = 1;
882
883         if (count == 1)
884         {
885             item->value.u.ulVal = value;
886             break;
887         }
888
889         item->value.vt |= VT_VECTOR;
890         item->value.u.caul.cElems = count;
891         item->value.u.caul.pElems = HeapAlloc(GetProcessHeap(), 0, count * 4);
892         if (!item->value.u.caul.pElems) return E_OUTOFMEMORY;
893
894         pos.QuadPart = value;
895         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
896         if (FAILED(hr))
897         {
898             HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems);
899             return hr;
900         }
901         hr = IStream_Read(input, item->value.u.caul.pElems, count * 4, NULL);
902         if (FAILED(hr))
903         {
904             HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems);
905             return hr;
906         }
907         for (i = 0; i < count; i++)
908             SWAP_ULONG(item->value.u.caul.pElems[i]);
909         break;
910     case IFD_RATIONAL:
911     case IFD_SRATIONAL:
912     case IFD_DOUBLE:
913         if (!count)
914         {
915             FIXME("IFD field type %d, count 0\n", type);
916             item->value.vt = VT_EMPTY;
917             break;
918         }
919
920         if (count == 1)
921         {
922             ULONGLONG ull;
923
924             pos.QuadPart = value;
925             hr = IStream_Seek(input, pos, SEEK_SET, NULL);
926             if (FAILED(hr)) return hr;
927
928             hr = IStream_Read(input, &ull, sizeof(ull), NULL);
929             if (FAILED(hr)) return hr;
930
931             item->value.u.uhVal.QuadPart = ull;
932
933             if (type == IFD_DOUBLE)
934                 SWAP_ULONGLONG(item->value.u.uhVal.QuadPart);
935             else
936             {
937                 SWAP_ULONG(item->value.u.uhVal.u.LowPart);
938                 SWAP_ULONG(item->value.u.uhVal.u.HighPart);
939             }
940             break;
941         }
942         else
943         {
944             item->value.vt |= VT_VECTOR;
945             item->value.u.cauh.cElems = count;
946             item->value.u.cauh.pElems = HeapAlloc(GetProcessHeap(), 0, count * 8);
947             if (!item->value.u.cauh.pElems) return E_OUTOFMEMORY;
948
949             pos.QuadPart = value;
950             hr = IStream_Seek(input, pos, SEEK_SET, NULL);
951             if (FAILED(hr))
952             {
953                 HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems);
954                 return hr;
955             }
956             hr = IStream_Read(input, item->value.u.cauh.pElems, count * 8, NULL);
957             if (FAILED(hr))
958             {
959                 HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems);
960                 return hr;
961             }
962             for (i = 0; i < count; i++)
963             {
964                 if (type == IFD_DOUBLE)
965                     SWAP_ULONGLONG(item->value.u.cauh.pElems[i].QuadPart);
966                 else
967                 {
968                     SWAP_ULONG(item->value.u.cauh.pElems[i].u.LowPart);
969                     SWAP_ULONG(item->value.u.cauh.pElems[i].u.HighPart);
970                 }
971             }
972         }
973         break;
974     case IFD_ASCII:
975         item->value.u.pszVal = HeapAlloc(GetProcessHeap(), 0, count + 1);
976         if (!item->value.u.pszVal) return E_OUTOFMEMORY;
977
978         if (count <= 4)
979         {
980             const char *data = (const char *)&entry->value;
981             memcpy(item->value.u.pszVal, data, count);
982             item->value.u.pszVal[count] = 0;
983             break;
984         }
985
986         pos.QuadPart = value;
987         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
988         if (FAILED(hr))
989         {
990             HeapFree(GetProcessHeap(), 0, item->value.u.pszVal);
991             return hr;
992         }
993         hr = IStream_Read(input, item->value.u.pszVal, count, NULL);
994         if (FAILED(hr))
995         {
996             HeapFree(GetProcessHeap(), 0, item->value.u.pszVal);
997             return hr;
998         }
999         item->value.u.pszVal[count] = 0;
1000         break;
1001     case IFD_UNDEFINED:
1002         if (!count)
1003         {
1004             FIXME("IFD field type %d, count 0\n", type);
1005             item->value.vt = VT_EMPTY;
1006             break;
1007         }
1008
1009         item->value.u.blob.pBlobData = HeapAlloc(GetProcessHeap(), 0, count);
1010         if (!item->value.u.blob.pBlobData) return E_OUTOFMEMORY;
1011
1012         item->value.u.blob.cbSize = count;
1013
1014         if (count <= 4)
1015         {
1016             const char *data = (const char *)&entry->value;
1017             memcpy(item->value.u.blob.pBlobData, data, count);
1018             break;
1019         }
1020
1021         pos.QuadPart = value;
1022         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
1023         if (FAILED(hr))
1024         {
1025             HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData);
1026             return hr;
1027         }
1028         hr = IStream_Read(input, item->value.u.blob.pBlobData, count, NULL);
1029         if (FAILED(hr))
1030         {
1031             HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData);
1032             return hr;
1033         }
1034         break;
1035     default:
1036         FIXME("loading field of type %d, count %u is not implemented\n", type, count);
1037         break;
1038     }
1039     return S_OK;
1040 }
1041
1042 static HRESULT LoadIfdMetadata(IStream *input, const GUID *preferred_vendor,
1043     DWORD persist_options, MetadataItem **items, DWORD *item_count)
1044 {
1045     HRESULT hr;
1046     MetadataItem *result;
1047     USHORT count, i;
1048     struct IFD_entry *entry;
1049     BOOL native_byte_order = TRUE;
1050
1051     TRACE("\n");
1052
1053 #ifdef WORDS_BIGENDIAN
1054     if (persist_options & WICPersistOptionsLittleEndian)
1055 #else
1056     if (persist_options & WICPersistOptionsBigEndian)
1057 #endif
1058         native_byte_order = FALSE;
1059
1060     hr = IStream_Read(input, &count, sizeof(count), NULL);
1061     if (FAILED(hr)) return hr;
1062
1063     SWAP_USHORT(count);
1064
1065     entry = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*entry));
1066     if (!entry) return E_OUTOFMEMORY;
1067
1068     hr = IStream_Read(input, entry, count * sizeof(*entry), NULL);
1069     if (FAILED(hr))
1070     {
1071         HeapFree(GetProcessHeap(), 0, entry);
1072         return hr;
1073     }
1074
1075     /* limit number of IFDs to 4096 to avoid infinite loop */
1076     for (i = 0; i < 4096; i++)
1077     {
1078         ULONG next_ifd_offset;
1079         LARGE_INTEGER pos;
1080         USHORT next_ifd_count;
1081
1082         hr = IStream_Read(input, &next_ifd_offset, sizeof(next_ifd_offset), NULL);
1083         if (FAILED(hr)) break;
1084
1085         SWAP_ULONG(next_ifd_offset);
1086         if (!next_ifd_offset) break;
1087
1088         pos.QuadPart = next_ifd_offset;
1089         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
1090         if (FAILED(hr)) break;
1091
1092         hr = IStream_Read(input, &next_ifd_count, sizeof(next_ifd_count), NULL);
1093         if (FAILED(hr)) break;
1094
1095         SWAP_USHORT(next_ifd_count);
1096
1097         pos.QuadPart = next_ifd_count * sizeof(*entry);
1098         hr = IStream_Seek(input, pos, SEEK_CUR, NULL);
1099         if (FAILED(hr)) break;
1100     }
1101
1102     if (FAILED(hr) || i == 4096)
1103     {
1104         HeapFree(GetProcessHeap(), 0, entry);
1105         return WINCODEC_ERR_BADMETADATAHEADER;
1106     }
1107
1108     result = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*result));
1109     if (!result)
1110     {
1111         HeapFree(GetProcessHeap(), 0, entry);
1112         return E_OUTOFMEMORY;
1113     }
1114
1115     for (i = 0; i < count; i++)
1116     {
1117         hr = load_IFD_entry(input, &entry[i], &result[i], native_byte_order);
1118         if (FAILED(hr))
1119         {
1120             HeapFree(GetProcessHeap(), 0, entry);
1121             HeapFree(GetProcessHeap(), 0, result);
1122             return hr;
1123         }
1124     }
1125
1126     HeapFree(GetProcessHeap(), 0, entry);
1127
1128     *items = result;
1129     *item_count = count;
1130
1131     return S_OK;
1132 }
1133
1134 static const MetadataHandlerVtbl IfdMetadataReader_Vtbl = {
1135     0,
1136     &CLSID_WICIfdMetadataReader,
1137     LoadIfdMetadata
1138 };
1139
1140 HRESULT IfdMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
1141 {
1142     return MetadataReader_Create(&IfdMetadataReader_Vtbl, pUnkOuter, iid, ppv);
1143 }