[16/17] windowscodecs: Add initial implementation of IWICMetadataQueryReader::GetMetadataByName.

Dmitry Timoshkov dmitry at baikal.ru
Mon Jun 19 23:37:47 CDT 2017


Signed-off-by: Dmitry Timoshkov <dmitry at baikal.ru>
---
 dlls/windowscodecs/metadataquery.c | 490 ++++++++++++++++++++++++++++++++++++-
 include/propidl.idl                |   2 +
 2 files changed, 488 insertions(+), 4 deletions(-)

diff --git a/dlls/windowscodecs/metadataquery.c b/dlls/windowscodecs/metadataquery.c
index 1f3a15b34f..c53834d167 100644
--- a/dlls/windowscodecs/metadataquery.c
+++ b/dlls/windowscodecs/metadataquery.c
@@ -22,10 +22,12 @@
 #include <stdarg.h>
 
 #define COBJMACROS
+#define NONAMELESSUNION
 
 #include "windef.h"
 #include "winbase.h"
 #include "objbase.h"
+#include "propvarutil.h"
 
 #include "wincodecs_private.h"
 
@@ -33,6 +35,8 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
 
+static const WCHAR *map_shortname_to_schema(const GUID *format, const WCHAR *name);
+
 typedef struct {
     IWICMetadataQueryReader IWICMetadataQueryReader_iface;
     LONG ref;
@@ -125,12 +129,470 @@ static HRESULT WINAPI mqr_GetLocation(IWICMetadataQueryReader *iface, UINT len,
     return S_OK;
 }
 
-static HRESULT WINAPI mqr_GetMetadataByName(IWICMetadataQueryReader *iface,
-        LPCWSTR wzName, PROPVARIANT *pvarValue)
+struct string_t
+{
+    const WCHAR *str;
+    int len;
+};
+
+static const struct
+{
+    int len;
+    WCHAR str[10];
+    VARTYPE vt;
+} str2vt[] =
+{
+    { 4, {'c','h','a','r'}, VT_I1 },
+    { 5, {'u','c','h','a','r'}, VT_UI1 },
+    { 5, {'s','h','o','r','t'}, VT_I2 },
+    { 6, {'u','s','h','o','r','t'}, VT_UI2 },
+    { 4, {'l','o','n','g'}, VT_I4 },
+    { 5, {'u','l','o','n','g'}, VT_UI4 },
+    { 3, {'i','n','t'}, VT_I4 },
+    { 4, {'u','i','n','t'}, VT_UI4 },
+    { 8, {'l','o','n','g','l','o','n','g'}, VT_I8 },
+    { 9, {'u','l','o','n','g','l','o','n','g'}, VT_UI8 },
+    { 5, {'f','l','o','a','t'}, VT_R4 },
+    { 6, {'d','o','u','b','l','e'}, VT_R8 },
+    { 3, {'s','t','r'}, VT_LPSTR },
+    { 4, {'w','s','t','r'}, VT_LPWSTR },
+    { 4, {'g','u','i','d'}, VT_CLSID },
+    { 4, {'b','o','o','l'}, VT_BOOL }
+};
+
+static VARTYPE map_type(struct string_t *str)
+{
+    UINT i;
+
+    for (i = 0; i < sizeof(str2vt)/sizeof(str2vt[0]); i++)
+    {
+        if (str2vt[i].len == str->len)
+        {
+            if (CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
+                str->str, str->len, str2vt[i].str, str2vt[i].len) == CSTR_EQUAL)
+                return str2vt[i].vt;
+        }
+    }
+
+    WARN("type %s is not recognized\n", wine_dbgstr_wn(str->str, str->len));
+
+    return VT_ILLEGAL;
+}
+
+static HRESULT get_token(struct string_t *elem, PROPVARIANT *id, PROPVARIANT *schema, int *idx)
+{
+    const WCHAR *start, *end, *p;
+    WCHAR *bstr;
+    struct string_t next_elem;
+    HRESULT hr;
+
+    TRACE("%s, len %d\n", wine_dbgstr_wn(elem->str, elem->len), elem->len);
+
+    PropVariantInit(id);
+    PropVariantInit(schema);
+
+    if (!elem->len) return S_OK;
+
+    start = elem->str;
+
+    if (*start == '[')
+    {
+        WCHAR *idx_end;
+
+        if (start[1] < '0' || start[1] > '9') return DISP_E_TYPEMISMATCH;
+
+        *idx = strtolW(start + 1, &idx_end, 10);
+        if (idx_end > elem->str + elem->len) return WINCODEC_ERR_INVALIDQUERYREQUEST;
+        if (*idx_end != ']') return WINCODEC_ERR_INVALIDQUERYREQUEST;
+        if (*idx < 0) return WINCODEC_ERR_INVALIDQUERYREQUEST;
+        end = idx_end + 1;
+
+        next_elem.str = end;
+        next_elem.len = elem->len - (end - start);
+        hr = get_token(&next_elem, id, schema, idx);
+        if (hr != S_OK)
+        {
+            TRACE("parse_elem error %#x\n", hr);
+            return hr;
+        }
+        elem->len = (end - start) + next_elem.len;
+
+        TRACE("indexed %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
+        return S_OK;
+    }
+    else if (*start == '{')
+    {
+        VARTYPE vt;
+        PROPVARIANT next_token;
+
+        end = memchrW(start + 1, '=', elem->len - 1);
+        if (!end) return WINCODEC_ERR_INVALIDQUERYREQUEST;
+        if (end > elem->str + elem->len) return WINCODEC_ERR_INVALIDQUERYREQUEST;
+
+        next_elem.str = start + 1;
+        next_elem.len = end - start - 1;
+        vt = map_type(&next_elem);
+        TRACE("type %s => %d\n", wine_dbgstr_wn(next_elem.str, next_elem.len), vt);
+        if (vt == VT_ILLEGAL) return WINCODEC_ERR_WRONGSTATE;
+
+        next_token.vt = VT_BSTR;
+        next_token.u.bstrVal = SysAllocStringLen(NULL, elem->len - (end - start) + 1);
+        if (!next_token.u.bstrVal) return E_OUTOFMEMORY;
+
+        bstr = next_token.u.bstrVal;
+
+        end++;
+        p = end;
+        while (*end && *end != '}' && end - start < elem->len)
+        {
+            if (*end == '\\') end++;
+            *bstr++ = *end++;
+        }
+        if (!*end || *end != '}')
+        {
+            PropVariantClear(&next_token);
+            return WINCODEC_ERR_INVALIDQUERYREQUEST;
+        }
+        *bstr = 0;
+        TRACE("schema/id %s\n", wine_dbgstr_w(next_token.u.bstrVal));
+
+        if (vt == VT_CLSID)
+        {
+            id->u.puuid = CoTaskMemAlloc(sizeof(GUID));
+            if (!id->u.puuid) return E_OUTOFMEMORY;
+
+            hr = UuidFromStringW(next_token.u.bstrVal, id->u.puuid);
+        }
+        else
+            hr = PropVariantChangeType(id, &next_token, 0, vt);
+        PropVariantClear(&next_token);
+        if (hr != S_OK)
+        {
+            PropVariantClear(id);
+            PropVariantClear(schema);
+            return hr;
+        }
+
+        id->vt = vt;
+
+        end++;
+        if (*end == ':')
+        {
+            PROPVARIANT next_id, next_schema;
+            int next_idx = 0;
+
+            next_elem.str = end + 1;
+            next_elem.len = elem->len - (end - start + 1);
+            hr = get_token(&next_elem, &next_id, &next_schema, &next_idx);
+            if (hr != S_OK)
+            {
+                TRACE("get_token error %#x\n", hr);
+                return hr;
+            }
+            elem->len = (end - start + 1) + next_elem.len;
+
+            TRACE("id %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
+
+            if (next_schema.vt != VT_EMPTY)
+            {
+                PropVariantClear(&next_id);
+                PropVariantClear(&next_schema);
+                return WINCODEC_ERR_WRONGSTATE;
+            }
+
+            *schema = *id;
+            *id = next_id;
+
+            return S_OK;
+        }
+
+        elem->len = end - start;
+        return S_OK;
+    }
+
+    end = memchrW(start, '/', elem->len);
+    if (!end) end = start + elem->len;
+
+    p = memchrW(start, ':', end - start);
+    if (p)
+    {
+        next_elem.str = p + 1;
+        next_elem.len = end - p - 1;
+
+        elem->len = p - start;
+    }
+    else
+        elem->len = end - start;
+
+    id->vt = VT_BSTR;
+    id->u.bstrVal = SysAllocStringLen(NULL, elem->len + 1);
+    if (!id->u.bstrVal) return E_OUTOFMEMORY;
+
+    bstr = id->u.bstrVal;
+    p = elem->str;
+    while (p - elem->str < elem->len)
+    {
+        if (*p == '\\') p++;
+        *bstr++ = *p++;
+    }
+    *bstr = 0;
+    TRACE("%s [%d]\n", wine_dbgstr_variant((VARIANT *)id), *idx);
+
+    if (*p == ':')
+    {
+        PROPVARIANT next_id, next_schema;
+        int next_idx = 0;
+
+        hr = get_token(&next_elem, &next_id, &next_schema, &next_idx);
+        if (hr != S_OK)
+        {
+            TRACE("get_token error %#x\n", hr);
+            PropVariantClear(id);
+            PropVariantClear(schema);
+            return hr;
+        }
+        elem->len += next_elem.len + 1;
+
+        TRACE("id %s [%d]\n", wine_dbgstr_wn(elem->str, elem->len), *idx);
+
+        if (next_schema.vt != VT_EMPTY)
+        {
+            PropVariantClear(&next_id);
+            PropVariantClear(&next_schema);
+            PropVariantClear(id);
+            PropVariantClear(schema);
+            return WINCODEC_ERR_WRONGSTATE;
+        }
+
+        *schema = *id;
+        *id = next_id;
+    }
+
+    return S_OK;
+}
+
+static HRESULT find_reader_from_block(IWICMetadataBlockReader *block_reader, UINT index,
+                                      GUID *guid, IWICMetadataReader **reader)
+{
+    HRESULT hr;
+    GUID format;
+    IWICMetadataReader *new_reader;
+    UINT count, i, matched_index;
+
+    *reader = NULL;
+
+    hr = IWICMetadataBlockReader_GetCount(block_reader, &count);
+    if (hr != S_OK) return hr;
+
+    matched_index = 0;
+
+    for (i = 0; i < count; i++)
+    {
+        hr = IWICMetadataBlockReader_GetReaderByIndex(block_reader, i, &new_reader);
+        if (hr != S_OK) return hr;
+
+        hr = IWICMetadataReader_GetMetadataFormat(new_reader, &format);
+        if (hr == S_OK)
+        {
+            if (IsEqualGUID(&format, guid))
+            {
+                if (matched_index == index)
+                {
+                    *reader = new_reader;
+                    return S_OK;
+                }
+
+                matched_index++;
+            }
+        }
+
+        IWICMetadataReader_Release(new_reader);
+        if (hr != S_OK) return hr;
+    }
+
+    return WINCODEC_ERR_PROPERTYNOTFOUND;
+}
+
+static HRESULT get_next_reader(IWICMetadataReader *reader, UINT index,
+                               GUID *guid, IWICMetadataReader **new_reader)
+{
+    HRESULT hr;
+    PROPVARIANT schema, id, value;
+
+    *new_reader = NULL;
+
+    PropVariantInit(&schema);
+    PropVariantInit(&id);
+    PropVariantInit(&value);
+
+    if (index)
+    {
+        schema.vt = VT_UI2;
+        schema.u.uiVal = index;
+    }
+
+    id.vt = VT_CLSID;
+    id.u.puuid = guid;
+    hr = IWICMetadataReader_GetValue(reader, &schema, &id, &value);
+    if (hr != S_OK) return hr;
+
+    if (value.vt == VT_UNKNOWN)
+        hr = IUnknown_QueryInterface(value.u.punkVal, &IID_IWICMetadataReader, (void **)new_reader);
+    else
+        hr = WINCODEC_ERR_UNEXPECTEDMETADATATYPE;
+
+    PropVariantClear(&value);
+    return hr;
+}
+
+static HRESULT WINAPI mqr_GetMetadataByName(IWICMetadataQueryReader *iface, LPCWSTR query, PROPVARIANT *value)
 {
     QueryReader *This = impl_from_IWICMetadataQueryReader(iface);
-    FIXME("(%p,%s,%p)\n", This, wine_dbgstr_w(wzName), pvarValue);
-    return E_NOTIMPL;
+    struct string_t elem;
+    WCHAR *full_query;
+    const WCHAR *p;
+    int index, len;
+    PROPVARIANT tk_id, tk_schema, new_value;
+    GUID guid;
+    IWICMetadataReader *reader;
+    HRESULT hr = S_OK;
+
+    TRACE("(%p,%s,%p)\n", This, wine_dbgstr_w(query), value);
+
+    len = lstrlenW(query) + 1;
+    if (This->root) len += lstrlenW(This->root);
+    full_query = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    full_query[0] = 0;
+    if (This->root)
+        lstrcpyW(full_query, This->root);
+    lstrcatW(full_query, query);
+
+    PropVariantInit(&tk_id);
+    PropVariantInit(&tk_schema);
+    PropVariantInit(&new_value);
+
+    reader = NULL;
+    p = full_query;
+
+    while (*p)
+    {
+        if (*p != '/')
+        {
+            WARN("query should start with '/'\n");
+            hr = WINCODEC_ERR_PROPERTYNOTSUPPORTED;
+            break;
+        }
+
+        p++;
+
+        index = 0;
+        elem.str = p;
+        elem.len = lstrlenW(p);
+        hr = get_token(&elem, &tk_id, &tk_schema, &index);
+        if (hr != S_OK)
+        {
+            WARN("get_token error %#x\n", hr);
+            break;
+        }
+        TRACE("parsed %d characters: %s, index %d\n", elem.len, wine_dbgstr_wn(elem.str, elem.len), index);
+        TRACE("id %s, schema %s\n", wine_dbgstr_variant((VARIANT *)&tk_id), wine_dbgstr_variant((VARIANT *)&tk_schema));
+
+        if (!elem.len) break;
+
+        if (tk_id.vt == VT_CLSID || (tk_id.vt == VT_BSTR && WICMapShortNameToGuid(tk_id.u.bstrVal, &guid) == S_OK))
+        {
+            WCHAR *root;
+
+            if (tk_schema.vt != VT_EMPTY)
+            {
+                FIXME("unsupported schema vt %u\n", tk_schema.vt);
+                PropVariantClear(&tk_schema);
+            }
+
+            if (tk_id.vt == VT_CLSID) guid = *tk_id.u.puuid;
+
+            if (reader)
+            {
+                IWICMetadataReader *new_reader;
+
+                hr = get_next_reader(reader, index, &guid, &new_reader);
+                IWICMetadataReader_Release(reader);
+                reader = new_reader;
+            }
+            else
+                hr = find_reader_from_block(This->block, index, &guid, &reader);
+
+            if (hr != S_OK) break;
+
+            root = SysAllocStringLen(NULL, elem.str + elem.len - full_query + 2);
+            if (!root)
+            {
+                hr = E_OUTOFMEMORY;
+                break;
+            }
+            lstrcpynW(root, full_query, p - full_query + elem.len + 1);
+
+            new_value.vt = VT_UNKNOWN;
+            hr = MetadataQueryReader_CreateInstance(This->block, root, (IWICMetadataQueryReader **)&new_value.u.punkVal);
+            SysFreeString(root);
+            if (hr != S_OK) break;
+        }
+        else
+        {
+            PROPVARIANT schema, id;
+
+            if (!reader)
+            {
+                hr = WINCODEC_ERR_INVALIDQUERYREQUEST;
+                break;
+            }
+
+            if (tk_schema.vt == VT_BSTR)
+            {
+                hr = IWICMetadataReader_GetMetadataFormat(reader, &guid);
+                if (hr != S_OK) break;
+
+                schema.vt = VT_LPWSTR;
+                schema.u.pwszVal = (LPWSTR)map_shortname_to_schema(&guid, tk_schema.u.bstrVal);
+                if (!schema.u.pwszVal)
+                    schema.u.pwszVal = tk_schema.u.bstrVal;
+            }
+            else
+                schema = tk_schema;
+
+            if (tk_id.vt == VT_BSTR)
+            {
+                id.vt = VT_LPWSTR;
+                id.u.pwszVal = tk_id.u.bstrVal;
+            }
+            else
+                id = tk_id;
+
+            PropVariantClear(&new_value);
+            hr = IWICMetadataReader_GetValue(reader, &schema, &id, &new_value);
+            if (hr != S_OK) break;
+        }
+
+        p += elem.len;
+
+        PropVariantClear(&tk_id);
+        PropVariantClear(&tk_schema);
+    }
+
+    if (reader)
+        IWICMetadataReader_Release(reader);
+
+    PropVariantClear(&tk_id);
+    PropVariantClear(&tk_schema);
+
+    if (hr == S_OK)
+        *value = new_value;
+    else
+        PropVariantClear(&new_value);
+
+    HeapFree(GetProcessHeap(), 0, full_query);
+
+    return hr;
 }
 
 static HRESULT WINAPI mqr_GetEnumerator(IWICMetadataQueryReader *iface,
@@ -407,6 +869,26 @@ static const struct
     { MPReg, MPReg_scheme }
 };
 
+static const WCHAR *map_shortname_to_schema(const GUID *format, const WCHAR *name)
+{
+    UINT i;
+
+    /* It appears that the only metadata formats
+     * that support schemas are xmp and xmpstruct.
++     */
+    if (!IsEqualGUID(format, &GUID_MetadataFormatXMP) &&
+        !IsEqualGUID(format, &GUID_MetadataFormatXMPStruct))
+        return NULL;
+
+    for (i = 0; i < sizeof(name2schema)/sizeof(name2schema[0]); i++)
+    {
+        if (!lstrcmpW(name2schema[i].name, name))
+            return name2schema[i].schema;
+    }
+
+    return NULL;
+}
+
 HRESULT WINAPI WICMapSchemaToName(REFGUID format, LPWSTR schema, UINT len, WCHAR *name, UINT *ret_len)
 {
     UINT i;
diff --git a/include/propidl.idl b/include/propidl.idl
index fbe80d8174..71401cb454 100644
--- a/include/propidl.idl
+++ b/include/propidl.idl
@@ -141,6 +141,8 @@ interface IPropertyStorage : IUnknown
     [case(VT_BSTR_BLOB)]                  BSTRBLOB bstrblobVal;
     [case(VT_LPSTR)]                      LPSTR pszVal;
     [case(VT_LPWSTR)]                     LPWSTR pwszVal;
+    [case(VT_UNKNOWN)]                    IUnknown *punkVal;
+    [case(VT_DISPATCH)]                   IDispatch *pdispVal;
     [case(VT_UI1|VT_VECTOR)]              CAUB caub;
     [case(VT_I2|VT_VECTOR)]               CAI cai;
     [case(VT_UI2|VT_VECTOR)]              CAUI caui;
-- 
2.13.1




More information about the wine-patches mailing list