mshtml: Keep reference in node returned from get_node.
[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 HRESULT WINAPI MetadataHandler_GetValue(IWICMetadataWriter *iface,
217     const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, PROPVARIANT *pvarValue)
218 {
219     FIXME("(%p,%p,%p,%p): stub\n", iface, pvarSchema, pvarId, pvarValue);
220     return E_NOTIMPL;
221 }
222
223 static HRESULT WINAPI MetadataHandler_GetEnumerator(IWICMetadataWriter *iface,
224     IWICEnumMetadataItem **ppIEnumMetadata)
225 {
226     MetadataHandler *This = impl_from_IWICMetadataWriter(iface);
227     TRACE("(%p,%p)\n", iface, ppIEnumMetadata);
228     return MetadataHandlerEnum_Create(This, 0, ppIEnumMetadata);
229 }
230
231 static HRESULT WINAPI MetadataHandler_SetValue(IWICMetadataWriter *iface,
232     const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, const PROPVARIANT *pvarValue)
233 {
234     FIXME("(%p,%p,%p,%p): stub\n", iface, pvarSchema, pvarId, pvarValue);
235     return E_NOTIMPL;
236 }
237
238 static HRESULT WINAPI MetadataHandler_SetValueByIndex(IWICMetadataWriter *iface,
239     UINT nIndex, const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, const PROPVARIANT *pvarValue)
240 {
241     FIXME("(%p,%u,%p,%p,%p): stub\n", iface, nIndex, pvarSchema, pvarId, pvarValue);
242     return E_NOTIMPL;
243 }
244
245 static HRESULT WINAPI MetadataHandler_RemoveValue(IWICMetadataWriter *iface,
246     const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId)
247 {
248     FIXME("(%p,%p,%p): stub\n", iface, pvarSchema, pvarId);
249     return E_NOTIMPL;
250 }
251
252 static HRESULT WINAPI MetadataHandler_RemoveValueByIndex(IWICMetadataWriter *iface,
253     UINT nIndex)
254 {
255     FIXME("(%p,%u): stub\n", iface, nIndex);
256     return E_NOTIMPL;
257 }
258
259 static const IWICMetadataWriterVtbl MetadataHandler_Vtbl = {
260     MetadataHandler_QueryInterface,
261     MetadataHandler_AddRef,
262     MetadataHandler_Release,
263     MetadataHandler_GetMetadataFormat,
264     MetadataHandler_GetMetadataHandlerInfo,
265     MetadataHandler_GetCount,
266     MetadataHandler_GetValueByIndex,
267     MetadataHandler_GetValue,
268     MetadataHandler_GetEnumerator,
269     MetadataHandler_SetValue,
270     MetadataHandler_SetValueByIndex,
271     MetadataHandler_RemoveValue,
272     MetadataHandler_RemoveValueByIndex
273 };
274
275 static HRESULT WINAPI MetadataHandler_PersistStream_QueryInterface(IWICPersistStream *iface,
276     REFIID iid, void **ppv)
277 {
278     MetadataHandler *This = impl_from_IWICPersistStream(iface);
279     return IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv);
280 }
281
282 static ULONG WINAPI MetadataHandler_PersistStream_AddRef(IWICPersistStream *iface)
283 {
284     MetadataHandler *This = impl_from_IWICPersistStream(iface);
285     return IWICMetadataWriter_AddRef(&This->IWICMetadataWriter_iface);
286 }
287
288 static ULONG WINAPI MetadataHandler_PersistStream_Release(IWICPersistStream *iface)
289 {
290     MetadataHandler *This = impl_from_IWICPersistStream(iface);
291     return IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface);
292 }
293
294 static HRESULT WINAPI MetadataHandler_GetClassID(IWICPersistStream *iface,
295     CLSID *pClassID)
296 {
297     FIXME("(%p,%p): stub\n", iface, pClassID);
298     return E_NOTIMPL;
299 }
300
301 static HRESULT WINAPI MetadataHandler_IsDirty(IWICPersistStream *iface)
302 {
303     FIXME("(%p): stub\n", iface);
304     return E_NOTIMPL;
305 }
306
307 static HRESULT WINAPI MetadataHandler_Load(IWICPersistStream *iface,
308     IStream *pStm)
309 {
310     FIXME("(%p,%p): stub\n", iface, pStm);
311     return E_NOTIMPL;
312 }
313
314 static HRESULT WINAPI MetadataHandler_Save(IWICPersistStream *iface,
315     IStream *pStm, BOOL fClearDirty)
316 {
317     FIXME("(%p,%p,%i): stub\n", iface, pStm, fClearDirty);
318     return E_NOTIMPL;
319 }
320
321 static HRESULT WINAPI MetadataHandler_GetSizeMax(IWICPersistStream *iface,
322     ULARGE_INTEGER *pcbSize)
323 {
324     FIXME("(%p,%p): stub\n", iface, pcbSize);
325     return E_NOTIMPL;
326 }
327
328 static HRESULT WINAPI MetadataHandler_LoadEx(IWICPersistStream *iface,
329     IStream *pIStream, const GUID *pguidPreferredVendor, DWORD dwPersistOptions)
330 {
331     MetadataHandler *This = impl_from_IWICPersistStream(iface);
332     HRESULT hr;
333     MetadataItem *new_items=NULL;
334     DWORD item_count=0;
335
336     TRACE("(%p,%p,%s,%x)\n", iface, pIStream, debugstr_guid(pguidPreferredVendor), dwPersistOptions);
337
338     EnterCriticalSection(&This->lock);
339
340     hr = This->vtable->fnLoad(pIStream, pguidPreferredVendor, dwPersistOptions,
341         &new_items, &item_count);
342
343     if (SUCCEEDED(hr))
344     {
345         MetadataHandler_FreeItems(This);
346         This->items = new_items;
347         This->item_count = item_count;
348     }
349
350     LeaveCriticalSection(&This->lock);
351
352     return hr;
353 }
354
355 static HRESULT WINAPI MetadataHandler_SaveEx(IWICPersistStream *iface,
356     IStream *pIStream, DWORD dwPersistOptions, BOOL fClearDirty)
357 {
358     FIXME("(%p,%p,%x,%i): stub\n", iface, pIStream, dwPersistOptions, fClearDirty);
359     return E_NOTIMPL;
360 }
361
362 static const IWICPersistStreamVtbl MetadataHandler_PersistStream_Vtbl = {
363     MetadataHandler_PersistStream_QueryInterface,
364     MetadataHandler_PersistStream_AddRef,
365     MetadataHandler_PersistStream_Release,
366     MetadataHandler_GetClassID,
367     MetadataHandler_IsDirty,
368     MetadataHandler_Load,
369     MetadataHandler_Save,
370     MetadataHandler_GetSizeMax,
371     MetadataHandler_LoadEx,
372     MetadataHandler_SaveEx
373 };
374
375 static HRESULT MetadataReader_Create(const MetadataHandlerVtbl *vtable, IUnknown *pUnkOuter, REFIID iid, void** ppv)
376 {
377     MetadataHandler *This;
378     HRESULT hr;
379
380     TRACE("%s\n", debugstr_guid(vtable->clsid));
381
382     *ppv = NULL;
383
384     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
385
386     This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandler));
387     if (!This) return E_OUTOFMEMORY;
388
389     This->IWICMetadataWriter_iface.lpVtbl = &MetadataHandler_Vtbl;
390     This->IWICPersistStream_iface.lpVtbl = &MetadataHandler_PersistStream_Vtbl;
391     This->ref = 1;
392     This->vtable = vtable;
393     This->items = NULL;
394     This->item_count = 0;
395
396     InitializeCriticalSection(&This->lock);
397     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MetadataHandler.lock");
398
399     hr = IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv);
400
401     IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface);
402
403     return hr;
404 }
405
406 typedef struct MetadataHandlerEnum {
407     IWICEnumMetadataItem IWICEnumMetadataItem_iface;
408     LONG ref;
409     MetadataHandler *parent;
410     DWORD index;
411 } MetadataHandlerEnum;
412
413 static inline MetadataHandlerEnum *impl_from_IWICEnumMetadataItem(IWICEnumMetadataItem *iface)
414 {
415     return CONTAINING_RECORD(iface, MetadataHandlerEnum, IWICEnumMetadataItem_iface);
416 }
417
418 static HRESULT WINAPI MetadataHandlerEnum_QueryInterface(IWICEnumMetadataItem *iface, REFIID iid,
419     void **ppv)
420 {
421     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
422     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
423
424     if (!ppv) return E_INVALIDARG;
425
426     if (IsEqualIID(&IID_IUnknown, iid) ||
427         IsEqualIID(&IID_IWICEnumMetadataItem, iid))
428     {
429         *ppv = &This->IWICEnumMetadataItem_iface;
430     }
431     else
432     {
433         *ppv = NULL;
434         return E_NOINTERFACE;
435     }
436
437     IUnknown_AddRef((IUnknown*)*ppv);
438     return S_OK;
439 }
440
441 static ULONG WINAPI MetadataHandlerEnum_AddRef(IWICEnumMetadataItem *iface)
442 {
443     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
444     ULONG ref = InterlockedIncrement(&This->ref);
445
446     TRACE("(%p) refcount=%u\n", iface, ref);
447
448     return ref;
449 }
450
451 static ULONG WINAPI MetadataHandlerEnum_Release(IWICEnumMetadataItem *iface)
452 {
453     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
454     ULONG ref = InterlockedDecrement(&This->ref);
455
456     TRACE("(%p) refcount=%u\n", iface, ref);
457
458     if (ref == 0)
459     {
460         IWICMetadataWriter_Release(&This->parent->IWICMetadataWriter_iface);
461         HeapFree(GetProcessHeap(), 0, This);
462     }
463
464     return ref;
465 }
466
467 static HRESULT WINAPI MetadataHandlerEnum_Next(IWICEnumMetadataItem *iface,
468     ULONG celt, PROPVARIANT *rgeltSchema, PROPVARIANT *rgeltId,
469     PROPVARIANT *rgeltValue, ULONG *pceltFetched)
470 {
471     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
472     ULONG new_index;
473     HRESULT hr=S_FALSE;
474     int i;
475
476     TRACE("(%p,%i)\n", iface, celt);
477
478     EnterCriticalSection(&This->parent->lock);
479
480     if (This->index >= This->parent->item_count)
481     {
482         *pceltFetched = 0;
483         LeaveCriticalSection(&This->parent->lock);
484         return S_FALSE;
485     }
486
487     new_index = min(This->parent->item_count, This->index + celt);
488     *pceltFetched = new_index - This->index;
489
490     if (rgeltSchema)
491     {
492         for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
493             hr = PropVariantCopy(&rgeltSchema[i], &This->parent->items[i+This->index].schema);
494     }
495
496     for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
497         hr = PropVariantCopy(&rgeltId[i], &This->parent->items[i+This->index].id);
498
499     if (rgeltValue)
500     {
501         for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++)
502             hr = PropVariantCopy(&rgeltValue[i], &This->parent->items[i+This->index].value);
503     }
504
505     if (SUCCEEDED(hr))
506     {
507         This->index = new_index;
508     }
509
510     LeaveCriticalSection(&This->parent->lock);
511
512     return hr;
513 }
514
515 static HRESULT WINAPI MetadataHandlerEnum_Skip(IWICEnumMetadataItem *iface,
516     ULONG celt)
517 {
518     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
519
520     EnterCriticalSection(&This->parent->lock);
521
522     This->index += celt;
523
524     LeaveCriticalSection(&This->parent->lock);
525
526     return S_OK;
527 }
528
529 static HRESULT WINAPI MetadataHandlerEnum_Reset(IWICEnumMetadataItem *iface)
530 {
531     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
532
533     EnterCriticalSection(&This->parent->lock);
534
535     This->index = 0;
536
537     LeaveCriticalSection(&This->parent->lock);
538
539     return S_OK;
540 }
541
542 static HRESULT WINAPI MetadataHandlerEnum_Clone(IWICEnumMetadataItem *iface,
543     IWICEnumMetadataItem **ppIEnumMetadataItem)
544 {
545     MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface);
546     HRESULT hr;
547
548     EnterCriticalSection(&This->parent->lock);
549
550     hr = MetadataHandlerEnum_Create(This->parent, This->index, ppIEnumMetadataItem);
551
552     LeaveCriticalSection(&This->parent->lock);
553
554     return hr;
555 }
556
557 static const IWICEnumMetadataItemVtbl MetadataHandlerEnum_Vtbl = {
558     MetadataHandlerEnum_QueryInterface,
559     MetadataHandlerEnum_AddRef,
560     MetadataHandlerEnum_Release,
561     MetadataHandlerEnum_Next,
562     MetadataHandlerEnum_Skip,
563     MetadataHandlerEnum_Reset,
564     MetadataHandlerEnum_Clone
565 };
566
567 static HRESULT MetadataHandlerEnum_Create(MetadataHandler *parent, DWORD index,
568     IWICEnumMetadataItem **ppIEnumMetadataItem)
569 {
570     MetadataHandlerEnum *This;
571
572     if (!ppIEnumMetadataItem) return E_INVALIDARG;
573
574     *ppIEnumMetadataItem = NULL;
575
576     This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandlerEnum));
577     if (!This) return E_OUTOFMEMORY;
578
579     IWICMetadataWriter_AddRef(&parent->IWICMetadataWriter_iface);
580
581     This->IWICEnumMetadataItem_iface.lpVtbl = &MetadataHandlerEnum_Vtbl;
582     This->ref = 1;
583     This->parent = parent;
584     This->index = index;
585
586     *ppIEnumMetadataItem = &This->IWICEnumMetadataItem_iface;
587
588     return S_OK;
589 }
590
591 static HRESULT LoadUnknownMetadata(IStream *input, const GUID *preferred_vendor,
592     DWORD persist_options, MetadataItem **items, DWORD *item_count)
593 {
594     HRESULT hr;
595     MetadataItem *result;
596     STATSTG stat;
597     BYTE *data;
598     ULONG bytesread;
599
600     TRACE("\n");
601
602     hr = IStream_Stat(input, &stat, STATFLAG_NONAME);
603     if (FAILED(hr))
604         return hr;
605
606     data = HeapAlloc(GetProcessHeap(), 0, stat.cbSize.QuadPart);
607     if (!data) return E_OUTOFMEMORY;
608
609     hr = IStream_Read(input, data, stat.cbSize.QuadPart, &bytesread);
610     if (FAILED(hr))
611     {
612         HeapFree(GetProcessHeap(), 0, data);
613         return hr;
614     }
615
616     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem));
617     if (!result)
618     {
619         HeapFree(GetProcessHeap(), 0, data);
620         return E_OUTOFMEMORY;
621     }
622
623     PropVariantInit(&result[0].schema);
624     PropVariantInit(&result[0].id);
625     PropVariantInit(&result[0].value);
626
627     result[0].value.vt = VT_BLOB;
628     result[0].value.u.blob.cbSize = bytesread;
629     result[0].value.u.blob.pBlobData = data;
630
631     *items = result;
632     *item_count = 1;
633
634     return S_OK;
635 }
636
637 static const MetadataHandlerVtbl UnknownMetadataReader_Vtbl = {
638     0,
639     &CLSID_WICUnknownMetadataReader,
640     LoadUnknownMetadata
641 };
642
643 HRESULT UnknownMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
644 {
645     return MetadataReader_Create(&UnknownMetadataReader_Vtbl, pUnkOuter, iid, ppv);
646 }
647
648 #define SWAP_USHORT(x) do { if (!native_byte_order) (x) = RtlUshortByteSwap(x); } while(0)
649 #define SWAP_ULONG(x) do { if (!native_byte_order) (x) = RtlUlongByteSwap(x); } while(0)
650 #define SWAP_ULONGLONG(x) do { if (!native_byte_order) (x) = RtlUlonglongByteSwap(x); } while(0)
651
652 struct IFD_entry
653 {
654     SHORT id;
655     SHORT type;
656     ULONG count;
657     LONG  value;
658 };
659
660 #define IFD_BYTE 1
661 #define IFD_ASCII 2
662 #define IFD_SHORT 3
663 #define IFD_LONG 4
664 #define IFD_RATIONAL 5
665 #define IFD_SBYTE 6
666 #define IFD_UNDEFINED 7
667 #define IFD_SSHORT 8
668 #define IFD_SLONG 9
669 #define IFD_SRATIONAL 10
670 #define IFD_FLOAT 11
671 #define IFD_DOUBLE 12
672 #define IFD_IFD 13
673
674 static int tag_to_vt(SHORT tag)
675 {
676     static const int tag2vt[] =
677     {
678         VT_EMPTY, /* 0 */
679         VT_UI1,   /* IFD_BYTE 1 */
680         VT_LPSTR, /* IFD_ASCII 2 */
681         VT_UI2,   /* IFD_SHORT 3 */
682         VT_UI4,   /* IFD_LONG 4 */
683         VT_UI8,   /* IFD_RATIONAL 5 */
684         VT_I1,    /* IFD_SBYTE 6 */
685         VT_BLOB,  /* IFD_UNDEFINED 7 */
686         VT_I2,    /* IFD_SSHORT 8 */
687         VT_I4,    /* IFD_SLONG 9 */
688         VT_I8,    /* IFD_SRATIONAL 10 */
689         VT_R4,    /* IFD_FLOAT 11 */
690         VT_R8,    /* IFD_DOUBLE 12 */
691         VT_BLOB,  /* IFD_IFD 13 */
692     };
693     return (tag > 0 && tag <= 13) ? tag2vt[tag] : VT_BLOB;
694 }
695
696 static HRESULT load_IFD_entry(IStream *input, const struct IFD_entry *entry,
697                               MetadataItem *item, BOOL native_byte_order)
698 {
699     ULONG count, value, i;
700     SHORT type;
701     LARGE_INTEGER pos;
702     HRESULT hr;
703
704     item->schema.vt = VT_EMPTY;
705     item->id.vt = VT_UI2;
706     item->id.u.uiVal = entry->id;
707     SWAP_USHORT(item->id.u.uiVal);
708
709     count = entry->count;
710     SWAP_ULONG(count);
711     type = entry->type;
712     SWAP_USHORT(type);
713     item->value.vt = tag_to_vt(type);
714     value = entry->value;
715     SWAP_ULONG(value);
716
717     switch (type)
718     {
719      case IFD_BYTE:
720      case IFD_SBYTE:
721         if (!count) count = 1;
722
723         if (count <= 4)
724         {
725             const BYTE *data = (const BYTE *)&entry->value;
726
727             if (count == 1)
728                 item->value.u.bVal = data[0];
729             else
730             {
731                 item->value.vt |= VT_VECTOR;
732                 item->value.u.caub.cElems = count;
733                 item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count);
734                 memcpy(item->value.u.caub.pElems, data, count);
735             }
736             break;
737         }
738
739         item->value.vt |= VT_VECTOR;
740         item->value.u.caub.cElems = count;
741         item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count);
742         if (!item->value.u.caub.pElems) return E_OUTOFMEMORY;
743
744         pos.QuadPart = value;
745         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
746         if (FAILED(hr))
747         {
748             HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems);
749             return hr;
750         }
751         hr = IStream_Read(input, item->value.u.caub.pElems, count, NULL);
752         if (FAILED(hr))
753         {
754             HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems);
755             return hr;
756         }
757         break;
758     case IFD_SHORT:
759     case IFD_SSHORT:
760         if (!count) count = 1;
761
762         if (count <= 2)
763         {
764             const SHORT *data = (const SHORT *)&entry->value;
765
766             if (count == 1)
767             {
768                 item->value.u.uiVal = data[0];
769                 SWAP_USHORT(item->value.u.uiVal);
770             }
771             else
772             {
773                 item->value.vt |= VT_VECTOR;
774                 item->value.u.caui.cElems = count;
775                 item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2);
776                 memcpy(item->value.u.caui.pElems, data, count * 2);
777                 for (i = 0; i < count; i++)
778                     SWAP_USHORT(item->value.u.caui.pElems[i]);
779             }
780             break;
781         }
782
783         item->value.vt |= VT_VECTOR;
784         item->value.u.caui.cElems = count;
785         item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2);
786         if (!item->value.u.caui.pElems) return E_OUTOFMEMORY;
787
788         pos.QuadPart = value;
789         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
790         if (FAILED(hr))
791         {
792             HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems);
793             return hr;
794         }
795         hr = IStream_Read(input, item->value.u.caui.pElems, count * 2, NULL);
796         if (FAILED(hr))
797         {
798             HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems);
799             return hr;
800         }
801         for (i = 0; i < count; i++)
802             SWAP_USHORT(item->value.u.caui.pElems[i]);
803         break;
804     case IFD_LONG:
805     case IFD_SLONG:
806     case IFD_FLOAT:
807         if (!count) count = 1;
808
809         if (count == 1)
810         {
811             item->value.u.ulVal = value;
812             break;
813         }
814
815         item->value.vt |= VT_VECTOR;
816         item->value.u.caul.cElems = count;
817         item->value.u.caul.pElems = HeapAlloc(GetProcessHeap(), 0, count * 4);
818         if (!item->value.u.caul.pElems) return E_OUTOFMEMORY;
819
820         pos.QuadPart = value;
821         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
822         if (FAILED(hr))
823         {
824             HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems);
825             return hr;
826         }
827         hr = IStream_Read(input, item->value.u.caul.pElems, count * 4, NULL);
828         if (FAILED(hr))
829         {
830             HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems);
831             return hr;
832         }
833         for (i = 0; i < count; i++)
834             SWAP_ULONG(item->value.u.caul.pElems[i]);
835         break;
836     case IFD_RATIONAL:
837     case IFD_SRATIONAL:
838     case IFD_DOUBLE:
839         if (!count)
840         {
841             FIXME("IFD field type %d, count 0\n", type);
842             item->value.vt = VT_EMPTY;
843             break;
844         }
845
846         if (count == 1)
847         {
848             ULONGLONG ull;
849
850             pos.QuadPart = value;
851             hr = IStream_Seek(input, pos, SEEK_SET, NULL);
852             if (FAILED(hr)) return hr;
853
854             hr = IStream_Read(input, &ull, sizeof(ull), NULL);
855             if (FAILED(hr)) return hr;
856
857             item->value.u.uhVal.QuadPart = ull;
858
859             if (type == IFD_DOUBLE)
860                 SWAP_ULONGLONG(item->value.u.uhVal.QuadPart);
861             else
862             {
863                 SWAP_ULONG(item->value.u.uhVal.u.LowPart);
864                 SWAP_ULONG(item->value.u.uhVal.u.HighPart);
865             }
866             break;
867         }
868         else
869         {
870             item->value.vt |= VT_VECTOR;
871             item->value.u.cauh.cElems = count;
872             item->value.u.cauh.pElems = HeapAlloc(GetProcessHeap(), 0, count * 8);
873             if (!item->value.u.cauh.pElems) return E_OUTOFMEMORY;
874
875             pos.QuadPart = value;
876             hr = IStream_Seek(input, pos, SEEK_SET, NULL);
877             if (FAILED(hr))
878             {
879                 HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems);
880                 return hr;
881             }
882             hr = IStream_Read(input, item->value.u.cauh.pElems, count * 8, NULL);
883             if (FAILED(hr))
884             {
885                 HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems);
886                 return hr;
887             }
888             for (i = 0; i < count; i++)
889             {
890                 if (type == IFD_DOUBLE)
891                     SWAP_ULONGLONG(item->value.u.cauh.pElems[i].QuadPart);
892                 else
893                 {
894                     SWAP_ULONG(item->value.u.cauh.pElems[i].u.LowPart);
895                     SWAP_ULONG(item->value.u.cauh.pElems[i].u.HighPart);
896                 }
897             }
898         }
899         break;
900     case IFD_ASCII:
901         item->value.u.pszVal = HeapAlloc(GetProcessHeap(), 0, count + 1);
902         if (!item->value.u.pszVal) return E_OUTOFMEMORY;
903
904         if (count <= 4)
905         {
906             const char *data = (const char *)&entry->value;
907             memcpy(item->value.u.pszVal, data, count);
908             item->value.u.pszVal[count] = 0;
909             break;
910         }
911
912         pos.QuadPart = value;
913         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
914         if (FAILED(hr))
915         {
916             HeapFree(GetProcessHeap(), 0, item->value.u.pszVal);
917             return hr;
918         }
919         hr = IStream_Read(input, item->value.u.pszVal, count, NULL);
920         if (FAILED(hr))
921         {
922             HeapFree(GetProcessHeap(), 0, item->value.u.pszVal);
923             return hr;
924         }
925         item->value.u.pszVal[count] = 0;
926         break;
927     case IFD_UNDEFINED:
928         if (!count)
929         {
930             FIXME("IFD field type %d, count 0\n", type);
931             item->value.vt = VT_EMPTY;
932             break;
933         }
934
935         item->value.u.blob.pBlobData = HeapAlloc(GetProcessHeap(), 0, count);
936         if (!item->value.u.blob.pBlobData) return E_OUTOFMEMORY;
937
938         item->value.u.blob.cbSize = count;
939
940         if (count <= 4)
941         {
942             const char *data = (const char *)&entry->value;
943             memcpy(item->value.u.blob.pBlobData, data, count);
944             break;
945         }
946
947         pos.QuadPart = value;
948         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
949         if (FAILED(hr))
950         {
951             HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData);
952             return hr;
953         }
954         hr = IStream_Read(input, item->value.u.blob.pBlobData, count, NULL);
955         if (FAILED(hr))
956         {
957             HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData);
958             return hr;
959         }
960         break;
961     default:
962         FIXME("loading field of type %d, count %u is not implemented\n", type, count);
963         break;
964     }
965     return S_OK;
966 }
967
968 static HRESULT LoadIfdMetadata(IStream *input, const GUID *preferred_vendor,
969     DWORD persist_options, MetadataItem **items, DWORD *item_count)
970 {
971     HRESULT hr;
972     MetadataItem *result;
973     USHORT count, i;
974     struct IFD_entry *entry;
975     BOOL native_byte_order = TRUE;
976
977     TRACE("\n");
978
979 #ifdef WORDS_BIGENDIAN
980     if (persist_options & WICPersistOptionsLittleEndian)
981 #else
982     if (persist_options & WICPersistOptionsBigEndian)
983 #endif
984         native_byte_order = FALSE;
985
986     hr = IStream_Read(input, &count, sizeof(count), NULL);
987     if (FAILED(hr)) return hr;
988
989     SWAP_USHORT(count);
990
991     entry = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*entry));
992     if (!entry) return E_OUTOFMEMORY;
993
994     hr = IStream_Read(input, entry, count * sizeof(*entry), NULL);
995     if (FAILED(hr))
996     {
997         HeapFree(GetProcessHeap(), 0, entry);
998         return hr;
999     }
1000
1001     /* limit number of IFDs to 4096 to avoid infinite loop */
1002     for (i = 0; i < 4096; i++)
1003     {
1004         ULONG next_ifd_offset;
1005         LARGE_INTEGER pos;
1006         USHORT next_ifd_count;
1007
1008         hr = IStream_Read(input, &next_ifd_offset, sizeof(next_ifd_offset), NULL);
1009         if (FAILED(hr)) break;
1010
1011         SWAP_ULONG(next_ifd_offset);
1012         if (!next_ifd_offset) break;
1013
1014         pos.QuadPart = next_ifd_offset;
1015         hr = IStream_Seek(input, pos, SEEK_SET, NULL);
1016         if (FAILED(hr)) break;
1017
1018         hr = IStream_Read(input, &next_ifd_count, sizeof(next_ifd_count), NULL);
1019         if (FAILED(hr)) break;
1020
1021         SWAP_USHORT(next_ifd_count);
1022
1023         pos.QuadPart = next_ifd_count * sizeof(*entry);
1024         hr = IStream_Seek(input, pos, SEEK_CUR, NULL);
1025         if (FAILED(hr)) break;
1026     }
1027
1028     if (FAILED(hr) || i == 4096)
1029     {
1030         HeapFree(GetProcessHeap(), 0, entry);
1031         return WINCODEC_ERR_BADMETADATAHEADER;
1032     }
1033
1034     result = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*result));
1035     if (!result)
1036     {
1037         HeapFree(GetProcessHeap(), 0, entry);
1038         return E_OUTOFMEMORY;
1039     }
1040
1041     for (i = 0; i < count; i++)
1042     {
1043         hr = load_IFD_entry(input, &entry[i], &result[i], native_byte_order);
1044         if (FAILED(hr))
1045         {
1046             HeapFree(GetProcessHeap(), 0, entry);
1047             HeapFree(GetProcessHeap(), 0, result);
1048             return hr;
1049         }
1050     }
1051
1052     HeapFree(GetProcessHeap(), 0, entry);
1053
1054     *items = result;
1055     *item_count = count;
1056
1057     return S_OK;
1058 }
1059
1060 static const MetadataHandlerVtbl IfdMetadataReader_Vtbl = {
1061     0,
1062     &CLSID_WICIfdMetadataReader,
1063     LoadIfdMetadata
1064 };
1065
1066 HRESULT IfdMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv)
1067 {
1068     return MetadataReader_Create(&IfdMetadataReader_Vtbl, pUnkOuter, iid, ppv);
1069 }