blob: e33884881b60391a4af3038721f6f50564af6b61 [file] [log] [blame]
/*
* Copyright 2016 Andrew Eikum for CodeWeavers
* Copyright 2017 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>
#define COBJMACROS
#define NONAMELESSUNION
#include "windef.h"
#include "winbase.h"
#include "objbase.h"
#include "propvarutil.h"
#include "wincodecs_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
static const WCHAR *map_shortname_to_schema(const GUID *format, const WCHAR *name);
typedef struct {
IWICMetadataQueryReader IWICMetadataQueryReader_iface;
LONG ref;
IWICMetadataBlockReader *block;
WCHAR *root;
} QueryReader;
static inline QueryReader *impl_from_IWICMetadataQueryReader(IWICMetadataQueryReader *iface)
{
return CONTAINING_RECORD(iface, QueryReader, IWICMetadataQueryReader_iface);
}
static HRESULT WINAPI mqr_QueryInterface(IWICMetadataQueryReader *iface, REFIID riid,
void **ppvObject)
{
QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObject);
if (IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IWICMetadataQueryReader))
*ppvObject = &This->IWICMetadataQueryReader_iface;
else
*ppvObject = NULL;
if (*ppvObject)
{
IUnknown_AddRef((IUnknown*)*ppvObject);
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG WINAPI mqr_AddRef(IWICMetadataQueryReader *iface)
{
QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) refcount=%u\n", This, ref);
return ref;
}
static ULONG WINAPI mqr_Release(IWICMetadataQueryReader *iface)
{
QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) refcount=%u\n", This, ref);
if (!ref)
{
IWICMetadataBlockReader_Release(This->block);
HeapFree(GetProcessHeap(), 0, This->root);
HeapFree(GetProcessHeap(), 0, This);
}
return ref;
}
static HRESULT WINAPI mqr_GetContainerFormat(IWICMetadataQueryReader *iface, GUID *format)
{
QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
TRACE("(%p,%p)\n", This, format);
return IWICMetadataBlockReader_GetContainerFormat(This->block, format);
}
static HRESULT WINAPI mqr_GetLocation(IWICMetadataQueryReader *iface, UINT len, WCHAR *location, UINT *ret_len)
{
static const WCHAR rootW[] = { '/',0 };
QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
const WCHAR *root;
UINT actual_len;
TRACE("(%p,%u,%p,%p)\n", This, len, location, ret_len);
if (!ret_len) return E_INVALIDARG;
root = This->root ? This->root : rootW;
actual_len = lstrlenW(root) + 1;
if (location)
{
if (len < actual_len)
return WINCODEC_ERR_INSUFFICIENTBUFFER;
memcpy(location, root, actual_len * sizeof(WCHAR));
}
*ret_len = actual_len;
return S_OK;
}
struct string_t
{
const WCHAR *str;
int len;
};
static const struct
{
int len;
WCHAR str[10];
VARTYPE vt;
} str2vt[] =
{
{ 4, {'c','h','a','r'}, VT_I1 },
{ 5, {'u','c','h','a','r'}, VT_UI1 },
{ 5, {'s','h','o','r','t'}, VT_I2 },
{ 6, {'u','s','h','o','r','t'}, VT_UI2 },
{ 4, {'l','o','n','g'}, VT_I4 },
{ 5, {'u','l','o','n','g'}, VT_UI4 },
{ 3, {'i','n','t'}, VT_I4 },
{ 4, {'u','i','n','t'}, VT_UI4 },
{ 8, {'l','o','n','g','l','o','n','g'}, VT_I8 },
{ 9, {'u','l','o','n','g','l','o','n','g'}, VT_UI8 },
{ 5, {'f','l','o','a','t'}, VT_R4 },
{ 6, {'d','o','u','b','l','e'}, VT_R8 },
{ 3, {'s','t','r'}, VT_LPSTR },
{ 4, {'w','s','t','r'}, VT_LPWSTR },
{ 4, {'g','u','i','d'}, VT_CLSID },
{ 4, {'b','o','o','l'}, VT_BOOL }
};
static VARTYPE map_type(struct string_t *str)
{
UINT i;
for (i = 0; i < sizeof(str2vt)/sizeof(str2vt[0]); i++)
{
if (str2vt[i].len == str->len)
{
if (CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
str->str, str->len, str2vt[i].str, str2vt[i].len) == CSTR_EQUAL)
return str2vt[i].vt;
}
}
WARN("type %s is not recognized\n", wine_dbgstr_wn(str->str, str->len));
return VT_ILLEGAL;
}
static HRESULT get_token(struct string_t *elem, PROPVARIANT *id, PROPVARIANT *schema, int *idx)
{
const WCHAR *start, *end, *p;
WCHAR *bstr;
struct string_t next_elem;
HRESULT hr;
TRACE("%s, len %d\n", wine_dbgstr_wn(elem->str, elem->len), elem->len);
PropVariantInit(id);
PropVariantInit(schema);
if (!elem->len) return S_OK;
start = elem->str;
if (*start == '[')
{
WCHAR *idx_end;
if (start[1] < '0' || start[1] > '9') return DISP_E_TYPEMISMATCH;
*idx = strtolW(start + 1, &idx_end, 10);
if (idx_end > elem->str + elem->len) return WINCODEC_ERR_INVALIDQUERYREQUEST;
if (*idx_end != ']') return WINCODEC_ERR_INVALIDQUERYREQUEST;
if (*idx < 0) return WINCODEC_ERR_INVALIDQUERYREQUEST;
end = idx_end + 1;
next_elem.str = end;
next_elem.len = elem->len - (end - start);
hr = get_token(&next_elem, id, schema, idx);
if (hr != S_OK)
{
TRACE("get_token error %#x\n", hr);
return hr;
}
elem->len = (end - start) + next_elem.len;
TRACE("indexed %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
return S_OK;
}
else if (*start == '{')
{
VARTYPE vt;
PROPVARIANT next_token;
end = memchrW(start + 1, '=', elem->len - 1);
if (!end) return WINCODEC_ERR_INVALIDQUERYREQUEST;
if (end > elem->str + elem->len) return WINCODEC_ERR_INVALIDQUERYREQUEST;
next_elem.str = start + 1;
next_elem.len = end - start - 1;
vt = map_type(&next_elem);
TRACE("type %s => %d\n", wine_dbgstr_wn(next_elem.str, next_elem.len), vt);
if (vt == VT_ILLEGAL) return WINCODEC_ERR_WRONGSTATE;
next_token.vt = VT_BSTR;
next_token.u.bstrVal = SysAllocStringLen(NULL, elem->len - (end - start) + 1);
if (!next_token.u.bstrVal) return E_OUTOFMEMORY;
bstr = next_token.u.bstrVal;
end++;
p = end;
while (*end && *end != '}' && end - start < elem->len)
{
if (*end == '\\') end++;
*bstr++ = *end++;
}
if (*end != '}')
{
PropVariantClear(&next_token);
return WINCODEC_ERR_INVALIDQUERYREQUEST;
}
*bstr = 0;
TRACE("schema/id %s\n", wine_dbgstr_w(next_token.u.bstrVal));
if (vt == VT_CLSID)
{
id->vt = VT_CLSID;
id->u.puuid = CoTaskMemAlloc(sizeof(GUID));
if (!id->u.puuid)
{
PropVariantClear(&next_token);
return E_OUTOFMEMORY;
}
hr = UuidFromStringW(next_token.u.bstrVal, id->u.puuid);
}
else
hr = PropVariantChangeType(id, &next_token, 0, vt);
PropVariantClear(&next_token);
if (hr != S_OK)
{
PropVariantClear(id);
PropVariantClear(schema);
return hr;
}
end++;
if (*end == ':')
{
PROPVARIANT next_id, next_schema;
int next_idx = 0;
next_elem.str = end + 1;
next_elem.len = elem->len - (end - start + 1);
hr = get_token(&next_elem, &next_id, &next_schema, &next_idx);
if (hr != S_OK)
{
TRACE("get_token error %#x\n", hr);
return hr;
}
elem->len = (end - start + 1) + next_elem.len;
TRACE("id %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
if (next_schema.vt != VT_EMPTY)
{
PropVariantClear(&next_id);
PropVariantClear(&next_schema);
return WINCODEC_ERR_WRONGSTATE;
}
*schema = *id;
*id = next_id;
return S_OK;
}
elem->len = end - start;
return S_OK;
}
end = memchrW(start, '/', elem->len);
if (!end) end = start + elem->len;
p = memchrW(start, ':', end - start);
if (p)
{
next_elem.str = p + 1;
next_elem.len = end - p - 1;
elem->len = p - start;
}
else
elem->len = end - start;
id->vt = VT_BSTR;
id->u.bstrVal = SysAllocStringLen(NULL, elem->len + 1);
if (!id->u.bstrVal) return E_OUTOFMEMORY;
bstr = id->u.bstrVal;
p = elem->str;
while (p - elem->str < elem->len)
{
if (*p == '\\') p++;
*bstr++ = *p++;
}
*bstr = 0;
TRACE("%s [%d]\n", wine_dbgstr_variant((VARIANT *)id), *idx);
if (*p == ':')
{
PROPVARIANT next_id, next_schema;
int next_idx = 0;
hr = get_token(&next_elem, &next_id, &next_schema, &next_idx);
if (hr != S_OK)
{
TRACE("get_token error %#x\n", hr);
PropVariantClear(id);
PropVariantClear(schema);
return hr;
}
elem->len += next_elem.len + 1;
TRACE("id %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
if (next_schema.vt != VT_EMPTY)
{
PropVariantClear(&next_id);
PropVariantClear(&next_schema);
PropVariantClear(id);
PropVariantClear(schema);
return WINCODEC_ERR_WRONGSTATE;
}
*schema = *id;
*id = next_id;
}
return S_OK;
}
static HRESULT find_reader_from_block(IWICMetadataBlockReader *block_reader, UINT index,
GUID *guid, IWICMetadataReader **reader)
{
HRESULT hr;
GUID format;
IWICMetadataReader *new_reader;
UINT count, i, matched_index;
*reader = NULL;
hr = IWICMetadataBlockReader_GetCount(block_reader, &count);
if (hr != S_OK) return hr;
matched_index = 0;
for (i = 0; i < count; i++)
{
hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &new_reader);
if (hr != S_OK) return hr;
hr = IWICMetadataReader_GetMetadataFormat(new_reader, &format);
if (hr == S_OK)
{
if (IsEqualGUID(&format, guid))
{
if (matched_index == index)
{
*reader = new_reader;
return S_OK;
}
matched_index++;
}
}
IWICMetadataReader_Release(new_reader);
if (hr != S_OK) return hr;
}
return WINCODEC_ERR_PROPERTYNOTFOUND;
}
static HRESULT get_next_reader(IWICMetadataReader *reader, UINT index,
GUID *guid, IWICMetadataReader **new_reader)
{
HRESULT hr;
PROPVARIANT schema, id, value;
*new_reader = NULL;
PropVariantInit(&schema);
PropVariantInit(&id);
PropVariantInit(&value);
if (index)
{
schema.vt = VT_UI2;
schema.u.uiVal = index;
}
id.vt = VT_CLSID;
id.u.puuid = guid;
hr = IWICMetadataReader_GetValue(reader, &schema, &id, &value);
if (hr != S_OK) return hr;
if (value.vt == VT_UNKNOWN)
hr = IUnknown_QueryInterface(value.u.punkVal, &IID_IWICMetadataReader, (void **)new_reader);
else
hr = WINCODEC_ERR_UNEXPECTEDMETADATATYPE;
PropVariantClear(&value);
return hr;
}
static HRESULT WINAPI mqr_GetMetadataByName(IWICMetadataQueryReader *iface, LPCWSTR query, PROPVARIANT *value)
{
QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
struct string_t elem;
WCHAR *full_query;
const WCHAR *p;
int index, len;
PROPVARIANT tk_id, tk_schema, new_value;
GUID guid;
IWICMetadataReader *reader;
HRESULT hr = S_OK;
TRACE("(%p,%s,%p)\n", This, wine_dbgstr_w(query), value);
len = lstrlenW(query) + 1;
if (This->root) len += lstrlenW(This->root);
full_query = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
full_query[0] = 0;
if (This->root)
lstrcpyW(full_query, This->root);
lstrcatW(full_query, query);
PropVariantInit(&tk_id);
PropVariantInit(&tk_schema);
PropVariantInit(&new_value);
reader = NULL;
p = full_query;
while (*p)
{
if (*p != '/')
{
WARN("query should start with '/'\n");
hr = WINCODEC_ERR_PROPERTYNOTSUPPORTED;
break;
}
p++;
index = 0;
elem.str = p;
elem.len = lstrlenW(p);
hr = get_token(&elem, &tk_id, &tk_schema, &index);
if (hr != S_OK)
{
WARN("get_token error %#x\n", hr);
break;
}
TRACE("parsed %d characters: %s, index %d\n", elem.len, wine_dbgstr_wn(elem.str, elem.len), index);
TRACE("id %s, schema %s\n", wine_dbgstr_variant((VARIANT *)&tk_id), wine_dbgstr_variant((VARIANT *)&tk_schema));
if (!elem.len) break;
if (tk_id.vt == VT_CLSID || (tk_id.vt == VT_BSTR && WICMapShortNameToGuid(tk_id.u.bstrVal, &guid) == S_OK))
{
WCHAR *root;
if (tk_schema.vt != VT_EMPTY)
{
FIXME("unsupported schema vt %u\n", tk_schema.vt);
PropVariantClear(&tk_schema);
}
if (tk_id.vt == VT_CLSID) guid = *tk_id.u.puuid;
if (reader)
{
IWICMetadataReader *new_reader;
hr = get_next_reader(reader, index, &guid, &new_reader);
IWICMetadataReader_Release(reader);
reader = new_reader;
}
else
hr = find_reader_from_block(This->block, index, &guid, &reader);
if (hr != S_OK) break;
root = SysAllocStringLen(NULL, elem.str + elem.len - full_query + 2);
if (!root)
{
hr = E_OUTOFMEMORY;
break;
}
lstrcpynW(root, full_query, p - full_query + elem.len + 1);
PropVariantClear(&new_value);
new_value.vt = VT_UNKNOWN;
hr = MetadataQueryReader_CreateInstance(This->block, root, (IWICMetadataQueryReader **)&new_value.u.punkVal);
SysFreeString(root);
if (hr != S_OK) break;
}
else
{
PROPVARIANT schema, id;
if (!reader)
{
hr = WINCODEC_ERR_INVALIDQUERYREQUEST;
break;
}
if (tk_schema.vt == VT_BSTR)
{
hr = IWICMetadataReader_GetMetadataFormat(reader, &guid);
if (hr != S_OK) break;
schema.vt = VT_LPWSTR;
schema.u.pwszVal = (LPWSTR)map_shortname_to_schema(&guid, tk_schema.u.bstrVal);
if (!schema.u.pwszVal)
schema.u.pwszVal = tk_schema.u.bstrVal;
}
else
schema = tk_schema;
if (tk_id.vt == VT_BSTR)
{
id.vt = VT_LPWSTR;
id.u.pwszVal = tk_id.u.bstrVal;
}
else
id = tk_id;
PropVariantClear(&new_value);
hr = IWICMetadataReader_GetValue(reader, &schema, &id, &new_value);
if (hr != S_OK) break;
}
p += elem.len;
PropVariantClear(&tk_id);
PropVariantClear(&tk_schema);
}
if (reader)
IWICMetadataReader_Release(reader);
PropVariantClear(&tk_id);
PropVariantClear(&tk_schema);
if (hr == S_OK)
*value = new_value;
else
PropVariantClear(&new_value);
HeapFree(GetProcessHeap(), 0, full_query);
return hr;
}
static HRESULT WINAPI mqr_GetEnumerator(IWICMetadataQueryReader *iface,
IEnumString **ppIEnumString)
{
QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
FIXME("(%p,%p)\n", This, ppIEnumString);
return E_NOTIMPL;
}
static IWICMetadataQueryReaderVtbl mqr_vtbl = {
mqr_QueryInterface,
mqr_AddRef,
mqr_Release,
mqr_GetContainerFormat,
mqr_GetLocation,
mqr_GetMetadataByName,
mqr_GetEnumerator
};
HRESULT MetadataQueryReader_CreateInstance(IWICMetadataBlockReader *mbr, const WCHAR *root, IWICMetadataQueryReader **out)
{
QueryReader *obj;
obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
if (!obj)
return E_OUTOFMEMORY;
obj->IWICMetadataQueryReader_iface.lpVtbl = &mqr_vtbl;
obj->ref = 1;
IWICMetadataBlockReader_AddRef(mbr);
obj->block = mbr;
obj->root = root ? heap_strdupW(root) : NULL;
*out = &obj->IWICMetadataQueryReader_iface;
return S_OK;
}
static const WCHAR bmpW[] = { 'b','m','p',0 };
static const WCHAR pngW[] = { 'p','n','g',0 };
static const WCHAR icoW[] = { 'i','c','o',0 };
static const WCHAR jpgW[] = { 'j','p','g',0 };
static const WCHAR tiffW[] = { 't','i','f','f',0 };
static const WCHAR gifW[] = { 'g','i','f',0 };
static const WCHAR wmphotoW[] = { 'w','m','p','h','o','t','o',0 };
static const WCHAR unknownW[] = { 'u','n','k','n','o','w','n',0 };
static const WCHAR ifdW[] = { 'i','f','d',0 };
static const WCHAR subW[] = { 's','u','b',0 };
static const WCHAR exifW[] = { 'e','x','i','f',0 };
static const WCHAR gpsW[] = { 'g','p','s',0 };
static const WCHAR interopW[] = { 'i','n','t','e','r','o','p',0 };
static const WCHAR app0W[] = { 'a','p','p','0',0 };
static const WCHAR app1W[] = { 'a','p','p','1',0 };
static const WCHAR app13W[] = { 'a','p','p','1','3',0 };
static const WCHAR iptcW[] = { 'i','p','t','c',0 };
static const WCHAR irbW[] = { 'i','r','b',0 };
static const WCHAR _8bimiptcW[] = { '8','b','i','m','i','p','t','c',0 };
static const WCHAR _8bimResInfoW[] = { '8','b','i','m','R','e','s','I','n','f','o',0 };
static const WCHAR _8bimiptcdigestW[] = { '8','b','i','m','i','p','t','c','d','i','g','e','s','t',0 };
static const WCHAR xmpW[] = { 'x','m','p',0 };
static const WCHAR thumbW[] = { 't','h','u','m','b',0 };
static const WCHAR tEXtW[] = { 't','E','X','t',0 };
static const WCHAR xmpstructW[] = { 'x','m','p','s','t','r','u','c','t',0 };
static const WCHAR xmpbagW[] = { 'x','m','p','b','a','g',0 };
static const WCHAR xmpseqW[] = { 'x','m','p','s','e','q',0 };
static const WCHAR xmpaltW[] = { 'x','m','p','a','l','t',0 };
static const WCHAR logscrdescW[] = { 'l','o','g','s','c','r','d','e','s','c',0 };
static const WCHAR imgdescW[] = { 'i','m','g','d','e','s','c',0 };
static const WCHAR grctlextW[] = { 'g','r','c','t','l','e','x','t',0 };
static const WCHAR appextW[] = { 'a','p','p','e','x','t',0 };
static const WCHAR chrominanceW[] = { 'c','h','r','o','m','i','n','a','n','c','e',0 };
static const WCHAR luminanceW[] = { 'l','u','m','i','n','a','n','c','e',0 };
static const WCHAR comW[] = { 'c','o','m',0 };
static const WCHAR commentextW[] = { 'c','o','m','m','e','n','t','e','x','t',0 };
static const WCHAR gAMAW[] = { 'g','A','M','A',0 };
static const WCHAR bKGDW[] = { 'b','K','G','D',0 };
static const WCHAR iTXtW[] = { 'i','T','X','t',0 };
static const WCHAR cHRMW[] = { 'c','H','R','M',0 };
static const WCHAR hISTW[] = { 'h','I','S','T',0 };
static const WCHAR iCCPW[] = { 'i','C','C','P',0 };
static const WCHAR sRGBW[] = { 's','R','G','B',0 };
static const WCHAR tIMEW[] = { 't','I','M','E',0 };
static const struct
{
const GUID *guid;
const WCHAR *name;
} guid2name[] =
{
{ &GUID_ContainerFormatBmp, bmpW },
{ &GUID_ContainerFormatPng, pngW },
{ &GUID_ContainerFormatIco, icoW },
{ &GUID_ContainerFormatJpeg, jpgW },
{ &GUID_ContainerFormatTiff, tiffW },
{ &GUID_ContainerFormatGif, gifW },
{ &GUID_ContainerFormatWmp, wmphotoW },
{ &GUID_MetadataFormatUnknown, unknownW },
{ &GUID_MetadataFormatIfd, ifdW },
{ &GUID_MetadataFormatSubIfd, subW },
{ &GUID_MetadataFormatExif, exifW },
{ &GUID_MetadataFormatGps, gpsW },
{ &GUID_MetadataFormatInterop, interopW },
{ &GUID_MetadataFormatApp0, app0W },
{ &GUID_MetadataFormatApp1, app1W },
{ &GUID_MetadataFormatApp13, app13W },
{ &GUID_MetadataFormatIPTC, iptcW },
{ &GUID_MetadataFormatIRB, irbW },
{ &GUID_MetadataFormat8BIMIPTC, _8bimiptcW },
{ &GUID_MetadataFormat8BIMResolutionInfo, _8bimResInfoW },
{ &GUID_MetadataFormat8BIMIPTCDigest, _8bimiptcdigestW },
{ &GUID_MetadataFormatXMP, xmpW },
{ &GUID_MetadataFormatThumbnail, thumbW },
{ &GUID_MetadataFormatChunktEXt, tEXtW },
{ &GUID_MetadataFormatXMPStruct, xmpstructW },
{ &GUID_MetadataFormatXMPBag, xmpbagW },
{ &GUID_MetadataFormatXMPSeq, xmpseqW },
{ &GUID_MetadataFormatXMPAlt, xmpaltW },
{ &GUID_MetadataFormatLSD, logscrdescW },
{ &GUID_MetadataFormatIMD, imgdescW },
{ &GUID_MetadataFormatGCE, grctlextW },
{ &GUID_MetadataFormatAPE, appextW },
{ &GUID_MetadataFormatJpegChrominance, chrominanceW },
{ &GUID_MetadataFormatJpegLuminance, luminanceW },
{ &GUID_MetadataFormatJpegComment, comW },
{ &GUID_MetadataFormatGifComment, commentextW },
{ &GUID_MetadataFormatChunkgAMA, gAMAW },
{ &GUID_MetadataFormatChunkbKGD, bKGDW },
{ &GUID_MetadataFormatChunkiTXt, iTXtW },
{ &GUID_MetadataFormatChunkcHRM, cHRMW },
{ &GUID_MetadataFormatChunkhIST, hISTW },
{ &GUID_MetadataFormatChunkiCCP, iCCPW },
{ &GUID_MetadataFormatChunksRGB, sRGBW },
{ &GUID_MetadataFormatChunktIME, tIMEW }
};
HRESULT WINAPI WICMapGuidToShortName(REFGUID guid, UINT len, WCHAR *name, UINT *ret_len)
{
UINT i;
TRACE("%s,%u,%p,%p\n", wine_dbgstr_guid(guid), len, name, ret_len);
if (!guid) return E_INVALIDARG;
for (i = 0; i < sizeof(guid2name)/sizeof(guid2name[0]); i++)
{
if (IsEqualGUID(guid, guid2name[i].guid))
{
if (name)
{
if (!len) return E_INVALIDARG;
len = min(len - 1, lstrlenW(guid2name[i].name));
memcpy(name, guid2name[i].name, len * sizeof(WCHAR));
name[len] = 0;
if (len < lstrlenW(guid2name[i].name))
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
if (ret_len) *ret_len = lstrlenW(guid2name[i].name) + 1;
return S_OK;
}
}
return WINCODEC_ERR_PROPERTYNOTFOUND;
}
HRESULT WINAPI WICMapShortNameToGuid(PCWSTR name, GUID *guid)
{
UINT i;
TRACE("%s,%p\n", debugstr_w(name), guid);
if (!name || !guid) return E_INVALIDARG;
for (i = 0; i < sizeof(guid2name)/sizeof(guid2name[0]); i++)
{
if (!lstrcmpiW(name, guid2name[i].name))
{
*guid = *guid2name[i].guid;
return S_OK;
}
}
return WINCODEC_ERR_PROPERTYNOTFOUND;
}
static const WCHAR rdf[] = { 'r','d','f',0 };
static const WCHAR rdf_scheme[] = { 'h','t','t','p',':','/','/','w','w','w','.','w','3','.','o','r','g','/','1','9','9','9','/','0','2','/','2','2','-','r','d','f','-','s','y','n','t','a','x','-','n','s','#',0 };
static const WCHAR dc[] = { 'd','c',0 };
static const WCHAR dc_scheme[] = { 'h','t','t','p',':','/','/','p','u','r','l','.','o','r','g','/','d','c','/','e','l','e','m','e','n','t','s','/','1','.','1','/',0 };
static const WCHAR xmp[] = { 'x','m','p',0 };
static const WCHAR xmp_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/',0 };
static const WCHAR xmpidq[] = { 'x','m','p','i','d','q',0 };
static const WCHAR xmpidq_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','m','p','/','I','d','e','n','t','i','f','i','e','r','/','q','u','a','l','/','1','.','0','/',0 };
static const WCHAR xmpRights[] = { 'x','m','p','R','i','g','h','t','s',0 };
static const WCHAR xmpRights_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','r','i','g','h','t','s','/',0 };
static const WCHAR xmpMM[] = { 'x','m','p','M','M',0 };
static const WCHAR xmpMM_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','m','m','/',0 };
static const WCHAR xmpBJ[] = { 'x','m','p','B','J',0 };
static const WCHAR xmpBJ_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','b','j','/',0 };
static const WCHAR xmpTPg[] = { 'x','m','p','T','P','g',0 };
static const WCHAR xmpTPg_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','t','/','p','g','/',0 };
static const WCHAR pdf[] = { 'p','d','f',0 };
static const WCHAR pdf_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','p','d','f','/','1','.','3','/',0 };
static const WCHAR photoshop[] = { 'p','h','o','t','o','s','h','o','p',0 };
static const WCHAR photoshop_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','p','h','o','t','o','s','h','o','p','/','1','.','0','/',0 };
static const WCHAR tiff[] = { 't','i','f','f',0 };
static const WCHAR tiff_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','t','i','f','f','/','1','.','0','/',0 };
static const WCHAR exif[] = { 'e','x','i','f',0 };
static const WCHAR exif_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','e','x','i','f','/','1','.','0','/',0 };
static const WCHAR stDim[] = { 's','t','D','i','m',0 };
static const WCHAR stDim_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','D','i','m','e','n','s','i','o','n','s','#',0 };
static const WCHAR xapGImg[] = { 'x','a','p','G','I','m','g',0 };
static const WCHAR xapGImg_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','g','/','i','m','g','/',0 };
static const WCHAR stEvt[] = { 's','t','E','v','t',0 };
static const WCHAR stEvt_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','R','e','s','o','u','r','c','e','E','v','e','n','t','#',0 };
static const WCHAR stRef[] = { 's','t','R','e','f',0 };
static const WCHAR stRef_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','R','e','s','o','u','r','c','e','R','e','f','#',0 };
static const WCHAR stVer[] = { 's','t','V','e','r',0 };
static const WCHAR stVer_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','V','e','r','s','i','o','n','#',0 };
static const WCHAR stJob[] = { 's','t','J','o','b',0 };
static const WCHAR stJob_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','a','p','/','1','.','0','/','s','T','y','p','e','/','J','o','b','#',0 };
static const WCHAR aux[] = { 'a','u','x',0 };
static const WCHAR aux_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','e','x','i','f','/','1','.','0','/','a','u','x','/',0 };
static const WCHAR crs[] = { 'c','r','s',0 };
static const WCHAR crs_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','c','a','m','e','r','a','-','r','a','w','-','s','e','t','t','i','n','g','s','/','1','.','0','/',0 };
static const WCHAR xmpDM[] = { 'x','m','p','D','M',0 };
static const WCHAR xmpDM_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','a','d','o','b','e','.','c','o','m','/','x','m','p','/','1','.','0','/','D','y','n','a','m','i','c','M','e','d','i','a','/',0 };
static const WCHAR Iptc4xmpCore[] = { 'I','p','t','c','4','x','m','p','C','o','r','e',0 };
static const WCHAR Iptc4xmpCore_scheme[] = { 'h','t','t','p',':','/','/','i','p','t','c','.','o','r','g','/','s','t','d','/','I','p','t','c','4','x','m','p','C','o','r','e','/','1','.','0','/','x','m','l','n','s','/',0 };
static const WCHAR MicrosoftPhoto[] = { 'M','i','c','r','o','s','o','f','t','P','h','o','t','o',0 };
static const WCHAR MicrosoftPhoto_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','m','i','c','r','o','s','o','f','t','.','c','o','m','/','p','h','o','t','o','/','1','.','0','/',0 };
static const WCHAR MP[] = { 'M','P',0 };
static const WCHAR MP_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','m','i','c','r','o','s','o','f','t','.','c','o','m','/','p','h','o','t','o','/','1','.','2','/',0 };
static const WCHAR MPRI[] = { 'M','P','R','I',0 };
static const WCHAR MPRI_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','m','i','c','r','o','s','o','f','t','.','c','o','m','/','p','h','o','t','o','/','1','.','2','/','t','/','R','e','g','i','o','n','I','n','f','o','#',0 };
static const WCHAR MPReg[] = { 'M','P','R','e','g',0 };
static const WCHAR MPReg_scheme[] = { 'h','t','t','p',':','/','/','n','s','.','m','i','c','r','o','s','o','f','t','.','c','o','m','/','p','h','o','t','o','/','1','.','2','/','t','/','R','e','g','i','o','n','#',0 };
static const struct
{
const WCHAR *name;
const WCHAR *schema;
} name2schema[] =
{
{ rdf, rdf_scheme },
{ dc, dc_scheme },
{ xmp, xmp_scheme },
{ xmpidq, xmpidq_scheme },
{ xmpRights, xmpRights_scheme },
{ xmpMM, xmpMM_scheme },
{ xmpBJ, xmpBJ_scheme },
{ xmpTPg, xmpTPg_scheme },
{ pdf, pdf_scheme },
{ photoshop, photoshop_scheme },
{ tiff, tiff_scheme },
{ exif, exif_scheme },
{ stDim, stDim_scheme },
{ xapGImg, xapGImg_scheme },
{ stEvt, stEvt_scheme },
{ stRef, stRef_scheme },
{ stVer, stVer_scheme },
{ stJob, stJob_scheme },
{ aux, aux_scheme },
{ crs, crs_scheme },
{ xmpDM, xmpDM_scheme },
{ Iptc4xmpCore, Iptc4xmpCore_scheme },
{ MicrosoftPhoto, MicrosoftPhoto_scheme },
{ MP, MP_scheme },
{ MPRI, MPRI_scheme },
{ MPReg, MPReg_scheme }
};
static const WCHAR *map_shortname_to_schema(const GUID *format, const WCHAR *name)
{
UINT i;
/* It appears that the only metadata formats
* that support schemas are xmp and xmpstruct.
*/
if (!IsEqualGUID(format, &GUID_MetadataFormatXMP) &&
!IsEqualGUID(format, &GUID_MetadataFormatXMPStruct))
return NULL;
for (i = 0; i < sizeof(name2schema)/sizeof(name2schema[0]); i++)
{
if (!lstrcmpW(name2schema[i].name, name))
return name2schema[i].schema;
}
return NULL;
}
HRESULT WINAPI WICMapSchemaToName(REFGUID format, LPWSTR schema, UINT len, WCHAR *name, UINT *ret_len)
{
UINT i;
TRACE("%s,%s,%u,%p,%p\n", wine_dbgstr_guid(format), debugstr_w(schema), len, name, ret_len);
if (!format || !schema || !ret_len)
return E_INVALIDARG;
/* It appears that the only metadata formats
* that support schemas are xmp and xmpstruct.
*/
if (!IsEqualGUID(format, &GUID_MetadataFormatXMP) &&
!IsEqualGUID(format, &GUID_MetadataFormatXMPStruct))
return WINCODEC_ERR_PROPERTYNOTFOUND;
for (i = 0; i < sizeof(name2schema)/sizeof(name2schema[0]); i++)
{
if (!lstrcmpW(name2schema[i].schema, schema))
{
if (name)
{
if (!len) return E_INVALIDARG;
len = min(len - 1, lstrlenW(name2schema[i].name));
memcpy(name, name2schema[i].name, len * sizeof(WCHAR));
name[len] = 0;
if (len < lstrlenW(name2schema[i].name))
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
if (ret_len) *ret_len = lstrlenW(name2schema[i].name) + 1;
return S_OK;
}
}
return WINCODEC_ERR_PROPERTYNOTFOUND;
}