[WINDOWSCODECS] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / windowscodecs / metadataquery.c
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 < ARRAY_SIZE(str2vt); 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 while (*end && *end != '}' && end - start < elem->len)
246 {
247 if (*end == '\\') end++;
248 *bstr++ = *end++;
249 }
250 if (*end != '}')
251 {
252 PropVariantClear(&next_token);
253 return WINCODEC_ERR_INVALIDQUERYREQUEST;
254 }
255 *bstr = 0;
256 TRACE("schema/id %s\n", wine_dbgstr_w(next_token.u.bstrVal));
257
258 if (vt == VT_CLSID)
259 {
260 id->vt = VT_CLSID;
261 id->u.puuid = CoTaskMemAlloc(sizeof(GUID));
262 if (!id->u.puuid)
263 {
264 PropVariantClear(&next_token);
265 return E_OUTOFMEMORY;
266 }
267
268 hr = UuidFromStringW(next_token.u.bstrVal, id->u.puuid);
269 }
270 else
271 hr = PropVariantChangeType(id, &next_token, 0, vt);
272 PropVariantClear(&next_token);
273 if (hr != S_OK)
274 {
275 PropVariantClear(id);
276 PropVariantClear(schema);
277 return hr;
278 }
279
280 end++;
281 if (*end == ':')
282 {
283 PROPVARIANT next_id, next_schema;
284 int next_idx = 0;
285
286 next_elem.str = end + 1;
287 next_elem.len = elem->len - (end - start + 1);
288 hr = get_token(&next_elem, &next_id, &next_schema, &next_idx);
289 if (hr != S_OK)
290 {
291 TRACE("get_token error %#x\n", hr);
292 return hr;
293 }
294 elem->len = (end - start + 1) + next_elem.len;
295
296 TRACE("id %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
297
298 if (next_schema.vt != VT_EMPTY)
299 {
300 PropVariantClear(&next_id);
301 PropVariantClear(&next_schema);
302 return WINCODEC_ERR_WRONGSTATE;
303 }
304
305 *schema = *id;
306 *id = next_id;
307
308 return S_OK;
309 }
310
311 elem->len = end - start;
312 return S_OK;
313 }
314
315 end = memchrW(start, '/', elem->len);
316 if (!end) end = start + elem->len;
317
318 p = memchrW(start, ':', end - start);
319 if (p)
320 {
321 next_elem.str = p + 1;
322 next_elem.len = end - p - 1;
323
324 elem->len = p - start;
325 }
326 else
327 elem->len = end - start;
328
329 id->vt = VT_BSTR;
330 id->u.bstrVal = SysAllocStringLen(NULL, elem->len + 1);
331 if (!id->u.bstrVal) return E_OUTOFMEMORY;
332
333 bstr = id->u.bstrVal;
334 p = elem->str;
335 while (p - elem->str < elem->len)
336 {
337 if (*p == '\\') p++;
338 *bstr++ = *p++;
339 }
340 *bstr = 0;
341 TRACE("%s [%d]\n", wine_dbgstr_variant((VARIANT *)id), *idx);
342
343 if (*p == ':')
344 {
345 PROPVARIANT next_id, next_schema;
346 int next_idx = 0;
347
348 hr = get_token(&next_elem, &next_id, &next_schema, &next_idx);
349 if (hr != S_OK)
350 {
351 TRACE("get_token error %#x\n", hr);
352 PropVariantClear(id);
353 PropVariantClear(schema);
354 return hr;
355 }
356 elem->len += next_elem.len + 1;
357
358 TRACE("id %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
359
360 if (next_schema.vt != VT_EMPTY)
361 {
362 PropVariantClear(&next_id);
363 PropVariantClear(&next_schema);
364 PropVariantClear(id);
365 PropVariantClear(schema);
366 return WINCODEC_ERR_WRONGSTATE;
367 }
368
369 *schema = *id;
370 *id = next_id;
371 }
372
373 return S_OK;
374 }
375
376 static HRESULT find_reader_from_block(IWICMetadataBlockReader *block_reader, UINT index,
377 GUID *guid, IWICMetadataReader **reader)
378 {
379 HRESULT hr;
380 GUID format;
381 IWICMetadataReader *new_reader;
382 UINT count, i, matched_index;
383
384 *reader = NULL;
385
386 hr = IWICMetadataBlockReader_GetCount(block_reader, &count);
387 if (hr != S_OK) return hr;
388
389 matched_index = 0;
390
391 for (i = 0; i < count; i++)
392 {
393 hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &new_reader);
394 if (hr != S_OK) return hr;
395
396 hr = IWICMetadataReader_GetMetadataFormat(new_reader, &format);
397 if (hr == S_OK)
398 {
399 if (IsEqualGUID(&format, guid))
400 {
401 if (matched_index == index)
402 {
403 *reader = new_reader;
404 return S_OK;
405 }
406
407 matched_index++;
408 }
409 }
410
411 IWICMetadataReader_Release(new_reader);
412 if (hr != S_OK) return hr;
413 }
414
415 return WINCODEC_ERR_PROPERTYNOTFOUND;
416 }
417
418 static HRESULT get_next_reader(IWICMetadataReader *reader, UINT index,
419 GUID *guid, IWICMetadataReader **new_reader)
420 {
421 HRESULT hr;
422 PROPVARIANT schema, id, value;
423
424 *new_reader = NULL;
425
426 PropVariantInit(&schema);
427 PropVariantInit(&id);
428 PropVariantInit(&value);
429
430 if (index)
431 {
432 schema.vt = VT_UI2;
433 schema.u.uiVal = index;
434 }
435
436 id.vt = VT_CLSID;
437 id.u.puuid = guid;
438 hr = IWICMetadataReader_GetValue(reader, &schema, &id, &value);
439 if (hr != S_OK) return hr;
440
441 if (value.vt == VT_UNKNOWN)
442 hr = IUnknown_QueryInterface(value.u.punkVal, &IID_IWICMetadataReader, (void **)new_reader);
443 else
444 hr = WINCODEC_ERR_UNEXPECTEDMETADATATYPE;
445
446 PropVariantClear(&value);
447 return hr;
448 }
449
450 static HRESULT WINAPI mqr_GetMetadataByName(IWICMetadataQueryReader *iface, LPCWSTR query, PROPVARIANT *value)
451 {
452 QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
453 struct string_t elem;
454 WCHAR *full_query;
455 const WCHAR *p;
456 int index, len;
457 PROPVARIANT tk_id, tk_schema, new_value;
458 GUID guid;
459 IWICMetadataReader *reader;
460 HRESULT hr = S_OK;
461
462 TRACE("(%p,%s,%p)\n", This, wine_dbgstr_w(query), value);
463
464 len = lstrlenW(query) + 1;
465 if (This->root) len += lstrlenW(This->root);
466 full_query = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
467 full_query[0] = 0;
468 if (This->root)
469 lstrcpyW(full_query, This->root);
470 lstrcatW(full_query, query);
471
472 PropVariantInit(&tk_id);
473 PropVariantInit(&tk_schema);
474 PropVariantInit(&new_value);
475
476 reader = NULL;
477 p = full_query;
478
479 while (*p)
480 {
481 if (*p != '/')
482 {
483 WARN("query should start with '/'\n");
484 hr = WINCODEC_ERR_PROPERTYNOTSUPPORTED;
485 break;
486 }
487
488 p++;
489
490 index = 0;
491 elem.str = p;
492 elem.len = lstrlenW(p);
493 hr = get_token(&elem, &tk_id, &tk_schema, &index);
494 if (hr != S_OK)
495 {
496 WARN("get_token error %#x\n", hr);
497 break;
498 }
499 TRACE("parsed %d characters: %s, index %d\n", elem.len, wine_dbgstr_wn(elem.str, elem.len), index);
500 TRACE("id %s, schema %s\n", wine_dbgstr_variant((VARIANT *)&tk_id), wine_dbgstr_variant((VARIANT *)&tk_schema));
501
502 if (!elem.len) break;
503
504 if (tk_id.vt == VT_CLSID || (tk_id.vt == VT_BSTR && WICMapShortNameToGuid(tk_id.u.bstrVal, &guid) == S_OK))
505 {
506 WCHAR *root;
507
508 if (tk_schema.vt != VT_EMPTY)
509 {
510 FIXME("unsupported schema vt %u\n", tk_schema.vt);
511 PropVariantClear(&tk_schema);
512 }
513
514 if (tk_id.vt == VT_CLSID) guid = *tk_id.u.puuid;
515
516 if (reader)
517 {
518 IWICMetadataReader *new_reader;
519
520 hr = get_next_reader(reader, index, &guid, &new_reader);
521 IWICMetadataReader_Release(reader);
522 reader = new_reader;
523 }
524 else
525 hr = find_reader_from_block(This->block, index, &guid, &reader);
526
527 if (hr != S_OK) break;
528
529 root = SysAllocStringLen(NULL, elem.str + elem.len - full_query + 2);
530 if (!root)
531 {
532 hr = E_OUTOFMEMORY;
533 break;
534 }
535 lstrcpynW(root, full_query, p - full_query + elem.len + 1);
536
537 PropVariantClear(&new_value);
538 new_value.vt = VT_UNKNOWN;
539 hr = MetadataQueryReader_CreateInstance(This->block, root, (IWICMetadataQueryReader **)&new_value.u.punkVal);
540 SysFreeString(root);
541 if (hr != S_OK) break;
542 }
543 else
544 {
545 PROPVARIANT schema, id;
546
547 if (!reader)
548 {
549 hr = WINCODEC_ERR_INVALIDQUERYREQUEST;
550 break;
551 }
552
553 if (tk_schema.vt == VT_BSTR)
554 {
555 hr = IWICMetadataReader_GetMetadataFormat(reader, &guid);
556 if (hr != S_OK) break;
557
558 schema.vt = VT_LPWSTR;
559 schema.u.pwszVal = (LPWSTR)map_shortname_to_schema(&guid, tk_schema.u.bstrVal);
560 if (!schema.u.pwszVal)
561 schema.u.pwszVal = tk_schema.u.bstrVal;
562 }
563 else
564 schema = tk_schema;
565
566 if (tk_id.vt == VT_BSTR)
567 {
568 id.vt = VT_LPWSTR;
569 id.u.pwszVal = tk_id.u.bstrVal;
570 }
571 else
572 id = tk_id;
573
574 PropVariantClear(&new_value);
575 hr = IWICMetadataReader_GetValue(reader, &schema, &id, &new_value);
576 if (hr != S_OK) break;
577 }
578
579 p += elem.len;
580
581 PropVariantClear(&tk_id);
582 PropVariantClear(&tk_schema);
583 }
584
585 if (reader)
586 IWICMetadataReader_Release(reader);
587
588 PropVariantClear(&tk_id);
589 PropVariantClear(&tk_schema);
590
591 if (hr == S_OK && value)
592 *value = new_value;
593 else
594 PropVariantClear(&new_value);
595
596 HeapFree(GetProcessHeap(), 0, full_query);
597
598 return hr;
599 }
600
601 static HRESULT WINAPI mqr_GetEnumerator(IWICMetadataQueryReader *iface,
602 IEnumString **ppIEnumString)
603 {
604 QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
605 FIXME("(%p,%p)\n", This, ppIEnumString);
606 return E_NOTIMPL;
607 }
608
609 static IWICMetadataQueryReaderVtbl mqr_vtbl = {
610 mqr_QueryInterface,
611 mqr_AddRef,
612 mqr_Release,
613 mqr_GetContainerFormat,
614 mqr_GetLocation,
615 mqr_GetMetadataByName,
616 mqr_GetEnumerator
617 };
618
619 HRESULT MetadataQueryReader_CreateInstance(IWICMetadataBlockReader *mbr, const WCHAR *root, IWICMetadataQueryReader **out)
620 {
621 QueryReader *obj;
622
623 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
624 if (!obj)
625 return E_OUTOFMEMORY;
626
627 obj->IWICMetadataQueryReader_iface.lpVtbl = &mqr_vtbl;
628 obj->ref = 1;
629
630 IWICMetadataBlockReader_AddRef(mbr);
631 obj->block = mbr;
632
633 obj->root = root ? heap_strdupW(root) : NULL;
634
635 *out = &obj->IWICMetadataQueryReader_iface;
636
637 return S_OK;
638 }
639
640 static const WCHAR bmpW[] = { 'b','m','p',0 };
641 static const WCHAR pngW[] = { 'p','n','g',0 };
642 static const WCHAR icoW[] = { 'i','c','o',0 };
643 static const WCHAR jpgW[] = { 'j','p','g',0 };
644 static const WCHAR tiffW[] = { 't','i','f','f',0 };
645 static const WCHAR gifW[] = { 'g','i','f',0 };
646 static const WCHAR wmphotoW[] = { 'w','m','p','h','o','t','o',0 };
647 static const WCHAR unknownW[] = { 'u','n','k','n','o','w','n',0 };
648 static const WCHAR ifdW[] = { 'i','f','d',0 };
649 static const WCHAR subW[] = { 's','u','b',0 };
650 static const WCHAR exifW[] = { 'e','x','i','f',0 };
651 static const WCHAR gpsW[] = { 'g','p','s',0 };
652 static const WCHAR interopW[] = { 'i','n','t','e','r','o','p',0 };
653 static const WCHAR app0W[] = { 'a','p','p','0',0 };
654 static const WCHAR app1W[] = { 'a','p','p','1',0 };
655 static const WCHAR app13W[] = { 'a','p','p','1','3',0 };
656 static const WCHAR iptcW[] = { 'i','p','t','c',0 };
657 static const WCHAR irbW[] = { 'i','r','b',0 };
658 static const WCHAR _8bimiptcW[] = { '8','b','i','m','i','p','t','c',0 };
659 static const WCHAR _8bimResInfoW[] = { '8','b','i','m','R','e','s','I','n','f','o',0 };
660 static const WCHAR _8bimiptcdigestW[] = { '8','b','i','m','i','p','t','c','d','i','g','e','s','t',0 };
661 static const WCHAR xmpW[] = { 'x','m','p',0 };
662 static const WCHAR thumbW[] = { 't','h','u','m','b',0 };
663 static const WCHAR tEXtW[] = { 't','E','X','t',0 };
664 static const WCHAR xmpstructW[] = { 'x','m','p','s','t','r','u','c','t',0 };
665 static const WCHAR xmpbagW[] = { 'x','m','p','b','a','g',0 };
666 static const WCHAR xmpseqW[] = { 'x','m','p','s','e','q',0 };
667 static const WCHAR xmpaltW[] = { 'x','m','p','a','l','t',0 };
668 static const WCHAR logscrdescW[] = { 'l','o','g','s','c','r','d','e','s','c',0 };
669 static const WCHAR imgdescW[] = { 'i','m','g','d','e','s','c',0 };
670 static const WCHAR grctlextW[] = { 'g','r','c','t','l','e','x','t',0 };
671 static const WCHAR appextW[] = { 'a','p','p','e','x','t',0 };
672 static const WCHAR chrominanceW[] = { 'c','h','r','o','m','i','n','a','n','c','e',0 };
673 static const WCHAR luminanceW[] = { 'l','u','m','i','n','a','n','c','e',0 };
674 static const WCHAR comW[] = { 'c','o','m',0 };
675 static const WCHAR commentextW[] = { 'c','o','m','m','e','n','t','e','x','t',0 };
676 static const WCHAR gAMAW[] = { 'g','A','M','A',0 };
677 static const WCHAR bKGDW[] = { 'b','K','G','D',0 };
678 static const WCHAR iTXtW[] = { 'i','T','X','t',0 };
679 static const WCHAR cHRMW[] = { 'c','H','R','M',0 };
680 static const WCHAR hISTW[] = { 'h','I','S','T',0 };
681 static const WCHAR iCCPW[] = { 'i','C','C','P',0 };
682 static const WCHAR sRGBW[] = { 's','R','G','B',0 };
683 static const WCHAR tIMEW[] = { 't','I','M','E',0 };
684
685 static const struct
686 {
687 const GUID *guid;
688 const WCHAR *name;
689 } guid2name[] =
690 {
691 { &GUID_ContainerFormatBmp, bmpW },
692 { &GUID_ContainerFormatPng, pngW },
693 { &GUID_ContainerFormatIco, icoW },
694 { &GUID_ContainerFormatJpeg, jpgW },
695 { &GUID_ContainerFormatTiff, tiffW },
696 { &GUID_ContainerFormatGif, gifW },
697 { &GUID_ContainerFormatWmp, wmphotoW },
698 { &GUID_MetadataFormatUnknown, unknownW },
699 { &GUID_MetadataFormatIfd, ifdW },
700 { &GUID_MetadataFormatSubIfd, subW },
701 { &GUID_MetadataFormatExif, exifW },
702 { &GUID_MetadataFormatGps, gpsW },
703 { &GUID_MetadataFormatInterop, interopW },
704 { &GUID_MetadataFormatApp0, app0W },
705 { &GUID_MetadataFormatApp1, app1W },
706 { &GUID_MetadataFormatApp13, app13W },
707 { &GUID_MetadataFormatIPTC, iptcW },
708 { &GUID_MetadataFormatIRB, irbW },
709 { &GUID_MetadataFormat8BIMIPTC, _8bimiptcW },
710 { &GUID_MetadataFormat8BIMResolutionInfo, _8bimResInfoW },
711 { &GUID_MetadataFormat8BIMIPTCDigest, _8bimiptcdigestW },
712 { &GUID_MetadataFormatXMP, xmpW },
713 { &GUID_MetadataFormatThumbnail, thumbW },
714 { &GUID_MetadataFormatChunktEXt, tEXtW },
715 { &GUID_MetadataFormatXMPStruct, xmpstructW },
716 { &GUID_MetadataFormatXMPBag, xmpbagW },
717 { &GUID_MetadataFormatXMPSeq, xmpseqW },
718 { &GUID_MetadataFormatXMPAlt, xmpaltW },
719 { &GUID_MetadataFormatLSD, logscrdescW },
720 { &GUID_MetadataFormatIMD, imgdescW },
721 { &GUID_MetadataFormatGCE, grctlextW },
722 { &GUID_MetadataFormatAPE, appextW },
723 { &GUID_MetadataFormatJpegChrominance, chrominanceW },
724 { &GUID_MetadataFormatJpegLuminance, luminanceW },
725 { &GUID_MetadataFormatJpegComment, comW },
726 { &GUID_MetadataFormatGifComment, commentextW },
727 { &GUID_MetadataFormatChunkgAMA, gAMAW },
728 { &GUID_MetadataFormatChunkbKGD, bKGDW },
729 { &GUID_MetadataFormatChunkiTXt, iTXtW },
730 { &GUID_MetadataFormatChunkcHRM, cHRMW },
731 { &GUID_MetadataFormatChunkhIST, hISTW },
732 { &GUID_MetadataFormatChunkiCCP, iCCPW },
733 { &GUID_MetadataFormatChunksRGB, sRGBW },
734 { &GUID_MetadataFormatChunktIME, tIMEW }
735 };
736
737 HRESULT WINAPI WICMapGuidToShortName(REFGUID guid, UINT len, WCHAR *name, UINT *ret_len)
738 {
739 UINT i;
740
741 TRACE("%s,%u,%p,%p\n", wine_dbgstr_guid(guid), len, name, ret_len);
742
743 if (!guid) return E_INVALIDARG;
744
745 for (i = 0; i < ARRAY_SIZE(guid2name); i++)
746 {
747 if (IsEqualGUID(guid, guid2name[i].guid))
748 {
749 if (name)
750 {
751 if (!len) return E_INVALIDARG;
752
753 len = min(len - 1, lstrlenW(guid2name[i].name));
754 memcpy(name, guid2name[i].name, len * sizeof(WCHAR));
755 name[len] = 0;
756
757 if (len < lstrlenW(guid2name[i].name))
758 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
759 }
760 if (ret_len) *ret_len = lstrlenW(guid2name[i].name) + 1;
761 return S_OK;
762 }
763 }
764
765 return WINCODEC_ERR_PROPERTYNOTFOUND;
766 }
767
768 HRESULT WINAPI WICMapShortNameToGuid(PCWSTR name, GUID *guid)
769 {
770 UINT i;
771
772 TRACE("%s,%p\n", debugstr_w(name), guid);
773
774 if (!name || !guid) return E_INVALIDARG;
775
776 for (i = 0; i < ARRAY_SIZE(guid2name); i++)
777 {
778 if (!lstrcmpiW(name, guid2name[i].name))
779 {
780 *guid = *guid2name[i].guid;
781 return S_OK;
782 }
783 }
784
785 return WINCODEC_ERR_PROPERTYNOTFOUND;
786 }
787
788 static const WCHAR rdf[] = { 'r','d','f',0 };
789 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 };
790 static const WCHAR dc[] = { 'd','c',0 };
791 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 };
792 static const WCHAR xmp[] = { 'x','m','p',0 };
793 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 };
794 static const WCHAR xmpidq[] = { 'x','m','p','i','d','q',0 };
795 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 };
796 static const WCHAR xmpRights[] = { 'x','m','p','R','i','g','h','t','s',0 };
797 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 };
798 static const WCHAR xmpMM[] = { 'x','m','p','M','M',0 };
799 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 };
800 static const WCHAR xmpBJ[] = { 'x','m','p','B','J',0 };
801 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 };
802 static const WCHAR xmpTPg[] = { 'x','m','p','T','P','g',0 };
803 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 };
804 static const WCHAR pdf[] = { 'p','d','f',0 };
805 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 };
806 static const WCHAR photoshop[] = { 'p','h','o','t','o','s','h','o','p',0 };
807 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 };
808 static const WCHAR tiff[] = { 't','i','f','f',0 };
809 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 };
810 static const WCHAR exif[] = { 'e','x','i','f',0 };
811 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 };
812 static const WCHAR stDim[] = { 's','t','D','i','m',0 };
813 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 };
814 static const WCHAR xapGImg[] = { 'x','a','p','G','I','m','g',0 };
815 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 };
816 static const WCHAR stEvt[] = { 's','t','E','v','t',0 };
817 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 };
818 static const WCHAR stRef[] = { 's','t','R','e','f',0 };
819 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 };
820 static const WCHAR stVer[] = { 's','t','V','e','r',0 };
821 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 };
822 static const WCHAR stJob[] = { 's','t','J','o','b',0 };
823 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 };
824 static const WCHAR aux[] = { 'a','u','x',0 };
825 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 };
826 static const WCHAR crs[] = { 'c','r','s',0 };
827 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 };
828 static const WCHAR xmpDM[] = { 'x','m','p','D','M',0 };
829 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 };
830 static const WCHAR Iptc4xmpCore[] = { 'I','p','t','c','4','x','m','p','C','o','r','e',0 };
831 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 };
832 static const WCHAR MicrosoftPhoto[] = { 'M','i','c','r','o','s','o','f','t','P','h','o','t','o',0 };
833 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 };
834 static const WCHAR MP[] = { 'M','P',0 };
835 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 };
836 static const WCHAR MPRI[] = { 'M','P','R','I',0 };
837 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 };
838 static const WCHAR MPReg[] = { 'M','P','R','e','g',0 };
839 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 };
840
841 static const struct
842 {
843 const WCHAR *name;
844 const WCHAR *schema;
845 } name2schema[] =
846 {
847 { rdf, rdf_scheme },
848 { dc, dc_scheme },
849 { xmp, xmp_scheme },
850 { xmpidq, xmpidq_scheme },
851 { xmpRights, xmpRights_scheme },
852 { xmpMM, xmpMM_scheme },
853 { xmpBJ, xmpBJ_scheme },
854 { xmpTPg, xmpTPg_scheme },
855 { pdf, pdf_scheme },
856 { photoshop, photoshop_scheme },
857 { tiff, tiff_scheme },
858 { exif, exif_scheme },
859 { stDim, stDim_scheme },
860 { xapGImg, xapGImg_scheme },
861 { stEvt, stEvt_scheme },
862 { stRef, stRef_scheme },
863 { stVer, stVer_scheme },
864 { stJob, stJob_scheme },
865 { aux, aux_scheme },
866 { crs, crs_scheme },
867 { xmpDM, xmpDM_scheme },
868 { Iptc4xmpCore, Iptc4xmpCore_scheme },
869 { MicrosoftPhoto, MicrosoftPhoto_scheme },
870 { MP, MP_scheme },
871 { MPRI, MPRI_scheme },
872 { MPReg, MPReg_scheme }
873 };
874
875 static const WCHAR *map_shortname_to_schema(const GUID *format, const WCHAR *name)
876 {
877 UINT i;
878
879 /* It appears that the only metadata formats
880 * that support schemas are xmp and xmpstruct.
881 */
882 if (!IsEqualGUID(format, &GUID_MetadataFormatXMP) &&
883 !IsEqualGUID(format, &GUID_MetadataFormatXMPStruct))
884 return NULL;
885
886 for (i = 0; i < ARRAY_SIZE(name2schema); i++)
887 {
888 if (!lstrcmpW(name2schema[i].name, name))
889 return name2schema[i].schema;
890 }
891
892 return NULL;
893 }
894
895 HRESULT WINAPI WICMapSchemaToName(REFGUID format, LPWSTR schema, UINT len, WCHAR *name, UINT *ret_len)
896 {
897 UINT i;
898
899 TRACE("%s,%s,%u,%p,%p\n", wine_dbgstr_guid(format), debugstr_w(schema), len, name, ret_len);
900
901 if (!format || !schema || !ret_len)
902 return E_INVALIDARG;
903
904 /* It appears that the only metadata formats
905 * that support schemas are xmp and xmpstruct.
906 */
907 if (!IsEqualGUID(format, &GUID_MetadataFormatXMP) &&
908 !IsEqualGUID(format, &GUID_MetadataFormatXMPStruct))
909 return WINCODEC_ERR_PROPERTYNOTFOUND;
910
911 for (i = 0; i < ARRAY_SIZE(name2schema); i++)
912 {
913 if (!lstrcmpW(name2schema[i].schema, schema))
914 {
915 if (name)
916 {
917 if (!len) return E_INVALIDARG;
918
919 len = min(len - 1, lstrlenW(name2schema[i].name));
920 memcpy(name, name2schema[i].name, len * sizeof(WCHAR));
921 name[len] = 0;
922
923 if (len < lstrlenW(name2schema[i].name))
924 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
925 }
926
927 *ret_len = lstrlenW(name2schema[i].name) + 1;
928 return S_OK;
929 }
930 }
931
932 return WINCODEC_ERR_PROPERTYNOTFOUND;
933 }