/* * Copyright 2012 Vincent Povirk for CodeWeavers * Copyright 2012 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include <stdarg.h> #include <stdio.h> #define COBJMACROS #define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "winternl.h" #include "objbase.h" #include "wincodec.h" #include "wincodecsdk.h" #include "wincodecs_private.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); typedef struct MetadataHandler { IWICMetadataWriter IWICMetadataWriter_iface; LONG ref; IWICPersistStream IWICPersistStream_iface; const MetadataHandlerVtbl *vtable; MetadataItem *items; DWORD item_count; CRITICAL_SECTION lock; } MetadataHandler; static inline MetadataHandler *impl_from_IWICMetadataWriter(IWICMetadataWriter *iface) { return CONTAINING_RECORD(iface, MetadataHandler, IWICMetadataWriter_iface); } static inline MetadataHandler *impl_from_IWICPersistStream(IWICPersistStream *iface) { return CONTAINING_RECORD(iface, MetadataHandler, IWICPersistStream_iface); } static void MetadataHandler_FreeItems(MetadataHandler *This) { int i; for (i=0; i<This->item_count; i++) { PropVariantClear(&This->items[i].schema); PropVariantClear(&This->items[i].id); PropVariantClear(&This->items[i].value); } HeapFree(GetProcessHeap(), 0, This->items); } static HRESULT MetadataHandlerEnum_Create(MetadataHandler *parent, DWORD index, IWICEnumMetadataItem **ppIEnumMetadataItem); static HRESULT WINAPI MetadataHandler_QueryInterface(IWICMetadataWriter *iface, REFIID iid, void **ppv) { MetadataHandler *This = impl_from_IWICMetadataWriter(iface); TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICMetadataReader, iid) || (IsEqualIID(&IID_IWICMetadataWriter, iid) && This->vtable->is_writer)) { *ppv = &This->IWICMetadataWriter_iface; } else if (IsEqualIID(&IID_IPersist, iid) || IsEqualIID(&IID_IPersistStream, iid) || IsEqualIID(&IID_IWICPersistStream, iid)) { *ppv = &This->IWICPersistStream_iface; } else { *ppv = NULL; return E_NOINTERFACE; } IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } static ULONG WINAPI MetadataHandler_AddRef(IWICMetadataWriter *iface) { MetadataHandler *This = impl_from_IWICMetadataWriter(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); return ref; } static ULONG WINAPI MetadataHandler_Release(IWICMetadataWriter *iface) { MetadataHandler *This = impl_from_IWICMetadataWriter(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); if (ref == 0) { MetadataHandler_FreeItems(This); This->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->lock); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI MetadataHandler_GetMetadataHandlerInfo(IWICMetadataWriter *iface, IWICMetadataHandlerInfo **ppIHandler) { HRESULT hr; IWICComponentInfo *component_info; MetadataHandler *This = impl_from_IWICMetadataWriter(iface); TRACE("%p,%p\n", iface, ppIHandler); hr = CreateComponentInfo(This->vtable->clsid, &component_info); if (FAILED(hr)) return hr; hr = IWICComponentInfo_QueryInterface(component_info, &IID_IWICMetadataHandlerInfo, (void **)ppIHandler); IWICComponentInfo_Release(component_info); return hr; } static HRESULT WINAPI MetadataHandler_GetMetadataFormat(IWICMetadataWriter *iface, GUID *pguidMetadataFormat) { HRESULT hr; IWICMetadataHandlerInfo *metadata_info; TRACE("%p,%p\n", iface, pguidMetadataFormat); if (!pguidMetadataFormat) return E_INVALIDARG; hr = MetadataHandler_GetMetadataHandlerInfo(iface, &metadata_info); if (FAILED(hr)) return hr; hr = IWICMetadataHandlerInfo_GetMetadataFormat(metadata_info, pguidMetadataFormat); IWICMetadataHandlerInfo_Release(metadata_info); return hr; } static HRESULT WINAPI MetadataHandler_GetCount(IWICMetadataWriter *iface, UINT *pcCount) { MetadataHandler *This = impl_from_IWICMetadataWriter(iface); TRACE("%p,%p\n", iface, pcCount); if (!pcCount) return E_INVALIDARG; *pcCount = This->item_count; return S_OK; } static HRESULT WINAPI MetadataHandler_GetValueByIndex(IWICMetadataWriter *iface, UINT index, PROPVARIANT *schema, PROPVARIANT *id, PROPVARIANT *value) { HRESULT hr = S_OK; MetadataHandler *This = impl_from_IWICMetadataWriter(iface); TRACE("%p,%u,%p,%p,%p\n", iface, index, schema, id, value); EnterCriticalSection(&This->lock); if (index >= This->item_count) { LeaveCriticalSection(&This->lock); return E_INVALIDARG; } if (schema) hr = PropVariantCopy(schema, &This->items[index].schema); if (SUCCEEDED(hr) && id) hr = PropVariantCopy(id, &This->items[index].id); if (SUCCEEDED(hr) && value) hr = PropVariantCopy(value, &This->items[index].value); LeaveCriticalSection(&This->lock); return hr; } static BOOL get_int_value(const PROPVARIANT *pv, LONGLONG *value) { switch (pv->vt) { case VT_NULL: case VT_EMPTY: *value = 0; break; case VT_I1: *value = pv->u.cVal; break; case VT_UI1: *value = pv->u.bVal; break; case VT_I2: *value = pv->u.iVal; break; case VT_UI2: *value = pv->u.uiVal; break; case VT_I4: *value = pv->u.lVal; break; case VT_UI4: *value = pv->u.ulVal; break; case VT_I8: case VT_UI8: *value = pv->u.hVal.QuadPart; break; default: FIXME("not supported variant type %d\n", pv->vt); return FALSE; } return TRUE; } /* FiXME: Use propsys.PropVariantCompareEx once it's implemented */ static int propvar_cmp(const PROPVARIANT *v1, const PROPVARIANT *v2) { LONGLONG value1, value2; if (v1->vt == VT_LPSTR && v2->vt == VT_LPSTR) { return lstrcmpA(v1->u.pszVal, v2->u.pszVal); } if (v1->vt == VT_LPWSTR && v2->vt == VT_LPWSTR) { return lstrcmpiW(v1->u.pwszVal, v2->u.pwszVal); } if (!get_int_value(v1, &value1)) return -1; if (!get_int_value(v2, &value2)) return -1; value1 -= value2; if (value1) return value1 < 0 ? -1 : 1; return 0; } static HRESULT WINAPI MetadataHandler_GetValue(IWICMetadataWriter *iface, const PROPVARIANT *schema, const PROPVARIANT *id, PROPVARIANT *value) { UINT i; HRESULT hr = WINCODEC_ERR_PROPERTYNOTFOUND; MetadataHandler *This = impl_from_IWICMetadataWriter(iface); TRACE("(%p,%p,%p,%p)\n", iface, schema, id, value); if (!id) return E_INVALIDARG; EnterCriticalSection(&This->lock); for (i = 0; i < This->item_count; i++) { if (schema && This->items[i].schema.vt != VT_EMPTY) { if (propvar_cmp(schema, &This->items[i].schema) != 0) continue; } if (propvar_cmp(id, &This->items[i].id) != 0) continue; hr = value ? PropVariantCopy(value, &This->items[i].value) : S_OK; break; } LeaveCriticalSection(&This->lock); return hr; } static HRESULT WINAPI MetadataHandler_GetEnumerator(IWICMetadataWriter *iface, IWICEnumMetadataItem **ppIEnumMetadata) { MetadataHandler *This = impl_from_IWICMetadataWriter(iface); TRACE("(%p,%p)\n", iface, ppIEnumMetadata); return MetadataHandlerEnum_Create(This, 0, ppIEnumMetadata); } static HRESULT WINAPI MetadataHandler_SetValue(IWICMetadataWriter *iface, const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, const PROPVARIANT *pvarValue) { FIXME("(%p,%p,%p,%p): stub\n", iface, pvarSchema, pvarId, pvarValue); return E_NOTIMPL; } static HRESULT WINAPI MetadataHandler_SetValueByIndex(IWICMetadataWriter *iface, UINT nIndex, const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId, const PROPVARIANT *pvarValue) { FIXME("(%p,%u,%p,%p,%p): stub\n", iface, nIndex, pvarSchema, pvarId, pvarValue); return E_NOTIMPL; } static HRESULT WINAPI MetadataHandler_RemoveValue(IWICMetadataWriter *iface, const PROPVARIANT *pvarSchema, const PROPVARIANT *pvarId) { FIXME("(%p,%p,%p): stub\n", iface, pvarSchema, pvarId); return E_NOTIMPL; } static HRESULT WINAPI MetadataHandler_RemoveValueByIndex(IWICMetadataWriter *iface, UINT nIndex) { FIXME("(%p,%u): stub\n", iface, nIndex); return E_NOTIMPL; } static const IWICMetadataWriterVtbl MetadataHandler_Vtbl = { MetadataHandler_QueryInterface, MetadataHandler_AddRef, MetadataHandler_Release, MetadataHandler_GetMetadataFormat, MetadataHandler_GetMetadataHandlerInfo, MetadataHandler_GetCount, MetadataHandler_GetValueByIndex, MetadataHandler_GetValue, MetadataHandler_GetEnumerator, MetadataHandler_SetValue, MetadataHandler_SetValueByIndex, MetadataHandler_RemoveValue, MetadataHandler_RemoveValueByIndex }; static HRESULT WINAPI MetadataHandler_PersistStream_QueryInterface(IWICPersistStream *iface, REFIID iid, void **ppv) { MetadataHandler *This = impl_from_IWICPersistStream(iface); return IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv); } static ULONG WINAPI MetadataHandler_PersistStream_AddRef(IWICPersistStream *iface) { MetadataHandler *This = impl_from_IWICPersistStream(iface); return IWICMetadataWriter_AddRef(&This->IWICMetadataWriter_iface); } static ULONG WINAPI MetadataHandler_PersistStream_Release(IWICPersistStream *iface) { MetadataHandler *This = impl_from_IWICPersistStream(iface); return IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface); } static HRESULT WINAPI MetadataHandler_GetClassID(IWICPersistStream *iface, CLSID *pClassID) { FIXME("(%p,%p): stub\n", iface, pClassID); return E_NOTIMPL; } static HRESULT WINAPI MetadataHandler_IsDirty(IWICPersistStream *iface) { FIXME("(%p): stub\n", iface); return E_NOTIMPL; } static HRESULT WINAPI MetadataHandler_Load(IWICPersistStream *iface, IStream *pStm) { MetadataHandler *This = impl_from_IWICPersistStream(iface); TRACE("(%p,%p)\n", iface, pStm); return IWICPersistStream_LoadEx(&This->IWICPersistStream_iface, pStm, NULL, WICPersistOptionsDefault); } static HRESULT WINAPI MetadataHandler_Save(IWICPersistStream *iface, IStream *pStm, BOOL fClearDirty) { FIXME("(%p,%p,%i): stub\n", iface, pStm, fClearDirty); return E_NOTIMPL; } static HRESULT WINAPI MetadataHandler_GetSizeMax(IWICPersistStream *iface, ULARGE_INTEGER *pcbSize) { FIXME("(%p,%p): stub\n", iface, pcbSize); return E_NOTIMPL; } static HRESULT WINAPI MetadataHandler_LoadEx(IWICPersistStream *iface, IStream *pIStream, const GUID *pguidPreferredVendor, DWORD dwPersistOptions) { MetadataHandler *This = impl_from_IWICPersistStream(iface); HRESULT hr; MetadataItem *new_items=NULL; DWORD item_count=0; TRACE("(%p,%p,%s,%x)\n", iface, pIStream, debugstr_guid(pguidPreferredVendor), dwPersistOptions); EnterCriticalSection(&This->lock); hr = This->vtable->fnLoad(pIStream, pguidPreferredVendor, dwPersistOptions, &new_items, &item_count); if (SUCCEEDED(hr)) { MetadataHandler_FreeItems(This); This->items = new_items; This->item_count = item_count; } LeaveCriticalSection(&This->lock); return hr; } static HRESULT WINAPI MetadataHandler_SaveEx(IWICPersistStream *iface, IStream *pIStream, DWORD dwPersistOptions, BOOL fClearDirty) { FIXME("(%p,%p,%x,%i): stub\n", iface, pIStream, dwPersistOptions, fClearDirty); return E_NOTIMPL; } static const IWICPersistStreamVtbl MetadataHandler_PersistStream_Vtbl = { MetadataHandler_PersistStream_QueryInterface, MetadataHandler_PersistStream_AddRef, MetadataHandler_PersistStream_Release, MetadataHandler_GetClassID, MetadataHandler_IsDirty, MetadataHandler_Load, MetadataHandler_Save, MetadataHandler_GetSizeMax, MetadataHandler_LoadEx, MetadataHandler_SaveEx }; HRESULT MetadataReader_Create(const MetadataHandlerVtbl *vtable, IUnknown *pUnkOuter, REFIID iid, void** ppv) { MetadataHandler *This; HRESULT hr; TRACE("%s\n", debugstr_guid(vtable->clsid)); *ppv = NULL; if (pUnkOuter) return CLASS_E_NOAGGREGATION; This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandler)); if (!This) return E_OUTOFMEMORY; This->IWICMetadataWriter_iface.lpVtbl = &MetadataHandler_Vtbl; This->IWICPersistStream_iface.lpVtbl = &MetadataHandler_PersistStream_Vtbl; This->ref = 1; This->vtable = vtable; This->items = NULL; This->item_count = 0; InitializeCriticalSection(&This->lock); This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MetadataHandler.lock"); hr = IWICMetadataWriter_QueryInterface(&This->IWICMetadataWriter_iface, iid, ppv); IWICMetadataWriter_Release(&This->IWICMetadataWriter_iface); return hr; } typedef struct MetadataHandlerEnum { IWICEnumMetadataItem IWICEnumMetadataItem_iface; LONG ref; MetadataHandler *parent; DWORD index; } MetadataHandlerEnum; static inline MetadataHandlerEnum *impl_from_IWICEnumMetadataItem(IWICEnumMetadataItem *iface) { return CONTAINING_RECORD(iface, MetadataHandlerEnum, IWICEnumMetadataItem_iface); } static HRESULT WINAPI MetadataHandlerEnum_QueryInterface(IWICEnumMetadataItem *iface, REFIID iid, void **ppv) { MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface); TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICEnumMetadataItem, iid)) { *ppv = &This->IWICEnumMetadataItem_iface; } else { *ppv = NULL; return E_NOINTERFACE; } IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } static ULONG WINAPI MetadataHandlerEnum_AddRef(IWICEnumMetadataItem *iface) { MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); return ref; } static ULONG WINAPI MetadataHandlerEnum_Release(IWICEnumMetadataItem *iface) { MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); if (ref == 0) { IWICMetadataWriter_Release(&This->parent->IWICMetadataWriter_iface); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI MetadataHandlerEnum_Next(IWICEnumMetadataItem *iface, ULONG celt, PROPVARIANT *rgeltSchema, PROPVARIANT *rgeltId, PROPVARIANT *rgeltValue, ULONG *pceltFetched) { MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface); ULONG new_index; HRESULT hr=S_FALSE; int i; TRACE("(%p,%i)\n", iface, celt); EnterCriticalSection(&This->parent->lock); if (This->index >= This->parent->item_count) { *pceltFetched = 0; LeaveCriticalSection(&This->parent->lock); return S_FALSE; } new_index = min(This->parent->item_count, This->index + celt); *pceltFetched = new_index - This->index; if (rgeltSchema) { for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++) hr = PropVariantCopy(&rgeltSchema[i], &This->parent->items[i+This->index].schema); } for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++) hr = PropVariantCopy(&rgeltId[i], &This->parent->items[i+This->index].id); if (rgeltValue) { for (i=0; SUCCEEDED(hr) && i < *pceltFetched; i++) hr = PropVariantCopy(&rgeltValue[i], &This->parent->items[i+This->index].value); } if (SUCCEEDED(hr)) { This->index = new_index; } LeaveCriticalSection(&This->parent->lock); return hr; } static HRESULT WINAPI MetadataHandlerEnum_Skip(IWICEnumMetadataItem *iface, ULONG celt) { MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface); EnterCriticalSection(&This->parent->lock); This->index += celt; LeaveCriticalSection(&This->parent->lock); return S_OK; } static HRESULT WINAPI MetadataHandlerEnum_Reset(IWICEnumMetadataItem *iface) { MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface); EnterCriticalSection(&This->parent->lock); This->index = 0; LeaveCriticalSection(&This->parent->lock); return S_OK; } static HRESULT WINAPI MetadataHandlerEnum_Clone(IWICEnumMetadataItem *iface, IWICEnumMetadataItem **ppIEnumMetadataItem) { MetadataHandlerEnum *This = impl_from_IWICEnumMetadataItem(iface); HRESULT hr; EnterCriticalSection(&This->parent->lock); hr = MetadataHandlerEnum_Create(This->parent, This->index, ppIEnumMetadataItem); LeaveCriticalSection(&This->parent->lock); return hr; } static const IWICEnumMetadataItemVtbl MetadataHandlerEnum_Vtbl = { MetadataHandlerEnum_QueryInterface, MetadataHandlerEnum_AddRef, MetadataHandlerEnum_Release, MetadataHandlerEnum_Next, MetadataHandlerEnum_Skip, MetadataHandlerEnum_Reset, MetadataHandlerEnum_Clone }; static HRESULT MetadataHandlerEnum_Create(MetadataHandler *parent, DWORD index, IWICEnumMetadataItem **ppIEnumMetadataItem) { MetadataHandlerEnum *This; if (!ppIEnumMetadataItem) return E_INVALIDARG; *ppIEnumMetadataItem = NULL; This = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataHandlerEnum)); if (!This) return E_OUTOFMEMORY; IWICMetadataWriter_AddRef(&parent->IWICMetadataWriter_iface); This->IWICEnumMetadataItem_iface.lpVtbl = &MetadataHandlerEnum_Vtbl; This->ref = 1; This->parent = parent; This->index = index; *ppIEnumMetadataItem = &This->IWICEnumMetadataItem_iface; return S_OK; } static HRESULT LoadUnknownMetadata(IStream *input, const GUID *preferred_vendor, DWORD persist_options, MetadataItem **items, DWORD *item_count) { HRESULT hr; MetadataItem *result; STATSTG stat; BYTE *data; ULONG bytesread; TRACE("\n"); hr = IStream_Stat(input, &stat, STATFLAG_NONAME); if (FAILED(hr)) return hr; data = HeapAlloc(GetProcessHeap(), 0, stat.cbSize.QuadPart); if (!data) return E_OUTOFMEMORY; hr = IStream_Read(input, data, stat.cbSize.QuadPart, &bytesread); if (bytesread != stat.cbSize.QuadPart) hr = E_FAIL; if (hr != S_OK) { HeapFree(GetProcessHeap(), 0, data); return hr; } result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem)); if (!result) { HeapFree(GetProcessHeap(), 0, data); return E_OUTOFMEMORY; } PropVariantInit(&result[0].schema); PropVariantInit(&result[0].id); PropVariantInit(&result[0].value); result[0].value.vt = VT_BLOB; result[0].value.u.blob.cbSize = bytesread; result[0].value.u.blob.pBlobData = data; *items = result; *item_count = 1; return S_OK; } static const MetadataHandlerVtbl UnknownMetadataReader_Vtbl = { 0, &CLSID_WICUnknownMetadataReader, LoadUnknownMetadata }; HRESULT UnknownMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv) { return MetadataReader_Create(&UnknownMetadataReader_Vtbl, pUnkOuter, iid, ppv); } #define SWAP_USHORT(x) do { if (!native_byte_order) (x) = RtlUshortByteSwap(x); } while(0) #define SWAP_ULONG(x) do { if (!native_byte_order) (x) = RtlUlongByteSwap(x); } while(0) #define SWAP_ULONGLONG(x) do { if (!native_byte_order) (x) = RtlUlonglongByteSwap(x); } while(0) struct IFD_entry { SHORT id; SHORT type; ULONG count; LONG value; }; #define IFD_BYTE 1 #define IFD_ASCII 2 #define IFD_SHORT 3 #define IFD_LONG 4 #define IFD_RATIONAL 5 #define IFD_SBYTE 6 #define IFD_UNDEFINED 7 #define IFD_SSHORT 8 #define IFD_SLONG 9 #define IFD_SRATIONAL 10 #define IFD_FLOAT 11 #define IFD_DOUBLE 12 #define IFD_IFD 13 static int tag_to_vt(SHORT tag) { static const int tag2vt[] = { VT_EMPTY, /* 0 */ VT_UI1, /* IFD_BYTE 1 */ VT_LPSTR, /* IFD_ASCII 2 */ VT_UI2, /* IFD_SHORT 3 */ VT_UI4, /* IFD_LONG 4 */ VT_UI8, /* IFD_RATIONAL 5 */ VT_I1, /* IFD_SBYTE 6 */ VT_BLOB, /* IFD_UNDEFINED 7 */ VT_I2, /* IFD_SSHORT 8 */ VT_I4, /* IFD_SLONG 9 */ VT_I8, /* IFD_SRATIONAL 10 */ VT_R4, /* IFD_FLOAT 11 */ VT_R8, /* IFD_DOUBLE 12 */ VT_BLOB, /* IFD_IFD 13 */ }; return (tag > 0 && tag <= 13) ? tag2vt[tag] : VT_BLOB; } static HRESULT load_IFD_entry(IStream *input, const struct IFD_entry *entry, MetadataItem *item, BOOL native_byte_order) { ULONG count, value, i, bytesread; SHORT type; LARGE_INTEGER pos; HRESULT hr; item->schema.vt = VT_EMPTY; item->id.vt = VT_UI2; item->id.u.uiVal = entry->id; SWAP_USHORT(item->id.u.uiVal); count = entry->count; SWAP_ULONG(count); type = entry->type; SWAP_USHORT(type); item->value.vt = tag_to_vt(type); value = entry->value; SWAP_ULONG(value); switch (type) { case IFD_BYTE: case IFD_SBYTE: if (!count) count = 1; if (count <= 4) { const BYTE *data = (const BYTE *)&entry->value; if (count == 1) item->value.u.bVal = data[0]; else { item->value.vt |= VT_VECTOR; item->value.u.caub.cElems = count; item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count); memcpy(item->value.u.caub.pElems, data, count); } break; } item->value.vt |= VT_VECTOR; item->value.u.caub.cElems = count; item->value.u.caub.pElems = HeapAlloc(GetProcessHeap(), 0, count); if (!item->value.u.caub.pElems) return E_OUTOFMEMORY; pos.QuadPart = value; hr = IStream_Seek(input, pos, SEEK_SET, NULL); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems); return hr; } hr = IStream_Read(input, item->value.u.caub.pElems, count, &bytesread); if (bytesread != count) hr = E_FAIL; if (hr != S_OK) { HeapFree(GetProcessHeap(), 0, item->value.u.caub.pElems); return hr; } break; case IFD_SHORT: case IFD_SSHORT: if (!count) count = 1; if (count <= 2) { const SHORT *data = (const SHORT *)&entry->value; if (count == 1) { item->value.u.uiVal = data[0]; SWAP_USHORT(item->value.u.uiVal); } else { item->value.vt |= VT_VECTOR; item->value.u.caui.cElems = count; item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2); memcpy(item->value.u.caui.pElems, data, count * 2); for (i = 0; i < count; i++) SWAP_USHORT(item->value.u.caui.pElems[i]); } break; } item->value.vt |= VT_VECTOR; item->value.u.caui.cElems = count; item->value.u.caui.pElems = HeapAlloc(GetProcessHeap(), 0, count * 2); if (!item->value.u.caui.pElems) return E_OUTOFMEMORY; pos.QuadPart = value; hr = IStream_Seek(input, pos, SEEK_SET, NULL); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems); return hr; } hr = IStream_Read(input, item->value.u.caui.pElems, count * 2, &bytesread); if (bytesread != count * 2) hr = E_FAIL; if (hr != S_OK) { HeapFree(GetProcessHeap(), 0, item->value.u.caui.pElems); return hr; } for (i = 0; i < count; i++) SWAP_USHORT(item->value.u.caui.pElems[i]); break; case IFD_LONG: case IFD_SLONG: case IFD_FLOAT: if (!count) count = 1; if (count == 1) { item->value.u.ulVal = value; break; } item->value.vt |= VT_VECTOR; item->value.u.caul.cElems = count; item->value.u.caul.pElems = HeapAlloc(GetProcessHeap(), 0, count * 4); if (!item->value.u.caul.pElems) return E_OUTOFMEMORY; pos.QuadPart = value; hr = IStream_Seek(input, pos, SEEK_SET, NULL); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems); return hr; } hr = IStream_Read(input, item->value.u.caul.pElems, count * 4, &bytesread); if (bytesread != count * 4) hr = E_FAIL; if (hr != S_OK) { HeapFree(GetProcessHeap(), 0, item->value.u.caul.pElems); return hr; } for (i = 0; i < count; i++) SWAP_ULONG(item->value.u.caul.pElems[i]); break; case IFD_RATIONAL: case IFD_SRATIONAL: case IFD_DOUBLE: if (!count) { FIXME("IFD field type %d, count 0\n", type); item->value.vt = VT_EMPTY; break; } if (count == 1) { ULONGLONG ull; pos.QuadPart = value; hr = IStream_Seek(input, pos, SEEK_SET, NULL); if (FAILED(hr)) return hr; hr = IStream_Read(input, &ull, sizeof(ull), &bytesread); if (bytesread != sizeof(ull)) hr = E_FAIL; if (hr != S_OK) return hr; item->value.u.uhVal.QuadPart = ull; if (type == IFD_DOUBLE) SWAP_ULONGLONG(item->value.u.uhVal.QuadPart); else { SWAP_ULONG(item->value.u.uhVal.u.LowPart); SWAP_ULONG(item->value.u.uhVal.u.HighPart); } break; } else { item->value.vt |= VT_VECTOR; item->value.u.cauh.cElems = count; item->value.u.cauh.pElems = HeapAlloc(GetProcessHeap(), 0, count * 8); if (!item->value.u.cauh.pElems) return E_OUTOFMEMORY; pos.QuadPart = value; hr = IStream_Seek(input, pos, SEEK_SET, NULL); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems); return hr; } hr = IStream_Read(input, item->value.u.cauh.pElems, count * 8, &bytesread); if (bytesread != count * 8) hr = E_FAIL; if (hr != S_OK) { HeapFree(GetProcessHeap(), 0, item->value.u.cauh.pElems); return hr; } for (i = 0; i < count; i++) { if (type == IFD_DOUBLE) SWAP_ULONGLONG(item->value.u.cauh.pElems[i].QuadPart); else { SWAP_ULONG(item->value.u.cauh.pElems[i].u.LowPart); SWAP_ULONG(item->value.u.cauh.pElems[i].u.HighPart); } } } break; case IFD_ASCII: item->value.u.pszVal = HeapAlloc(GetProcessHeap(), 0, count + 1); if (!item->value.u.pszVal) return E_OUTOFMEMORY; if (count <= 4) { const char *data = (const char *)&entry->value; memcpy(item->value.u.pszVal, data, count); item->value.u.pszVal[count] = 0; break; } pos.QuadPart = value; hr = IStream_Seek(input, pos, SEEK_SET, NULL); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, item->value.u.pszVal); return hr; } hr = IStream_Read(input, item->value.u.pszVal, count, &bytesread); if (bytesread != count) hr = E_FAIL; if (hr != S_OK) { HeapFree(GetProcessHeap(), 0, item->value.u.pszVal); return hr; } item->value.u.pszVal[count] = 0; break; case IFD_UNDEFINED: if (!count) { FIXME("IFD field type %d, count 0\n", type); item->value.vt = VT_EMPTY; break; } item->value.u.blob.pBlobData = HeapAlloc(GetProcessHeap(), 0, count); if (!item->value.u.blob.pBlobData) return E_OUTOFMEMORY; item->value.u.blob.cbSize = count; if (count <= 4) { const char *data = (const char *)&entry->value; memcpy(item->value.u.blob.pBlobData, data, count); break; } pos.QuadPart = value; hr = IStream_Seek(input, pos, SEEK_SET, NULL); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData); return hr; } hr = IStream_Read(input, item->value.u.blob.pBlobData, count, &bytesread); if (bytesread != count) hr = E_FAIL; if (hr != S_OK) { HeapFree(GetProcessHeap(), 0, item->value.u.blob.pBlobData); return hr; } break; default: FIXME("loading field of type %d, count %u is not implemented\n", type, count); break; } return S_OK; } static HRESULT LoadIfdMetadata(IStream *input, const GUID *preferred_vendor, DWORD persist_options, MetadataItem **items, DWORD *item_count) { HRESULT hr; MetadataItem *result; USHORT count, i; struct IFD_entry *entry; BOOL native_byte_order = TRUE; ULONG bytesread; TRACE("\n"); #ifdef WORDS_BIGENDIAN if (persist_options & WICPersistOptionsLittleEndian) #else if (persist_options & WICPersistOptionsBigEndian) #endif native_byte_order = FALSE; hr = IStream_Read(input, &count, sizeof(count), &bytesread); if (bytesread != sizeof(count)) hr = E_FAIL; if (hr != S_OK) return hr; SWAP_USHORT(count); entry = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*entry)); if (!entry) return E_OUTOFMEMORY; hr = IStream_Read(input, entry, count * sizeof(*entry), &bytesread); if (bytesread != count * sizeof(*entry)) hr = E_FAIL; if (hr != S_OK) { HeapFree(GetProcessHeap(), 0, entry); return hr; } /* limit number of IFDs to 4096 to avoid infinite loop */ for (i = 0; i < 4096; i++) { ULONG next_ifd_offset; LARGE_INTEGER pos; USHORT next_ifd_count; hr = IStream_Read(input, &next_ifd_offset, sizeof(next_ifd_offset), &bytesread); if (bytesread != sizeof(next_ifd_offset)) hr = E_FAIL; if (hr != S_OK) break; SWAP_ULONG(next_ifd_offset); if (!next_ifd_offset) break; pos.QuadPart = next_ifd_offset; hr = IStream_Seek(input, pos, SEEK_SET, NULL); if (FAILED(hr)) break; hr = IStream_Read(input, &next_ifd_count, sizeof(next_ifd_count), &bytesread); if (bytesread != sizeof(next_ifd_count)) hr = E_FAIL; if (hr != S_OK) break; SWAP_USHORT(next_ifd_count); pos.QuadPart = next_ifd_count * sizeof(*entry); hr = IStream_Seek(input, pos, SEEK_CUR, NULL); if (FAILED(hr)) break; } if (hr != S_OK || i == 4096) { HeapFree(GetProcessHeap(), 0, entry); return WINCODEC_ERR_BADMETADATAHEADER; } result = HeapAlloc(GetProcessHeap(), 0, count * sizeof(*result)); if (!result) { HeapFree(GetProcessHeap(), 0, entry); return E_OUTOFMEMORY; } for (i = 0; i < count; i++) { hr = load_IFD_entry(input, &entry[i], &result[i], native_byte_order); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, entry); HeapFree(GetProcessHeap(), 0, result); return hr; } } HeapFree(GetProcessHeap(), 0, entry); *items = result; *item_count = count; return S_OK; } static const MetadataHandlerVtbl IfdMetadataReader_Vtbl = { 0, &CLSID_WICIfdMetadataReader, LoadIfdMetadata }; HRESULT IfdMetadataReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void **ppv) { return MetadataReader_Create(&IfdMetadataReader_Vtbl, pUnkOuter, iid, ppv); }