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