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