1 /*
2  * Copyright 2016 Andrew Eikum for CodeWeavers
3  * Copyright 2017 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 
24 #define COBJMACROS
25 #define NONAMELESSUNION
26 
27 #include "windef.h"
28 #include "winbase.h"
29 #include "objbase.h"
30 #include "propvarutil.h"
31 
32 #include "wincodecs_private.h"
33 
34 #include "wine/debug.h"
35 
36 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
37 
38 static const WCHAR *map_shortname_to_schema(const GUID *format, const WCHAR *name);
39 
40 typedef struct {
41     IWICMetadataQueryReader IWICMetadataQueryReader_iface;
42     LONG ref;
43     IWICMetadataBlockReader *block;
44     WCHAR *root;
45 } QueryReader;
46 
47 static inline QueryReader *impl_from_IWICMetadataQueryReader(IWICMetadataQueryReader *iface)
48 {
49     return CONTAINING_RECORD(iface, QueryReader, IWICMetadataQueryReader_iface);
50 }
51 
52 static HRESULT WINAPI mqr_QueryInterface(IWICMetadataQueryReader *iface, REFIID riid,
53         void **ppvObject)
54 {
55     QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
56 
57     TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObject);
58 
59     if (IsEqualGUID(riid, &IID_IUnknown) ||
60             IsEqualGUID(riid, &IID_IWICMetadataQueryReader))
61         *ppvObject = &This->IWICMetadataQueryReader_iface;
62     else
63         *ppvObject = NULL;
64 
65     if (*ppvObject)
66     {
67         IUnknown_AddRef((IUnknown*)*ppvObject);
68         return S_OK;
69     }
70 
71     return E_NOINTERFACE;
72 }
73 
74 static ULONG WINAPI mqr_AddRef(IWICMetadataQueryReader *iface)
75 {
76     QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
77     ULONG ref = InterlockedIncrement(&This->ref);
78     TRACE("(%p) refcount=%u\n", This, ref);
79     return ref;
80 }
81 
82 static ULONG WINAPI mqr_Release(IWICMetadataQueryReader *iface)
83 {
84     QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
85     ULONG ref = InterlockedDecrement(&This->ref);
86     TRACE("(%p) refcount=%u\n", This, ref);
87     if (!ref)
88     {
89         IWICMetadataBlockReader_Release(This->block);
90         HeapFree(GetProcessHeap(), 0, This->root);
91         HeapFree(GetProcessHeap(), 0, This);
92     }
93     return ref;
94 }
95 
96 static HRESULT WINAPI mqr_GetContainerFormat(IWICMetadataQueryReader *iface, GUID *format)
97 {
98     QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
99 
100     TRACE("(%p,%p)\n", This, format);
101 
102     return IWICMetadataBlockReader_GetContainerFormat(This->block, format);
103 }
104 
105 static HRESULT WINAPI mqr_GetLocation(IWICMetadataQueryReader *iface, UINT len, WCHAR *location, UINT *ret_len)
106 {
107     static const WCHAR rootW[] = { '/',0 };
108     QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
109     const WCHAR *root;
110     UINT actual_len;
111 
112     TRACE("(%p,%u,%p,%p)\n", This, len, location, ret_len);
113 
114     if (!ret_len) return E_INVALIDARG;
115 
116     root = This->root ? This->root : rootW;
117     actual_len = lstrlenW(root) + 1;
118 
119     if (location)
120     {
121         if (len < actual_len)
122             return WINCODEC_ERR_INSUFFICIENTBUFFER;
123 
124         memcpy(location, root, actual_len * sizeof(WCHAR));
125     }
126 
127     *ret_len = actual_len;
128 
129     return S_OK;
130 }
131 
132 struct string_t
133 {
134     const WCHAR *str;
135     int len;
136 };
137 
138 static const struct
139 {
140     int len;
141     WCHAR str[10];
142     VARTYPE vt;
143 } str2vt[] =
144 {
145     { 4, {'c','h','a','r'}, VT_I1 },
146     { 5, {'u','c','h','a','r'}, VT_UI1 },
147     { 5, {'s','h','o','r','t'}, VT_I2 },
148     { 6, {'u','s','h','o','r','t'}, VT_UI2 },
149     { 4, {'l','o','n','g'}, VT_I4 },
150     { 5, {'u','l','o','n','g'}, VT_UI4 },
151     { 3, {'i','n','t'}, VT_I4 },
152     { 4, {'u','i','n','t'}, VT_UI4 },
153     { 8, {'l','o','n','g','l','o','n','g'}, VT_I8 },
154     { 9, {'u','l','o','n','g','l','o','n','g'}, VT_UI8 },
155     { 5, {'f','l','o','a','t'}, VT_R4 },
156     { 6, {'d','o','u','b','l','e'}, VT_R8 },
157     { 3, {'s','t','r'}, VT_LPSTR },
158     { 4, {'w','s','t','r'}, VT_LPWSTR },
159     { 4, {'g','u','i','d'}, VT_CLSID },
160     { 4, {'b','o','o','l'}, VT_BOOL }
161 };
162 
163 static VARTYPE map_type(struct string_t *str)
164 {
165     UINT i;
166 
167     for (i = 0; i < sizeof(str2vt)/sizeof(str2vt[0]); i++)
168     {
169         if (str2vt[i].len == str->len)
170         {
171             if (CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
172                 str->str, str->len, str2vt[i].str, str2vt[i].len) == CSTR_EQUAL)
173                 return str2vt[i].vt;
174         }
175     }
176 
177     WARN("type %s is not recognized\n", wine_dbgstr_wn(str->str, str->len));
178 
179     return VT_ILLEGAL;
180 }
181 
182 static HRESULT get_token(struct string_t *elem, PROPVARIANT *id, PROPVARIANT *schema, int *idx)
183 {
184     const WCHAR *start, *end, *p;
185     WCHAR *bstr;
186     struct string_t next_elem;
187     HRESULT hr;
188 
189     TRACE("%s, len %d\n", wine_dbgstr_wn(elem->str, elem->len), elem->len);
190 
191     PropVariantInit(id);
192     PropVariantInit(schema);
193 
194     if (!elem->len) return S_OK;
195 
196     start = elem->str;
197 
198     if (*start == '[')
199     {
200         WCHAR *idx_end;
201 
202         if (start[1] < '0' || start[1] > '9') return DISP_E_TYPEMISMATCH;
203 
204         *idx = strtolW(start + 1, &idx_end, 10);
205         if (idx_end > elem->str + elem->len) return WINCODEC_ERR_INVALIDQUERYREQUEST;
206         if (*idx_end != ']') return WINCODEC_ERR_INVALIDQUERYREQUEST;
207         if (*idx < 0) return WINCODEC_ERR_INVALIDQUERYREQUEST;
208         end = idx_end + 1;
209 
210         next_elem.str = end;
211         next_elem.len = elem->len - (end - start);
212         hr = get_token(&next_elem, id, schema, idx);
213         if (hr != S_OK)
214         {
215             TRACE("get_token error %#x\n", hr);
216             return hr;
217         }
218         elem->len = (end - start) + next_elem.len;
219 
220         TRACE("indexed %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
221         return S_OK;
222     }
223     else if (*start == '{')
224     {
225         VARTYPE vt;
226         PROPVARIANT next_token;
227 
228         end = memchrW(start + 1, '=', elem->len - 1);
229         if (!end) return WINCODEC_ERR_INVALIDQUERYREQUEST;
230         if (end > elem->str + elem->len) return WINCODEC_ERR_INVALIDQUERYREQUEST;
231 
232         next_elem.str = start + 1;
233         next_elem.len = end - start - 1;
234         vt = map_type(&next_elem);
235         TRACE("type %s => %d\n", wine_dbgstr_wn(next_elem.str, next_elem.len), vt);
236         if (vt == VT_ILLEGAL) return WINCODEC_ERR_WRONGSTATE;
237 
238         next_token.vt = VT_BSTR;
239         next_token.u.bstrVal = SysAllocStringLen(NULL, elem->len - (end - start) + 1);
240         if (!next_token.u.bstrVal) return E_OUTOFMEMORY;
241 
242         bstr = next_token.u.bstrVal;
243 
244         end++;
245         p = end;
246         while (*end && *end != '}' && end - start < elem->len)
247         {
248             if (*end == '\\') end++;
249             *bstr++ = *end++;
250         }
251         if (*end != '}')
252         {
253             PropVariantClear(&next_token);
254             return WINCODEC_ERR_INVALIDQUERYREQUEST;
255         }
256         *bstr = 0;
257         TRACE("schema/id %s\n", wine_dbgstr_w(next_token.u.bstrVal));
258 
259         if (vt == VT_CLSID)
260         {
261             id->vt = VT_CLSID;
262             id->u.puuid = CoTaskMemAlloc(sizeof(GUID));
263             if (!id->u.puuid)
264             {
265                 PropVariantClear(&next_token);
266                 return E_OUTOFMEMORY;
267             }
268 
269             hr = UuidFromStringW(next_token.u.bstrVal, id->u.puuid);
270         }
271         else
272             hr = PropVariantChangeType(id, &next_token, 0, vt);
273         PropVariantClear(&next_token);
274         if (hr != S_OK)
275         {
276             PropVariantClear(id);
277             PropVariantClear(schema);
278             return hr;
279         }
280 
281         end++;
282         if (*end == ':')
283         {
284             PROPVARIANT next_id, next_schema;
285             int next_idx = 0;
286 
287             next_elem.str = end + 1;
288             next_elem.len = elem->len - (end - start + 1);
289             hr = get_token(&next_elem, &next_id, &next_schema, &next_idx);
290             if (hr != S_OK)
291             {
292                 TRACE("get_token error %#x\n", hr);
293                 return hr;
294             }
295             elem->len = (end - start + 1) + next_elem.len;
296 
297             TRACE("id %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
298 
299             if (next_schema.vt != VT_EMPTY)
300             {
301                 PropVariantClear(&next_id);
302                 PropVariantClear(&next_schema);
303                 return WINCODEC_ERR_WRONGSTATE;
304             }
305 
306             *schema = *id;
307             *id = next_id;
308 
309             return S_OK;
310         }
311 
312         elem->len = end - start;
313         return S_OK;
314     }
315 
316     end = memchrW(start, '/', elem->len);
317     if (!end) end = start + elem->len;
318 
319     p = memchrW(start, ':', end - start);
320     if (p)
321     {
322         next_elem.str = p + 1;
323         next_elem.len = end - p - 1;
324 
325         elem->len = p - start;
326     }
327     else
328         elem->len = end - start;
329 
330     id->vt = VT_BSTR;
331     id->u.bstrVal = SysAllocStringLen(NULL, elem->len + 1);
332     if (!id->u.bstrVal) return E_OUTOFMEMORY;
333 
334     bstr = id->u.bstrVal;
335     p = elem->str;
336     while (p - elem->str < elem->len)
337     {
338         if (*p == '\\') p++;
339         *bstr++ = *p++;
340     }
341     *bstr = 0;
342     TRACE("%s [%d]\n", wine_dbgstr_variant((VARIANT *)id), *idx);
343 
344     if (*p == ':')
345     {
346         PROPVARIANT next_id, next_schema;
347         int next_idx = 0;
348 
349         hr = get_token(&next_elem, &next_id, &next_schema, &next_idx);
350         if (hr != S_OK)
351         {
352             TRACE("get_token error %#x\n", hr);
353             PropVariantClear(id);
354             PropVariantClear(schema);
355             return hr;
356         }
357         elem->len += next_elem.len + 1;
358 
359         TRACE("id %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
360 
361         if (next_schema.vt != VT_EMPTY)
362         {
363             PropVariantClear(&next_id);
364             PropVariantClear(&next_schema);
365             PropVariantClear(id);
366             PropVariantClear(schema);
367             return WINCODEC_ERR_WRONGSTATE;
368         }
369 
370         *schema = *id;
371         *id = next_id;
372     }
373 
374     return S_OK;
375 }
376 
377 static HRESULT find_reader_from_block(IWICMetadataBlockReader *block_reader, UINT index,
378                                       GUID *guid, IWICMetadataReader **reader)
379 {
380     HRESULT hr;
381     GUID format;
382     IWICMetadataReader *new_reader;
383     UINT count, i, matched_index;
384 
385     *reader = NULL;
386 
387     hr = IWICMetadataBlockReader_GetCount(block_reader, &count);
388     if (hr != S_OK) return hr;
389 
390     matched_index = 0;
391 
392     for (i = 0; i < count; i++)
393     {
394         hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &new_reader);
395         if (hr != S_OK) return hr;
396 
397         hr = IWICMetadataReader_GetMetadataFormat(new_reader, &format);
398         if (hr == S_OK)
399         {
400             if (IsEqualGUID(&format, guid))
401             {
402                 if (matched_index == index)
403                 {
404                     *reader = new_reader;
405                     return S_OK;
406                 }
407 
408                 matched_index++;
409             }
410         }
411 
412         IWICMetadataReader_Release(new_reader);
413         if (hr != S_OK) return hr;
414     }
415 
416     return WINCODEC_ERR_PROPERTYNOTFOUND;
417 }
418 
419 static HRESULT get_next_reader(IWICMetadataReader *reader, UINT index,
420                                GUID *guid, IWICMetadataReader **new_reader)
421 {
422     HRESULT hr;
423     PROPVARIANT schema, id, value;
424 
425     *new_reader = NULL;
426 
427     PropVariantInit(&schema);
428     PropVariantInit(&id);
429     PropVariantInit(&value);
430 
431     if (index)
432     {
433         schema.vt = VT_UI2;
434         schema.u.uiVal = index;
435     }
436 
437     id.vt = VT_CLSID;
438     id.u.puuid = guid;
439     hr = IWICMetadataReader_GetValue(reader, &schema, &id, &value);
440     if (hr != S_OK) return hr;
441 
442     if (value.vt == VT_UNKNOWN)
443         hr = IUnknown_QueryInterface(value.u.punkVal, &IID_IWICMetadataReader, (void **)new_reader);
444     else
445         hr = WINCODEC_ERR_UNEXPECTEDMETADATATYPE;
446 
447     PropVariantClear(&value);
448     return hr;
449 }
450 
451 static HRESULT WINAPI mqr_GetMetadataByName(IWICMetadataQueryReader *iface, LPCWSTR query, PROPVARIANT *value)
452 {
453     QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
454     struct string_t elem;
455     WCHAR *full_query;
456     const WCHAR *p;
457     int index, len;
458     PROPVARIANT tk_id, tk_schema, new_value;
459     GUID guid;
460     IWICMetadataReader *reader;
461     HRESULT hr = S_OK;
462 
463     TRACE("(%p,%s,%p)\n", This, wine_dbgstr_w(query), value);
464 
465     len = lstrlenW(query) + 1;
466     if (This->root) len += lstrlenW(This->root);
467     full_query = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
468     full_query[0] = 0;
469     if (This->root)
470         lstrcpyW(full_query, This->root);
471     lstrcatW(full_query, query);
472 
473     PropVariantInit(&tk_id);
474     PropVariantInit(&tk_schema);
475     PropVariantInit(&new_value);
476 
477     reader = NULL;
478     p = full_query;
479 
480     while (*p)
481     {
482         if (*p != '/')
483         {
484             WARN("query should start with '/'\n");
485             hr = WINCODEC_ERR_PROPERTYNOTSUPPORTED;
486             break;
487         }
488 
489         p++;
490 
491         index = 0;
492         elem.str = p;
493         elem.len = lstrlenW(p);
494         hr = get_token(&elem, &tk_id, &tk_schema, &index);
495         if (hr != S_OK)
496         {
497             WARN("get_token error %#x\n", hr);
498             break;
499         }
500         TRACE("parsed %d characters: %s, index %d\n", elem.len, wine_dbgstr_wn(elem.str, elem.len), index);
501         TRACE("id %s, schema %s\n", wine_dbgstr_variant((VARIANT *)&tk_id), wine_dbgstr_variant((VARIANT *)&tk_schema));
502 
503         if (!elem.len) break;
504 
505         if (tk_id.vt == VT_CLSID || (tk_id.vt == VT_BSTR && WICMapShortNameToGuid(tk_id.u.bstrVal, &guid) == S_OK))
506         {
507             WCHAR *root;
508 
509             if (tk_schema.vt != VT_EMPTY)
510             {
511                 FIXME("unsupported schema vt %u\n", tk_schema.vt);
512                 PropVariantClear(&tk_schema);
513             }
514 
515             if (tk_id.vt == VT_CLSID) guid = *tk_id.u.puuid;
516 
517             if (reader)
518             {
519                 IWICMetadataReader *new_reader;
520 
521                 hr = get_next_reader(reader, index, &guid, &new_reader);
522                 IWICMetadataReader_Release(reader);
523                 reader = new_reader;
524             }
525             else
526                 hr = find_reader_from_block(This->block, index, &guid, &reader);
527 
528             if (hr != S_OK) break;
529 
530             root = SysAllocStringLen(NULL, elem.str + elem.len - full_query + 2);
531             if (!root)
532             {
533                 hr = E_OUTOFMEMORY;
534                 break;
535             }
536             lstrcpynW(root, full_query, p - full_query + elem.len + 1);
537 
538             PropVariantClear(&new_value);
539             new_value.vt = VT_UNKNOWN;
540             hr = MetadataQueryReader_CreateInstance(This->block, root, (IWICMetadataQueryReader **)&new_value.u.punkVal);
541             SysFreeString(root);
542             if (hr != S_OK) break;
543         }
544         else
545         {
546             PROPVARIANT schema, id;
547 
548             if (!reader)
549             {
550                 hr = WINCODEC_ERR_INVALIDQUERYREQUEST;
551                 break;
552             }
553 
554             if (tk_schema.vt == VT_BSTR)
555             {
556                 hr = IWICMetadataReader_GetMetadataFormat(reader, &guid);
557                 if (hr != S_OK) break;
558 
559                 schema.vt = VT_LPWSTR;
560                 schema.u.pwszVal = (LPWSTR)map_shortname_to_schema(&guid, tk_schema.u.bstrVal);
561                 if (!schema.u.pwszVal)
562                     schema.u.pwszVal = tk_schema.u.bstrVal;
563             }
564             else
565                 schema = tk_schema;
566 
567             if (tk_id.vt == VT_BSTR)
568             {
569                 id.vt = VT_LPWSTR;
570                 id.u.pwszVal = tk_id.u.bstrVal;
571             }
572             else
573                 id = tk_id;
574 
575             PropVariantClear(&new_value);
576             hr = IWICMetadataReader_GetValue(reader, &schema, &id, &new_value);
577             if (hr != S_OK) break;
578         }
579 
580         p += elem.len;
581 
582         PropVariantClear(&tk_id);
583         PropVariantClear(&tk_schema);
584     }
585 
586     if (reader)
587         IWICMetadataReader_Release(reader);
588 
589     PropVariantClear(&tk_id);
590     PropVariantClear(&tk_schema);
591 
592     if (hr == S_OK)
593         *value = new_value;
594     else
595         PropVariantClear(&new_value);
596 
597     HeapFree(GetProcessHeap(), 0, full_query);
598 
599     return hr;
600 }
601 
602 static HRESULT WINAPI mqr_GetEnumerator(IWICMetadataQueryReader *iface,
603         IEnumString **ppIEnumString)
604 {
605     QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
606     FIXME("(%p,%p)\n", This, ppIEnumString);
607     return E_NOTIMPL;
608 }
609 
610 static IWICMetadataQueryReaderVtbl mqr_vtbl = {
611     mqr_QueryInterface,
612     mqr_AddRef,
613     mqr_Release,
614     mqr_GetContainerFormat,
615     mqr_GetLocation,
616     mqr_GetMetadataByName,
617     mqr_GetEnumerator
618 };
619 
620 HRESULT MetadataQueryReader_CreateInstance(IWICMetadataBlockReader *mbr, const WCHAR *root, IWICMetadataQueryReader **out)
621 {
622     QueryReader *obj;
623 
624     obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
625     if (!obj)
626         return E_OUTOFMEMORY;
627 
628     obj->IWICMetadataQueryReader_iface.lpVtbl = &mqr_vtbl;
629     obj->ref = 1;
630 
631     IWICMetadataBlockReader_AddRef(mbr);
632     obj->block = mbr;
633 
634     obj->root = root ? heap_strdupW(root) : NULL;
635 
636     *out = &obj->IWICMetadataQueryReader_iface;
637 
638     return S_OK;
639 }
640 
641 static const WCHAR bmpW[] = { 'b','m','p',0 };
642 static const WCHAR pngW[] = { 'p','n','g',0 };
643 static const WCHAR icoW[] = { 'i','c','o',0 };
644 static const WCHAR jpgW[] = { 'j','p','g',0 };
645 static const WCHAR tiffW[] = { 't','i','f','f',0 };
646 static const WCHAR gifW[] = { 'g','i','f',0 };
647 static const WCHAR wmphotoW[] = { 'w','m','p','h','o','t','o',0 };
648 static const WCHAR unknownW[] = { 'u','n','k','n','o','w','n',0 };
649 static const WCHAR ifdW[] = { 'i','f','d',0 };
650 static const WCHAR subW[] = { 's','u','b',0 };
651 static const WCHAR exifW[] = { 'e','x','i','f',0 };
652 static const WCHAR gpsW[] = { 'g','p','s',0 };
653 static const WCHAR interopW[] = { 'i','n','t','e','r','o','p',0 };
654 static const WCHAR app0W[] = { 'a','p','p','0',0 };
655 static const WCHAR app1W[] = { 'a','p','p','1',0 };
656 static const WCHAR app13W[] = { 'a','p','p','1','3',0 };
657 static const WCHAR iptcW[] = { 'i','p','t','c',0 };
658 static const WCHAR irbW[] = { 'i','r','b',0 };
659 static const WCHAR _8bimiptcW[] = { '8','b','i','m','i','p','t','c',0 };
660 static const WCHAR _8bimResInfoW[] = { '8','b','i','m','R','e','s','I','n','f','o',0 };
661 static const WCHAR _8bimiptcdigestW[] = { '8','b','i','m','i','p','t','c','d','i','g','e','s','t',0 };
662 static const WCHAR xmpW[] = { 'x','m','p',0 };
663 static const WCHAR thumbW[] = { 't','h','u','m','b',0 };
664 static const WCHAR tEXtW[] = { 't','E','X','t',0 };
665 static const WCHAR xmpstructW[] = { 'x','m','p','s','t','r','u','c','t',0 };
666 static const WCHAR xmpbagW[] = { 'x','m','p','b','a','g',0 };
667 static const WCHAR xmpseqW[] = { 'x','m','p','s','e','q',0 };
668 static const WCHAR xmpaltW[] = { 'x','m','p','a','l','t',0 };
669 static const WCHAR logscrdescW[] = { 'l','o','g','s','c','r','d','e','s','c',0 };
670 static const WCHAR imgdescW[] = { 'i','m','g','d','e','s','c',0 };
671 static const WCHAR grctlextW[] = { 'g','r','c','t','l','e','x','t',0 };
672 static const WCHAR appextW[] = { 'a','p','p','e','x','t',0 };
673 static const WCHAR chrominanceW[] = { 'c','h','r','o','m','i','n','a','n','c','e',0 };
674 static const WCHAR luminanceW[] = { 'l','u','m','i','n','a','n','c','e',0 };
675 static const WCHAR comW[] = { 'c','o','m',0 };
676 static const WCHAR commentextW[] = { 'c','o','m','m','e','n','t','e','x','t',0 };
677 static const WCHAR gAMAW[] = { 'g','A','M','A',0 };
678 static const WCHAR bKGDW[] = { 'b','K','G','D',0 };
679 static const WCHAR iTXtW[] = { 'i','T','X','t',0 };
680 static const WCHAR cHRMW[] = { 'c','H','R','M',0 };
681 static const WCHAR hISTW[] = { 'h','I','S','T',0 };
682 static const WCHAR iCCPW[] = { 'i','C','C','P',0 };
683 static const WCHAR sRGBW[] = { 's','R','G','B',0 };
684 static const WCHAR tIMEW[] = { 't','I','M','E',0 };
685 
686 static const struct
687 {
688     const GUID *guid;
689     const WCHAR *name;
690 } guid2name[] =
691 {
692     { &GUID_ContainerFormatBmp, bmpW },
693     { &GUID_ContainerFormatPng, pngW },
694     { &GUID_ContainerFormatIco, icoW },
695     { &GUID_ContainerFormatJpeg, jpgW },
696     { &GUID_ContainerFormatTiff, tiffW },
697     { &GUID_ContainerFormatGif, gifW },
698     { &GUID_ContainerFormatWmp, wmphotoW },
699     { &GUID_MetadataFormatUnknown, unknownW },
700     { &GUID_MetadataFormatIfd, ifdW },
701     { &GUID_MetadataFormatSubIfd, subW },
702     { &GUID_MetadataFormatExif, exifW },
703     { &GUID_MetadataFormatGps, gpsW },
704     { &GUID_MetadataFormatInterop, interopW },
705     { &GUID_MetadataFormatApp0, app0W },
706     { &GUID_MetadataFormatApp1, app1W },
707     { &GUID_MetadataFormatApp13, app13W },
708     { &GUID_MetadataFormatIPTC, iptcW },
709     { &GUID_MetadataFormatIRB, irbW },
710     { &GUID_MetadataFormat8BIMIPTC, _8bimiptcW },
711     { &GUID_MetadataFormat8BIMResolutionInfo, _8bimResInfoW },
712     { &GUID_MetadataFormat8BIMIPTCDigest, _8bimiptcdigestW },
713     { &GUID_MetadataFormatXMP, xmpW },
714     { &GUID_MetadataFormatThumbnail, thumbW },
715     { &GUID_MetadataFormatChunktEXt, tEXtW },
716     { &GUID_MetadataFormatXMPStruct, xmpstructW },
717     { &GUID_MetadataFormatXMPBag, xmpbagW },
718     { &GUID_MetadataFormatXMPSeq, xmpseqW },
719     { &GUID_MetadataFormatXMPAlt, xmpaltW },
720     { &GUID_MetadataFormatLSD, logscrdescW },
721     { &GUID_MetadataFormatIMD, imgdescW },
722     { &GUID_MetadataFormatGCE, grctlextW },
723     { &GUID_MetadataFormatAPE, appextW },
724     { &GUID_MetadataFormatJpegChrominance, chrominanceW },
725     { &GUID_MetadataFormatJpegLuminance, luminanceW },
726     { &GUID_MetadataFormatJpegComment, comW },
727     { &GUID_MetadataFormatGifComment, commentextW },
728     { &GUID_MetadataFormatChunkgAMA, gAMAW },
729     { &GUID_MetadataFormatChunkbKGD, bKGDW },
730     { &GUID_MetadataFormatChunkiTXt, iTXtW },
731     { &GUID_MetadataFormatChunkcHRM, cHRMW },
732     { &GUID_MetadataFormatChunkhIST, hISTW },
733     { &GUID_MetadataFormatChunkiCCP, iCCPW },
734     { &GUID_MetadataFormatChunksRGB, sRGBW },
735     { &GUID_MetadataFormatChunktIME, tIMEW }
736 };
737 
738 HRESULT WINAPI WICMapGuidToShortName(REFGUID guid, UINT len, WCHAR *name, UINT *ret_len)
739 {
740     UINT i;
741 
742     TRACE("%s,%u,%p,%p\n", wine_dbgstr_guid(guid), len, name, ret_len);
743 
744     if (!guid) return E_INVALIDARG;
745 
746     for (i = 0; i < sizeof(guid2name)/sizeof(guid2name[0]); i++)
747     {
748         if (IsEqualGUID(guid, guid2name[i].guid))
749         {
750             if (name)
751             {
752                 if (!len) return E_INVALIDARG;
753 
754                 len = min(len - 1, lstrlenW(guid2name[i].name));
755                 memcpy(name, guid2name[i].name, len * sizeof(WCHAR));
756                 name[len] = 0;
757 
758                 if (len < lstrlenW(guid2name[i].name))
759                     return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
760             }
761             if (ret_len) *ret_len = lstrlenW(guid2name[i].name) + 1;
762             return S_OK;
763         }
764     }
765 
766     return WINCODEC_ERR_PROPERTYNOTFOUND;
767 }
768 
769 HRESULT WINAPI WICMapShortNameToGuid(PCWSTR name, GUID *guid)
770 {
771     UINT i;
772 
773     TRACE("%s,%p\n", debugstr_w(name), guid);
774 
775     if (!name || !guid) return E_INVALIDARG;
776 
777     for (i = 0; i < sizeof(guid2name)/sizeof(guid2name[0]); i++)
778     {
779         if (!lstrcmpiW(name, guid2name[i].name))
780         {
781             *guid = *guid2name[i].guid;
782             return S_OK;
783         }
784     }
785 
786     return WINCODEC_ERR_PROPERTYNOTFOUND;
787 }
788 
789 static const WCHAR rdf[] = { 'r','d','f',0 };
790 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 };
791 static const WCHAR dc[] = { 'd','c',0 };
792 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 };
793 static const WCHAR xmp[] = { 'x','m','p',0 };
794 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 };
795 static const WCHAR xmpidq[] = { 'x','m','p','i','d','q',0 };
796 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 };
797 static const WCHAR xmpRights[] = { 'x','m','p','R','i','g','h','t','s',0 };
798 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 };
799 static const WCHAR xmpMM[] = { 'x','m','p','M','M',0 };
800 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 };
801 static const WCHAR xmpBJ[] = { 'x','m','p','B','J',0 };
802 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 };
803 static const WCHAR xmpTPg[] = { 'x','m','p','T','P','g',0 };
804 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 };
805 static const WCHAR pdf[] = { 'p','d','f',0 };
806 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 };
807 static const WCHAR photoshop[] = { 'p','h','o','t','o','s','h','o','p',0 };
808 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 };
809 static const WCHAR tiff[] = { 't','i','f','f',0 };
810 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 };
811 static const WCHAR exif[] = { 'e','x','i','f',0 };
812 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 };
813 static const WCHAR stDim[] = { 's','t','D','i','m',0 };
814 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 };
815 static const WCHAR xapGImg[] = { 'x','a','p','G','I','m','g',0 };
816 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 };
817 static const WCHAR stEvt[] = { 's','t','E','v','t',0 };
818 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 };
819 static const WCHAR stRef[] = { 's','t','R','e','f',0 };
820 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 };
821 static const WCHAR stVer[] = { 's','t','V','e','r',0 };
822 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 };
823 static const WCHAR stJob[] = { 's','t','J','o','b',0 };
824 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 };
825 static const WCHAR aux[] = { 'a','u','x',0 };
826 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 };
827 static const WCHAR crs[] = { 'c','r','s',0 };
828 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 };
829 static const WCHAR xmpDM[] = { 'x','m','p','D','M',0 };
830 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 };
831 static const WCHAR Iptc4xmpCore[] = { 'I','p','t','c','4','x','m','p','C','o','r','e',0 };
832 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 };
833 static const WCHAR MicrosoftPhoto[] = { 'M','i','c','r','o','s','o','f','t','P','h','o','t','o',0 };
834 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 };
835 static const WCHAR MP[] = { 'M','P',0 };
836 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 };
837 static const WCHAR MPRI[] = { 'M','P','R','I',0 };
838 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 };
839 static const WCHAR MPReg[] = { 'M','P','R','e','g',0 };
840 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 };
841 
842 static const struct
843 {
844     const WCHAR *name;
845     const WCHAR *schema;
846 } name2schema[] =
847 {
848     { rdf, rdf_scheme },
849     { dc, dc_scheme },
850     { xmp, xmp_scheme },
851     { xmpidq, xmpidq_scheme },
852     { xmpRights, xmpRights_scheme },
853     { xmpMM, xmpMM_scheme },
854     { xmpBJ, xmpBJ_scheme },
855     { xmpTPg, xmpTPg_scheme },
856     { pdf, pdf_scheme },
857     { photoshop, photoshop_scheme },
858     { tiff, tiff_scheme },
859     { exif, exif_scheme },
860     { stDim, stDim_scheme },
861     { xapGImg, xapGImg_scheme },
862     { stEvt, stEvt_scheme },
863     { stRef, stRef_scheme },
864     { stVer, stVer_scheme },
865     { stJob, stJob_scheme },
866     { aux, aux_scheme },
867     { crs, crs_scheme },
868     { xmpDM, xmpDM_scheme },
869     { Iptc4xmpCore, Iptc4xmpCore_scheme },
870     { MicrosoftPhoto, MicrosoftPhoto_scheme },
871     { MP, MP_scheme },
872     { MPRI, MPRI_scheme },
873     { MPReg, MPReg_scheme }
874 };
875 
876 static const WCHAR *map_shortname_to_schema(const GUID *format, const WCHAR *name)
877 {
878     UINT i;
879 
880     /* It appears that the only metadata formats
881      * that support schemas are xmp and xmpstruct.
882      */
883     if (!IsEqualGUID(format, &GUID_MetadataFormatXMP) &&
884         !IsEqualGUID(format, &GUID_MetadataFormatXMPStruct))
885         return NULL;
886 
887     for (i = 0; i < sizeof(name2schema)/sizeof(name2schema[0]); i++)
888     {
889         if (!lstrcmpW(name2schema[i].name, name))
890             return name2schema[i].schema;
891     }
892 
893     return NULL;
894 }
895 
896 HRESULT WINAPI WICMapSchemaToName(REFGUID format, LPWSTR schema, UINT len, WCHAR *name, UINT *ret_len)
897 {
898     UINT i;
899 
900     TRACE("%s,%s,%u,%p,%p\n", wine_dbgstr_guid(format), debugstr_w(schema), len, name, ret_len);
901 
902     if (!format || !schema || !ret_len)
903         return E_INVALIDARG;
904 
905     /* It appears that the only metadata formats
906      * that support schemas are xmp and xmpstruct.
907      */
908     if (!IsEqualGUID(format, &GUID_MetadataFormatXMP) &&
909         !IsEqualGUID(format, &GUID_MetadataFormatXMPStruct))
910         return WINCODEC_ERR_PROPERTYNOTFOUND;
911 
912     for (i = 0; i < sizeof(name2schema)/sizeof(name2schema[0]); i++)
913     {
914         if (!lstrcmpW(name2schema[i].schema, schema))
915         {
916             if (name)
917             {
918                 if (!len) return E_INVALIDARG;
919 
920                 len = min(len - 1, lstrlenW(name2schema[i].name));
921                 memcpy(name, name2schema[i].name, len * sizeof(WCHAR));
922                 name[len] = 0;
923 
924                 if (len < lstrlenW(name2schema[i].name))
925                     return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
926             }
927 
928             if (ret_len) *ret_len = lstrlenW(name2schema[i].name) + 1;
929             return S_OK;
930         }
931     }
932 
933     return WINCODEC_ERR_PROPERTYNOTFOUND;
934 }
935