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